summaryrefslogtreecommitdiffstats
path: root/webapp/components/suggestion/channel_mention_provider.jsx
diff options
context:
space:
mode:
authorGeorge Goldberg <george@gberg.me>2016-09-19 13:21:22 +0100
committerJoram Wilander <jwawilander@gmail.com>2016-09-19 08:21:22 -0400
commit8443ca58289055cde25a3cdaaa3987c6f8cfde4a (patch)
treeec957cd1903e76ba5e62eac710777abd74ee0330 /webapp/components/suggestion/channel_mention_provider.jsx
parent781ff323db4c70e4ca476f9ef13a04e5aa063585 (diff)
downloadchat-8443ca58289055cde25a3cdaaa3987c6f8cfde4a.tar.gz
chat-8443ca58289055cde25a3cdaaa3987c6f8cfde4a.tar.bz2
chat-8443ca58289055cde25a3cdaaa3987c6f8cfde4a.zip
PLT-1759 - Auto-complete for !channels when posting messages. (#3890)
* Auto-complete for !channels when posting messages. This is part 1 of the fix for PLT-1759 to make channels linkable. Still to do: - Make the !channels clickable when they appear in messages. This is blocked until PR #3865 is resolved as it looks like that refactors some of the code that would be touched by making this change. - Unit tests. Again, I think the above referenced PR should be merged before tackling this. * Fix style problems. * Highlighting of !channel-names in messages. This only identifies the !channel-name (not the display name). The implementation of the auto-complete on channel names now needs to be modified to convert to the channel handle before sending the message. * Display !channel-name as !Display Name. When we encounter !channel-name in a message, display it as a link using the channel's actual name rather than it's handle (name). * Match on names and display name, and use name. * Autocomplete channels matching on both the name and the the display name. * Use the name as the text we fill in instead of the display name. It's potentially a bit ugly, but it minimises complexity for now as otherwise we'd have to do complicated things to the message box. * Fix style issues. * Load more channels everywhere. Whenever we load the list of channels, we should also load the list of more channels. This is to enable auto-completing and auto-linking of all channels whether or not the user is in them currently. * Include more channels in the map for linking. * Listen for channel list updates for autolinking. * Remove accidental console.log. * Autocomplete on more channels too. * i18n for channel autocomplete. * Link directly to channels in !channel mentions. This currently does not work if you aren't a member of that channel. Need to decide what the correct behaviour is in that case. * Fix style issues. * Show channel name and handle in suggestion. * Match channels only at start or after space. * Better matching in text-formatting. Only match channels after a space-type character or at the start in the posts list too. * Move the route construction to make tests work. Moves route-construction out of text_formatting.jsx and into utils.jsx so that the unit tests work once again.
Diffstat (limited to 'webapp/components/suggestion/channel_mention_provider.jsx')
-rw-r--r--webapp/components/suggestion/channel_mention_provider.jsx130
1 files changed, 130 insertions, 0 deletions
diff --git a/webapp/components/suggestion/channel_mention_provider.jsx b/webapp/components/suggestion/channel_mention_provider.jsx
new file mode 100644
index 000000000..9e8a7b47b
--- /dev/null
+++ b/webapp/components/suggestion/channel_mention_provider.jsx
@@ -0,0 +1,130 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import React from 'react';
+
+import SuggestionStore from 'stores/suggestion_store.jsx';
+import ChannelStore from 'stores/channel_store.jsx';
+import Constants from 'utils/constants.jsx';
+
+import Suggestion from './suggestion.jsx';
+
+const MaxChannelSuggestions = 40;
+
+class ChannelMentionSuggestion extends Suggestion {
+ render() {
+ const isSelection = this.props.isSelection;
+ const item = this.props.item;
+
+ const channelName = item.channel.display_name;
+ let purpose = item.channel.purpose;
+
+ let className = 'mentions__name';
+ if (isSelection) {
+ className += ' suggestion--selected';
+ }
+
+ const description = '(!' + item.channel.name + ')';
+
+ return (
+ <div
+ className={className}
+ onClick={this.handleClick}
+ >
+ <div className='mention__align'>
+ <span>
+ {channelName}
+ </span>
+ <span className='mention__channelname'>
+ {' '}
+ {description}
+ </span>
+ </div>
+ <div className='mention__purpose'>
+ {purpose}
+ </div>
+ </div>
+ );
+ }
+}
+
+function filterChannelsByPrefix(channels, prefix, limit) {
+ const filtered = [];
+
+ for (const id of Object.keys(channels)) {
+ if (filtered.length >= limit) {
+ break;
+ }
+
+ const channel = channels[id];
+
+ if (channel.delete_at > 0) {
+ continue;
+ }
+
+ if (channel.display_name.toLowerCase().startsWith(prefix) || channel.name.startsWith(prefix)) {
+ filtered.push(channel);
+ }
+ }
+
+ return filtered;
+}
+
+export default class ChannelMentionProvider {
+ handlePretextChanged(suggestionId, pretext) {
+ const captured = (/(^|\s)(!([^!]*))$/i).exec(pretext.toLowerCase());
+ if (captured) {
+ const prefix = captured[3];
+
+ const channels = ChannelStore.getAll();
+ const moreChannels = ChannelStore.getMoreAll();
+
+ // Remove private channels from the list.
+ const publicChannels = channels.filter((channel) => {
+ return channel.type === 'O';
+ });
+
+ // Filter channels by prefix.
+ const filteredChannels = filterChannelsByPrefix(
+ publicChannels, prefix, MaxChannelSuggestions);
+ const filteredMoreChannels = filterChannelsByPrefix(
+ moreChannels, prefix, MaxChannelSuggestions - filteredChannels.length);
+
+ // Sort channels by display name.
+ [filteredChannels, filteredMoreChannels].forEach((items) => {
+ items.sort((a, b) => {
+ const aPrefix = a.display_name.startsWith(prefix);
+ const bPrefix = b.display_name.startsWith(prefix);
+
+ if (aPrefix === bPrefix) {
+ return a.display_name.localeCompare(b.display_name);
+ } else if (aPrefix) {
+ return -1;
+ }
+
+ return 1;
+ });
+ });
+
+ // Wrap channels in an outer object to avoid overwriting the 'type' property.
+ const wrappedChannels = filteredChannels.map((item) => {
+ return {
+ type: Constants.MENTION_CHANNELS,
+ channel: item
+ };
+ });
+ const wrappedMoreChannels = filteredMoreChannels.map((item) => {
+ return {
+ type: Constants.MENTION_MORE_CHANNELS,
+ channel: item
+ };
+ });
+
+ const wrapped = wrappedChannels.concat(wrappedMoreChannels);
+
+ const mentions = wrapped.map((item) => '!' + item.channel.name);
+
+ SuggestionStore.addSuggestions(suggestionId, mentions, wrapped, ChannelMentionSuggestion, captured[2]);
+ }
+ }
+}