summaryrefslogtreecommitdiffstats
path: root/webapp/components/suggestion
diff options
context:
space:
mode:
authorJoram Wilander <jwawilander@gmail.com>2016-11-24 09:35:09 -0500
committerHarrison Healey <harrisonmhealey@gmail.com>2016-11-24 09:35:09 -0500
commit981ea33b8e10456bc279f36235c814305d01b243 (patch)
tree00fb6119d9ef16f60d4c0dbdaad1bd6dfbc347ed /webapp/components/suggestion
parentc96ecae6da31aceabf29586cde872876b81d11d9 (diff)
downloadchat-981ea33b8e10456bc279f36235c814305d01b243.tar.gz
chat-981ea33b8e10456bc279f36235c814305d01b243.tar.bz2
chat-981ea33b8e10456bc279f36235c814305d01b243.zip
PLT-4403 Add server-based channel autocomplete, search and paging (#4585)
* Add more channel paging API * Add channel paging support to client * Add DB channel search functions * Add API for searching more channels * Add more channel search functionality to client * Add API for autocompleting channels * Add channel autocomplete functionality to the client * Move to be deprecated APIs to their own file * Final clean-up * Fixes related to feedback * Localization changes * Add unit as suffix to timeout constants
Diffstat (limited to 'webapp/components/suggestion')
-rw-r--r--webapp/components/suggestion/channel_mention_provider.jsx134
-rw-r--r--webapp/components/suggestion/search_channel_provider.jsx80
2 files changed, 116 insertions, 98 deletions
diff --git a/webapp/components/suggestion/channel_mention_provider.jsx b/webapp/components/suggestion/channel_mention_provider.jsx
index d80433271..c644d1a9f 100644
--- a/webapp/components/suggestion/channel_mention_provider.jsx
+++ b/webapp/components/suggestion/channel_mention_provider.jsx
@@ -1,15 +1,16 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import React from 'react';
+import Suggestion from './suggestion.jsx';
+
+import {autocompleteChannels} from 'actions/channel_actions.jsx';
-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';
+import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
+import {Constants, ActionTypes} from 'utils/constants.jsx';
-const MaxChannelSuggestions = 40;
+import React from 'react';
class ChannelMentionSuggestion extends Suggestion {
render() {
@@ -48,84 +49,71 @@ class ChannelMentionSuggestion extends Suggestion {
}
}
-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);
- }
+export default class ChannelMentionProvider {
+ constructor() {
+ this.timeoutId = '';
}
- return filtered;
-}
+ componentWillUnmount() {
+ clearTimeout(this.timeoutId);
+ }
-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;
+ function autocomplete() {
+ autocompleteChannels(
+ prefix,
+ (data) => {
+ const channels = data;
+
+ // Wrap channels in an outer object to avoid overwriting the 'type' property.
+ const wrappedChannels = [];
+ const wrappedMoreChannels = [];
+ const moreChannels = [];
+ channels.forEach((item) => {
+ if (ChannelStore.get(item.id)) {
+ wrappedChannels.push({
+ type: Constants.MENTION_CHANNELS,
+ channel: item
+ });
+ return;
+ }
+
+ wrappedMoreChannels.push({
+ type: Constants.MENTION_MORE_CHANNELS,
+ channel: item
+ });
+
+ moreChannels.push(item);
+ });
+
+ const wrapped = wrappedChannels.concat(wrappedMoreChannels);
+ const mentions = wrapped.map((item) => '~' + item.channel.name);
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_MORE_CHANNELS,
+ channels: moreChannels
+ });
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.SUGGESTION_RECEIVED_SUGGESTIONS,
+ id: suggestionId,
+ matchedPretext: captured[2],
+ terms: mentions,
+ items: wrapped,
+ component: ChannelMentionSuggestion
+ });
}
+ );
+ }
- 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.clearSuggestions(suggestionId);
- SuggestionStore.addSuggestions(suggestionId, mentions, wrapped, ChannelMentionSuggestion, captured[2]);
+ this.timeoutId = setTimeout(
+ autocomplete.bind(this),
+ Constants.AUTOCOMPLETE_TIMEOUT
+ );
}
}
}
diff --git a/webapp/components/suggestion/search_channel_provider.jsx b/webapp/components/suggestion/search_channel_provider.jsx
index 0f07b6e29..66011af9f 100644
--- a/webapp/components/suggestion/search_channel_provider.jsx
+++ b/webapp/components/suggestion/search_channel_provider.jsx
@@ -1,13 +1,16 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import React from 'react';
+import Suggestion from './suggestion.jsx';
+
+import {autocompleteChannels} from 'actions/channel_actions.jsx';
import ChannelStore from 'stores/channel_store.jsx';
-import Constants from 'utils/constants.jsx';
-import SuggestionStore from 'stores/suggestion_store.jsx';
-import Suggestion from './suggestion.jsx';
+import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
+import {Constants, ActionTypes} from 'utils/constants.jsx';
+
+import React from 'react';
class SearchChannelSuggestion extends Suggestion {
render() {
@@ -30,37 +33,64 @@ class SearchChannelSuggestion extends Suggestion {
}
export default class SearchChannelProvider {
+ constructor() {
+ this.timeoutId = '';
+ }
+
+ componentWillUnmount() {
+ clearTimeout(this.timeoutId);
+ }
+
handlePretextChanged(suggestionId, pretext) {
const captured = (/\b(?:in|channel):\s*(\S*)$/i).exec(pretext.toLowerCase());
if (captured) {
const channelPrefix = captured[1];
- const channels = ChannelStore.getAll();
- const publicChannels = [];
- const privateChannels = [];
+ function autocomplete() {
+ autocompleteChannels(
+ channelPrefix,
+ (data) => {
+ const publicChannels = data;
- for (const id of Object.keys(channels)) {
- const channel = channels[id];
+ const localChannels = ChannelStore.getAll();
+ const privateChannels = [];
- // don't show direct channels
- if (channel.type !== Constants.DM_CHANNEL && channel.name.startsWith(channelPrefix)) {
- if (channel.type === Constants.OPEN_CHANNEL) {
- publicChannels.push(channel);
- } else {
- privateChannels.push(channel);
- }
- }
- }
+ for (const id of Object.keys(localChannels)) {
+ const channel = localChannels[id];
+ if (channel.name.startsWith(channelPrefix) && channel.type === Constants.PRIVATE_CHANNEL) {
+ privateChannels.push(channel);
+ }
+ }
- publicChannels.sort((a, b) => a.name.localeCompare(b.name));
- const publicChannelNames = publicChannels.map((channel) => channel.name);
+ const filteredPublicChannels = [];
+ publicChannels.forEach((item) => {
+ if (item.name.startsWith(channelPrefix)) {
+ filteredPublicChannels.push(item);
+ }
+ });
- privateChannels.sort((a, b) => a.name.localeCompare(b.name));
- const privateChannelNames = privateChannels.map((channel) => channel.name);
+ privateChannels.sort((a, b) => a.name.localeCompare(b.name));
+ filteredPublicChannels.sort((a, b) => a.name.localeCompare(b.name));
+
+ const channels = filteredPublicChannels.concat(privateChannels);
+ const channelNames = channels.map((channel) => channel.name);
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.SUGGESTION_RECEIVED_SUGGESTIONS,
+ id: suggestionId,
+ matchedPretext: channelPrefix,
+ terms: channelNames,
+ items: channels,
+ component: SearchChannelSuggestion
+ });
+ }
+ );
+ }
- SuggestionStore.clearSuggestions(suggestionId);
- SuggestionStore.addSuggestions(suggestionId, publicChannelNames, publicChannels, SearchChannelSuggestion, channelPrefix);
- SuggestionStore.addSuggestions(suggestionId, privateChannelNames, privateChannels, SearchChannelSuggestion, channelPrefix);
+ this.timeoutId = setTimeout(
+ autocomplete.bind(this),
+ Constants.AUTOCOMPLETE_TIMEOUT
+ );
}
}
}