summaryrefslogtreecommitdiffstats
path: root/webapp
diff options
context:
space:
mode:
authorYi EungJun <semtlenori@gmail.com>2016-08-11 23:33:22 +0900
committerJoram Wilander <jwawilander@gmail.com>2016-08-11 10:33:22 -0400
commitd6da7d1220fe520a162560266182dff503b95786 (patch)
tree19ceb3851fff0bce43c149737e7bcbe15c570e6c /webapp
parentbb0b895bad5cddbc0297d0857d10a531a6ca6755 (diff)
downloadchat-d6da7d1220fe520a162560266182dff503b95786.tar.gz
chat-d6da7d1220fe520a162560266182dff503b95786.tar.bz2
chat-d6da7d1220fe520a162560266182dff503b95786.zip
Change ordering of at-mention suggestions (#3698)
List members in the current channel first.
Diffstat (limited to 'webapp')
-rw-r--r--webapp/components/suggestion/at_mention_provider.jsx110
-rw-r--r--webapp/components/suggestion/suggestion_list.jsx21
-rw-r--r--webapp/i18n/en.json3
-rw-r--r--webapp/sass/components/_suggestion-list.scss30
-rw-r--r--webapp/utils/constants.jsx5
5 files changed, 129 insertions, 40 deletions
diff --git a/webapp/components/suggestion/at_mention_provider.jsx b/webapp/components/suggestion/at_mention_provider.jsx
index b9127e8d3..87cdc6894 100644
--- a/webapp/components/suggestion/at_mention_provider.jsx
+++ b/webapp/components/suggestion/at_mention_provider.jsx
@@ -8,6 +8,7 @@ import ChannelStore from 'stores/channel_store.jsx';
import UserStore from 'stores/user_store.jsx';
import * as Utils from 'utils/utils.jsx';
import Client from 'client/web_client.jsx';
+import Constants from 'utils/constants.jsx';
import {FormattedMessage} from 'react-intl';
import Suggestion from './suggestion.jsx';
@@ -98,60 +99,91 @@ class AtMentionSuggestion extends Suggestion {
}
}
+function filterUsersByPrefix(users, prefix, limit) {
+ const filtered = [];
+
+ for (const id of Object.keys(users)) {
+ if (filtered.length >= limit) {
+ break;
+ }
+
+ const user = users[id];
+
+ if (user.delete_at > 0) {
+ continue;
+ }
+
+ if (user.username.startsWith(prefix) ||
+ (user.first_name && user.first_name.toLowerCase().startsWith(prefix)) ||
+ (user.last_name && user.last_name.toLowerCase().startsWith(prefix)) ||
+ (user.nickname && user.nickname.toLowerCase().startsWith(prefix))) {
+ filtered.push(user);
+ }
+ }
+
+ return filtered;
+}
+
export default class AtMentionProvider {
handlePretextChanged(suggestionId, pretext) {
const captured = (/@([a-z0-9\-\._]*)$/i).exec(pretext.toLowerCase());
if (captured) {
const prefix = captured[1];
+ // Group users into members and nonmembers of the channel.
const users = UserStore.getActiveOnlyProfiles(true);
-
- const filtered = [];
-
- for (const id of Object.keys(users)) {
- const user = users[id];
-
- if (user.delete_at > 0) {
- continue;
- }
-
- if (user.username.startsWith(prefix) ||
- (user.first_name && user.first_name.toLowerCase().startsWith(prefix)) ||
- (user.last_name && user.last_name.toLowerCase().startsWith(prefix)) ||
- (user.nickname && user.nickname.toLowerCase().startsWith(prefix))) {
- filtered.push(user);
- }
-
- if (filtered.length >= MaxUserSuggestions) {
- break;
+ const channelMembers = {};
+ const extra = ChannelStore.getCurrentExtraInfo();
+ for (let i = 0; i < extra.members.length; i++) {
+ const id = extra.members[i].id;
+ if (users[id]) {
+ channelMembers[id] = users[id];
+ Reflect.deleteProperty(users, id);
}
}
-
+ const channelNonmembers = users;
+
+ // Filter users by prefix.
+ const filteredMembers = filterUsersByPrefix(
+ channelMembers, prefix, MaxUserSuggestions);
+ const filteredNonmembers = filterUsersByPrefix(
+ channelNonmembers, prefix, MaxUserSuggestions - filteredMembers.length);
+ let filteredSpecialMentions = [];
if (!pretext.startsWith('/msg')) {
- // add dummy users to represent the @channel and @all special mentions when not using the /msg command
- if ('channel'.startsWith(prefix)) {
- filtered.push({username: 'channel'});
- }
- if ('all'.startsWith(prefix)) {
- filtered.push({username: 'all'});
- }
- if ('here'.startsWith(prefix)) {
- filtered.push({username: 'here'});
- }
+ filteredSpecialMentions = ['here', 'channel', 'all'].filter((item) => {
+ return item.startsWith(prefix);
+ }).map((name) => {
+ return {username: name};
+ });
}
- filtered.sort((a, b) => {
- const aPrefix = a.username.startsWith(prefix);
- const bPrefix = b.username.startsWith(prefix);
+ // Sort users by username.
+ [filteredMembers, filteredNonmembers].forEach((items) => {
+ items.sort((a, b) => {
+ const aPrefix = a.username.startsWith(prefix);
+ const bPrefix = b.username.startsWith(prefix);
- if (aPrefix === bPrefix) {
- return a.username.localeCompare(b.username);
- } else if (aPrefix) {
- return -1;
- }
+ if (aPrefix === bPrefix) {
+ return a.username.localeCompare(b.username);
+ } else if (aPrefix) {
+ return -1;
+ }
+
+ return 1;
+ });
+ });
- return 1;
+ filteredMembers.forEach((item) => {
+ item.type = Constants.MENTION_MEMBERS;
});
+ filteredNonmembers.forEach((item) => {
+ item.type = Constants.MENTION_NONMEMBERS;
+ });
+ filteredSpecialMentions.forEach((item) => {
+ item.type = Constants.MENTION_SPECIAL;
+ });
+
+ const filtered = filteredMembers.concat(filteredSpecialMentions).concat(filteredNonmembers);
const mentions = filtered.map((user) => '@' + user.username);
diff --git a/webapp/components/suggestion/suggestion_list.jsx b/webapp/components/suggestion/suggestion_list.jsx
index 52b85b2f5..7c746ac2a 100644
--- a/webapp/components/suggestion/suggestion_list.jsx
+++ b/webapp/components/suggestion/suggestion_list.jsx
@@ -5,6 +5,7 @@ import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as GlobalActions from 'actions/global_actions.jsx';
import SuggestionStore from 'stores/suggestion_store.jsx';
+import {FormattedMessage} from 'react-intl';
import React from 'react';
@@ -92,19 +93,39 @@ export default class SuggestionList extends React.Component {
}
}
+ renderDivider(type) {
+ return (
+ <div
+ key={type + '-divider'}
+ className='suggestion-list__divider'
+ >
+ <span>
+ <FormattedMessage id={'suggestion.' + type}/>
+ </span>
+ </div>
+ );
+ }
+
render() {
if (this.state.items.length === 0) {
return null;
}
const items = [];
+ let lastType;
for (let i = 0; i < this.state.items.length; i++) {
+ const item = this.state.items[i];
const term = this.state.terms[i];
const isSelection = term === this.state.selection;
// ReactComponent names need to be upper case when used in JSX
const Component = this.state.components[i];
+ if (item.type !== lastType) {
+ items.push(this.renderDivider(item.type));
+ lastType = item.type;
+ }
+
items.push(
<Component
key={term}
diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json
index 986b4d036..9e899754e 100644
--- a/webapp/i18n/en.json
+++ b/webapp/i18n/en.json
@@ -1538,6 +1538,9 @@
"suggestion.mention.all": "Notifies everyone in the channel, use in {townsquare} to notify the whole team",
"suggestion.mention.channel": "Notifies everyone in the channel",
"suggestion.mention.here": "Notifies everyone in the channel and online",
+ "suggestion.mention.members": "Channel Members",
+ "suggestion.mention.nonmembers": "Not in Channel",
+ "suggestion.mention.special": "Special Mentions",
"suggestion.search.private": "Private Groups",
"suggestion.search.public": "Public Channels",
"team_export_tab.download": "download",
diff --git a/webapp/sass/components/_suggestion-list.scss b/webapp/sass/components/_suggestion-list.scss
index 8bee49ce6..c995d5ebf 100644
--- a/webapp/sass/components/_suggestion-list.scss
+++ b/webapp/sass/components/_suggestion-list.scss
@@ -44,3 +44,33 @@
.suggestion-list--bottom {
position: relative;
}
+
+.suggestion-list__divider {
+ line-height: 21px;
+ margin: 5px 0px 0px 5px;
+ position: relative;
+
+ &:first-child {
+ margin-top: 5px;
+ }
+
+ > span {
+ color: rgba(51,51,51,0.7);
+ background: #f2f4f8;
+ display: inline-block;
+ padding-right: 10px;
+ position: relative;
+ z-index: 5;
+ }
+
+ &:before {
+ @include opacity(.2);
+ background: $dark-gray;
+ content: '';
+ height: 1px;
+ left: 0;
+ position: absolute;
+ top: 10px;
+ width: 100%;
+ }
+}
diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx
index dbdc3e9f1..24d1d6b19 100644
--- a/webapp/utils/constants.jsx
+++ b/webapp/utils/constants.jsx
@@ -797,7 +797,10 @@ export const Constants = {
LICENSE_GRACE_PERIOD: 1000 * 60 * 60 * 24 * 15, // 15 days
PERMISSIONS_ALL: 'all',
PERMISSIONS_TEAM_ADMIN: 'team_admin',
- PERMISSIONS_SYSTEM_ADMIN: 'system_admin'
+ PERMISSIONS_SYSTEM_ADMIN: 'system_admin',
+ MENTION_MEMBERS: 'mention.members',
+ MENTION_NONMEMBERS: 'mention.nonmembers',
+ MENTION_SPECIAL: 'mention.special'
};
export default Constants;