Change ordering of at-mention suggestions (#3698)

List members in the current channel first.
Этот коммит содержится в:
Yi EungJun
2016-08-11 23:33:22 +09:00
коммит произвёл Joram Wilander
родитель bb0b895bad
Коммит d6da7d1220
5 изменённых файлов: 128 добавлений и 39 удалений

Просмотреть файл

@@ -8,6 +8,7 @@ import ChannelStore from 'stores/channel_store.jsx';
import UserStore from 'stores/user_store.jsx'; import UserStore from 'stores/user_store.jsx';
import * as Utils from 'utils/utils.jsx'; import * as Utils from 'utils/utils.jsx';
import Client from 'client/web_client.jsx'; import Client from 'client/web_client.jsx';
import Constants from 'utils/constants.jsx';
import {FormattedMessage} from 'react-intl'; import {FormattedMessage} from 'react-intl';
import Suggestion from './suggestion.jsx'; import Suggestion from './suggestion.jsx';
@@ -98,61 +99,92 @@ 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 { export default class AtMentionProvider {
handlePretextChanged(suggestionId, pretext) { handlePretextChanged(suggestionId, pretext) {
const captured = (/@([a-z0-9\-\._]*)$/i).exec(pretext.toLowerCase()); const captured = (/@([a-z0-9\-\._]*)$/i).exec(pretext.toLowerCase());
if (captured) { if (captured) {
const prefix = captured[1]; const prefix = captured[1];
// Group users into members and nonmembers of the channel.
const users = UserStore.getActiveOnlyProfiles(true); const users = UserStore.getActiveOnlyProfiles(true);
const channelMembers = {};
const filtered = []; const extra = ChannelStore.getCurrentExtraInfo();
for (let i = 0; i < extra.members.length; i++) {
for (const id of Object.keys(users)) { const id = extra.members[i].id;
const user = users[id]; if (users[id]) {
channelMembers[id] = users[id];
if (user.delete_at > 0) { Reflect.deleteProperty(users, id);
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 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')) { if (!pretext.startsWith('/msg')) {
// add dummy users to represent the @channel and @all special mentions when not using the /msg command filteredSpecialMentions = ['here', 'channel', 'all'].filter((item) => {
if ('channel'.startsWith(prefix)) { return item.startsWith(prefix);
filtered.push({username: 'channel'}); }).map((name) => {
} return {username: name};
if ('all'.startsWith(prefix)) { });
filtered.push({username: 'all'});
}
if ('here'.startsWith(prefix)) {
filtered.push({username: 'here'});
}
} }
filtered.sort((a, b) => { // Sort users by username.
const aPrefix = a.username.startsWith(prefix); [filteredMembers, filteredNonmembers].forEach((items) => {
const bPrefix = b.username.startsWith(prefix); items.sort((a, b) => {
const aPrefix = a.username.startsWith(prefix);
const bPrefix = b.username.startsWith(prefix);
if (aPrefix === bPrefix) { if (aPrefix === bPrefix) {
return a.username.localeCompare(b.username); return a.username.localeCompare(b.username);
} else if (aPrefix) { } else if (aPrefix) {
return -1; 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); const mentions = filtered.map((user) => '@' + user.username);
SuggestionStore.addSuggestions(suggestionId, mentions, filtered, AtMentionSuggestion, captured[0]); SuggestionStore.addSuggestions(suggestionId, mentions, filtered, AtMentionSuggestion, captured[0]);

Просмотреть файл

@@ -5,6 +5,7 @@ import $ from 'jquery';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import * as GlobalActions from 'actions/global_actions.jsx'; import * as GlobalActions from 'actions/global_actions.jsx';
import SuggestionStore from 'stores/suggestion_store.jsx'; import SuggestionStore from 'stores/suggestion_store.jsx';
import {FormattedMessage} from 'react-intl';
import React from 'react'; 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() { render() {
if (this.state.items.length === 0) { if (this.state.items.length === 0) {
return null; return null;
} }
const items = []; const items = [];
let lastType;
for (let i = 0; i < this.state.items.length; i++) { for (let i = 0; i < this.state.items.length; i++) {
const item = this.state.items[i];
const term = this.state.terms[i]; const term = this.state.terms[i];
const isSelection = term === this.state.selection; const isSelection = term === this.state.selection;
// ReactComponent names need to be upper case when used in JSX // ReactComponent names need to be upper case when used in JSX
const Component = this.state.components[i]; const Component = this.state.components[i];
if (item.type !== lastType) {
items.push(this.renderDivider(item.type));
lastType = item.type;
}
items.push( items.push(
<Component <Component
key={term} key={term}

Просмотреть файл

@@ -1538,6 +1538,9 @@
"suggestion.mention.all": "Notifies everyone in the channel, use in {townsquare} to notify the whole team", "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.channel": "Notifies everyone in the channel",
"suggestion.mention.here": "Notifies everyone in the channel and online", "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.private": "Private Groups",
"suggestion.search.public": "Public Channels", "suggestion.search.public": "Public Channels",
"team_export_tab.download": "download", "team_export_tab.download": "download",

Просмотреть файл

@@ -44,3 +44,33 @@
.suggestion-list--bottom { .suggestion-list--bottom {
position: relative; 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%;
}
}

Просмотреть файл

@@ -797,7 +797,10 @@ export const Constants = {
LICENSE_GRACE_PERIOD: 1000 * 60 * 60 * 24 * 15, // 15 days LICENSE_GRACE_PERIOD: 1000 * 60 * 60 * 24 * 15, // 15 days
PERMISSIONS_ALL: 'all', PERMISSIONS_ALL: 'all',
PERMISSIONS_TEAM_ADMIN: 'team_admin', 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; export default Constants;