summaryrefslogtreecommitdiffstats
path: root/webapp/components/suggestion
diff options
context:
space:
mode:
Diffstat (limited to 'webapp/components/suggestion')
-rw-r--r--webapp/components/suggestion/at_mention_provider.jsx165
-rw-r--r--webapp/components/suggestion/channel_mention_provider.jsx143
-rw-r--r--webapp/components/suggestion/command_provider.jsx41
-rw-r--r--webapp/components/suggestion/emoticon_provider.jsx131
-rw-r--r--webapp/components/suggestion/provider.jsx38
-rw-r--r--webapp/components/suggestion/search_channel_provider.jsx91
-rw-r--r--webapp/components/suggestion/search_suggestion_list.jsx98
-rw-r--r--webapp/components/suggestion/search_user_provider.jsx91
-rw-r--r--webapp/components/suggestion/suggestion.jsx30
-rw-r--r--webapp/components/suggestion/suggestion_box.jsx441
-rw-r--r--webapp/components/suggestion/suggestion_list.jsx177
-rw-r--r--webapp/components/suggestion/switch_channel_provider.jsx283
-rw-r--r--webapp/components/suggestion/switch_team_provider.jsx96
13 files changed, 0 insertions, 1825 deletions
diff --git a/webapp/components/suggestion/at_mention_provider.jsx b/webapp/components/suggestion/at_mention_provider.jsx
deleted file mode 100644
index 9cf32440f..000000000
--- a/webapp/components/suggestion/at_mention_provider.jsx
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Suggestion from './suggestion.jsx';
-import Provider from './provider.jsx';
-
-import UserStore from 'stores/user_store.jsx';
-
-import {autocompleteUsersInChannel} from 'actions/user_actions.jsx';
-
-import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
-import * as Utils from 'utils/utils.jsx';
-import {Constants, ActionTypes} from 'utils/constants.jsx';
-
-import React from 'react';
-import {FormattedMessage} from 'react-intl';
-import XRegExp from 'xregexp';
-
-class AtMentionSuggestion extends Suggestion {
- render() {
- const isSelection = this.props.isSelection;
- const user = this.props.item;
-
- let username;
- let description;
- let icon;
- if (user.username === 'all') {
- username = 'all';
- description = (
- <FormattedMessage
- id='suggestion.mention.all'
- defaultMessage='CAUTION: This mentions everyone in channel'
- />
- );
- icon = <i className='mention__image fa fa-users fa-2x'/>;
- } else if (user.username === 'channel') {
- username = 'channel';
- description = (
- <FormattedMessage
- id='suggestion.mention.channel'
- defaultMessage='Notifies everyone in the channel'
- />
- );
- icon = <i className='mention__image fa fa-users fa-2x'/>;
- } else if (user.username === 'here') {
- username = 'here';
- description = (
- <FormattedMessage
- id='suggestion.mention.here'
- defaultMessage='Notifies everyone in the channel and online'
- />
- );
- icon = <i className='mention__image fa fa-users fa-2x'/>;
- } else {
- username = user.username;
-
- if ((user.first_name || user.last_name) && user.nickname) {
- description = `- ${Utils.getFullName(user)} (${user.nickname})`;
- } else if (user.nickname) {
- description = `- (${user.nickname})`;
- } else if (user.first_name || user.last_name) {
- description = `- ${Utils.getFullName(user)}`;
- }
-
- icon = (
- <img
- className='mention__image'
- src={Utils.imageURLForUser(user)}
- />
- );
- }
-
- let className = 'mentions__name';
- if (isSelection) {
- className += ' suggestion--selected';
- }
-
- return (
- <div
- className={className}
- onClick={this.handleClick}
- >
- <div className='pull-left'>
- {icon}
- </div>
- <div className='pull-left mention--align'>
- <span>
- {'@' + username}
- </span>
- <span className='mention__fullname'>
- {' '}
- {description}
- </span>
- </div>
- </div>
- );
- }
-}
-
-export default class AtMentionProvider extends Provider {
- constructor(channelId) {
- super();
-
- this.channelId = channelId;
- }
-
- handlePretextChanged(suggestionId, pretext) {
- const captured = XRegExp.cache('(?:^|\\W)@([\\pL\\d\\-_.]*)$', 'i').exec(pretext.toLowerCase());
- if (!captured) {
- return false;
- }
-
- const prefix = captured[1];
-
- this.startNewRequest(suggestionId, prefix);
-
- autocompleteUsersInChannel(
- prefix,
- this.channelId,
- (data) => {
- if (this.shouldCancelDispatch(prefix)) {
- return;
- }
-
- const members = Object.assign([], data.users);
- for (const id of Object.keys(members)) {
- members[id] = {...members[id], type: Constants.MENTION_MEMBERS};
- }
-
- const nonmembers = data.out_of_channel || [];
- for (const id of Object.keys(nonmembers)) {
- nonmembers[id] = {...nonmembers[id], type: Constants.MENTION_NONMEMBERS};
- }
-
- let specialMentions = [];
- if (!pretext.startsWith('/msg')) {
- specialMentions = ['here', 'channel', 'all'].filter((item) => {
- return item.startsWith(prefix);
- }).map((name) => {
- return {username: name, type: Constants.MENTION_SPECIAL};
- });
- }
-
- let users = members.concat(specialMentions).concat(nonmembers);
- const me = UserStore.getCurrentUser();
- users = users.filter((user) => {
- return user.id !== me.id;
- });
-
- const mentions = users.map((user) => '@' + user.username);
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.SUGGESTION_RECEIVED_SUGGESTIONS,
- id: suggestionId,
- matchedPretext: `@${captured[1]}`,
- terms: mentions,
- items: users,
- component: AtMentionSuggestion
- });
- }
- );
-
- return true;
- }
-}
diff --git a/webapp/components/suggestion/channel_mention_provider.jsx b/webapp/components/suggestion/channel_mention_provider.jsx
deleted file mode 100644
index 1d85d8082..000000000
--- a/webapp/components/suggestion/channel_mention_provider.jsx
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Suggestion from './suggestion.jsx';
-import Provider from './provider.jsx';
-
-import {autocompleteChannels} from 'actions/channel_actions.jsx';
-
-import ChannelStore from 'stores/channel_store.jsx';
-
-import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
-import {Constants, ActionTypes} from 'utils/constants.jsx';
-
-import React from 'react';
-
-class ChannelMentionSuggestion extends Suggestion {
- render() {
- const isSelection = this.props.isSelection;
- const item = this.props.item;
-
- const channelName = item.channel.display_name;
- const 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>
- );
- }
-}
-
-export default class ChannelMentionProvider extends Provider {
- constructor() {
- super();
-
- this.lastTermWithNoResults = '';
- this.lastCompletedWord = '';
- }
-
- handlePretextChanged(suggestionId, pretext) {
- const captured = (/(^|\s)(~([^~\r\n]*))$/i).exec(pretext.toLowerCase());
-
- if (!captured) {
- // Not a channel mention
- return false;
- }
-
- if (this.lastTermWithNoResults && pretext.startsWith(this.lastTermWithNoResults)) {
- // Just give up since we know it won't return any results
- return false;
- }
-
- if (this.lastCompletedWord && captured[0].startsWith(this.lastCompletedWord)) {
- // It appears we're still matching a channel handle that we already completed
- return false;
- }
-
- // Clear the last completed word since we've started to match new text
- this.lastCompletedWord = '';
-
- const prefix = captured[3];
-
- this.startNewRequest(suggestionId, prefix);
-
- autocompleteChannels(
- prefix,
- (channels) => {
- if (this.shouldCancelDispatch(prefix)) {
- return;
- }
-
- if (channels.length === 0) {
- this.lastTermWithNoResults = pretext;
- }
-
- // 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 true;
- }
-
- handleCompleteWord(term) {
- this.lastCompletedWord = term;
- }
-}
diff --git a/webapp/components/suggestion/command_provider.jsx b/webapp/components/suggestion/command_provider.jsx
deleted file mode 100644
index 4f74ebd55..000000000
--- a/webapp/components/suggestion/command_provider.jsx
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import React from 'react';
-
-import Suggestion from './suggestion.jsx';
-
-import {getSuggestedCommands} from 'actions/integration_actions.jsx';
-
-class CommandSuggestion extends Suggestion {
- render() {
- const {item, isSelection} = this.props;
-
- let className = 'command';
- if (isSelection) {
- className += ' suggestion--selected';
- }
-
- return (
- <div
- className={className}
- onClick={this.handleClick}
- >
- <div className='command__title'>
- <string>{item.suggestion} {item.hint}</string>
- </div>
- <div className='command__desc'>
- {item.description}
- </div>
- </div>
- );
- }
-}
-
-export default class CommandProvider {
- handlePretextChanged(suggestionId, pretext) {
- if (pretext.startsWith('/')) {
- getSuggestedCommands(pretext.toLowerCase(), suggestionId, CommandSuggestion, pretext.toLowerCase());
- }
- }
-}
diff --git a/webapp/components/suggestion/emoticon_provider.jsx b/webapp/components/suggestion/emoticon_provider.jsx
deleted file mode 100644
index 1de35dc20..000000000
--- a/webapp/components/suggestion/emoticon_provider.jsx
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import React from 'react';
-
-import {default as EmojiStore, EmojiMap} from 'stores/emoji_store.jsx';
-import * as Emoticons from 'utils/emoticons.jsx';
-import SuggestionStore from 'stores/suggestion_store.jsx';
-
-import Suggestion from './suggestion.jsx';
-
-import store from 'stores/redux_store.jsx';
-import {getCustomEmojisByName} from 'mattermost-redux/selectors/entities/emojis';
-
-const MIN_EMOTICON_LENGTH = 2;
-
-class EmoticonSuggestion extends Suggestion {
- render() {
- const text = this.props.term;
- const emoji = this.props.item.emoji;
-
- let className = 'emoticon-suggestion';
- if (this.props.isSelection) {
- className += ' suggestion--selected';
- }
-
- return (
- <div
- className={className}
- onClick={this.handleClick}
- >
- <div className='pull-left'>
- <img
- alt={text}
- className='emoticon-suggestion__image'
- src={EmojiStore.getEmojiImageUrl(emoji)}
- title={text}
- />
- </div>
- <div className='pull-left'>
- {text}
- </div>
- </div>
- );
- }
-}
-
-export default class EmoticonProvider {
- handlePretextChanged(suggestionId, pretext) {
- let hasSuggestions = false;
-
- // look for the potential emoticons at the start of the text, after whitespace, and at the start of emoji reaction commands
- const captured = (/(^|\s|^\+|^-)(:([^:\s]*))$/g).exec(pretext);
- if (captured) {
- const prefix = captured[1];
- const text = captured[2];
- const partialName = captured[3];
-
- if (partialName.length < MIN_EMOTICON_LENGTH) {
- SuggestionStore.clearSuggestions(suggestionId);
- return false;
- }
-
- const matched = [];
-
- // check for text emoticons if this isn't for an emoji reaction
- if (prefix !== '-' && prefix !== '+') {
- for (const emoticon of Object.keys(Emoticons.emoticonPatterns)) {
- if (Emoticons.emoticonPatterns[emoticon].test(text)) {
- SuggestionStore.addSuggestion(suggestionId, text, EmojiStore.get(emoticon), EmoticonSuggestion, text);
-
- hasSuggestions = true;
- }
- }
- }
-
- const emojis = new EmojiMap(getCustomEmojisByName(store.getState()));
-
- // check for named emoji
- for (const [name, emoji] of emojis) {
- if (emoji.aliases) {
- // This is a system emoji so it may have multiple names
- for (const alias of emoji.aliases) {
- if (alias.indexOf(partialName) !== -1) {
- matched.push({name: alias, emoji});
- break;
- }
- }
- } else if (name.indexOf(partialName) !== -1) {
- // This is a custom emoji so it only has one name
- matched.push({name, emoji});
- }
- }
-
- // sort the emoticons so that emoticons starting with the entered text come first
- matched.sort((a, b) => {
- const aName = a.name;
- const bName = b.name;
-
- const aPrefix = aName.startsWith(partialName);
- const bPrefix = bName.startsWith(partialName);
-
- if (aPrefix === bPrefix) {
- return aName.localeCompare(bName);
- } else if (aPrefix) {
- return -1;
- }
-
- return 1;
- });
-
- const terms = matched.map((item) => ':' + item.name + ':');
-
- SuggestionStore.clearSuggestions(suggestionId);
- if (terms.length > 0) {
- SuggestionStore.addSuggestions(suggestionId, terms, matched, EmoticonSuggestion, text);
-
- hasSuggestions = true;
- }
- }
-
- if (hasSuggestions) {
- // force the selection to be cleared since the order of elements may have changed
- SuggestionStore.clearSelection(suggestionId);
-
- return true;
- }
-
- return false;
- }
-}
diff --git a/webapp/components/suggestion/provider.jsx b/webapp/components/suggestion/provider.jsx
deleted file mode 100644
index a5b54fb26..000000000
--- a/webapp/components/suggestion/provider.jsx
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SuggestionStore from 'stores/suggestion_store.jsx';
-
-export default class Provider {
- constructor() {
- this.latestPrefix = '';
- this.latestComplete = true;
- this.disableDispatches = false;
- }
-
- handlePretextChanged(suggestionId, pretext) { // eslint-disable-line no-unused-vars
- // NO-OP for inherited classes to override
- }
-
- startNewRequest(suggestionId, prefix) {
- this.latestPrefix = prefix;
- this.latestComplete = false;
-
- // Don't use the dispatcher here since this is only called while handling an event
- SuggestionStore.setSuggestionsPending(suggestionId, true);
- }
-
- shouldCancelDispatch(prefix) {
- if (this.disableDispatches) {
- return true;
- }
-
- if (prefix === this.latestPrefix) {
- this.latestComplete = true;
- } else if (this.latestComplete) {
- return true;
- }
-
- return false;
- }
-}
diff --git a/webapp/components/suggestion/search_channel_provider.jsx b/webapp/components/suggestion/search_channel_provider.jsx
deleted file mode 100644
index 650ec6973..000000000
--- a/webapp/components/suggestion/search_channel_provider.jsx
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Suggestion from './suggestion.jsx';
-import Provider from './provider.jsx';
-
-import {autocompleteChannels} from 'actions/channel_actions.jsx';
-
-import ChannelStore from 'stores/channel_store.jsx';
-
-import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
-import {Constants, ActionTypes} from 'utils/constants.jsx';
-import {sortChannelsByDisplayName} from 'utils/channel_utils.jsx';
-
-import React from 'react';
-
-class SearchChannelSuggestion extends Suggestion {
- render() {
- const {item, isSelection} = this.props;
-
- let className = 'search-autocomplete__item';
- if (isSelection) {
- className += ' selected';
- }
-
- return (
- <div
- onClick={this.handleClick}
- className={className}
- >
- <i className='fa fa fa-plus-square'/>{item.name}
- </div>
- );
- }
-}
-
-export default class SearchChannelProvider extends Provider {
- handlePretextChanged(suggestionId, pretext) {
- const captured = (/\b(?:in|channel):\s*(\S*)$/i).exec(pretext.toLowerCase());
- if (captured) {
- const channelPrefix = captured[1];
-
- this.startNewRequest(suggestionId, channelPrefix);
-
- autocompleteChannels(
- channelPrefix,
- (data) => {
- if (this.shouldCancelDispatch(channelPrefix)) {
- return;
- }
-
- const publicChannels = data;
-
- const localChannels = ChannelStore.getAll();
- let privateChannels = [];
-
- for (const id of Object.keys(localChannels)) {
- const channel = localChannels[id];
- if (channel.name.startsWith(channelPrefix) && channel.type === Constants.PRIVATE_CHANNEL) {
- privateChannels.push(channel);
- }
- }
-
- let filteredPublicChannels = [];
- publicChannels.forEach((item) => {
- if (item.name.startsWith(channelPrefix)) {
- filteredPublicChannels.push(item);
- }
- });
-
- privateChannels = privateChannels.sort(sortChannelsByDisplayName);
- filteredPublicChannels = filteredPublicChannels.sort(sortChannelsByDisplayName);
-
- 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
- });
- }
- );
- }
-
- return Boolean(captured);
- }
-}
diff --git a/webapp/components/suggestion/search_suggestion_list.jsx b/webapp/components/suggestion/search_suggestion_list.jsx
deleted file mode 100644
index 944ddaeca..000000000
--- a/webapp/components/suggestion/search_suggestion_list.jsx
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import $ from 'jquery';
-import React from 'react';
-import ReactDOM from 'react-dom';
-import Constants from 'utils/constants.jsx';
-import SuggestionList from './suggestion_list.jsx';
-
-import {FormattedMessage} from 'react-intl';
-
-import {Popover} from 'react-bootstrap';
-
-export default class SearchSuggestionList extends SuggestionList {
- static propTypes = {
- ...SuggestionList.propTypes
- };
-
- getContent() {
- return $(ReactDOM.findDOMNode(this.refs.popover)).find('.popover-content');
- }
-
- renderChannelDivider(type) {
- let text;
- if (type === Constants.OPEN_CHANNEL) {
- text = (
- <FormattedMessage
- id='suggestion.search.public'
- defaultMessage='Public Channels'
- />
- );
- } else {
- text = (
- <FormattedMessage
- id='suggestion.search.private'
- defaultMessage='Private Channels'
- />
- );
- }
-
- return (
- <div
- key={type + '-divider'}
- className='search-autocomplete__divider'
- >
- <span>{text}</span>
- </div>
- );
- }
-
- render() {
- if (this.state.items.length === 0) {
- return null;
- }
-
- const items = [];
- 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];
-
- // temporary hack to add dividers between public and private channels in the search suggestion list
- if (i === 0 || item.type !== this.state.items[i - 1].type) {
- if (item.type === Constants.OPEN_CHANNEL) {
- items.push(this.renderChannelDivider(Constants.OPEN_CHANNEL));
- } else if (item.type === Constants.PRIVATE_CHANNEL) {
- items.push(this.renderChannelDivider(Constants.PRIVATE_CHANNEL));
- }
- }
-
- items.push(
- <Component
- key={term}
- ref={term}
- item={item}
- term={term}
- matchedPretext={this.state.matchedPretext[i]}
- isSelection={isSelection}
- onClick={this.props.onCompleteWord}
- />
- );
- }
-
- return (
- <Popover
- ref='popover'
- id='search-autocomplete__popover'
- className='search-help-popover autocomplete visible'
- placement='bottom'
- >
- {items}
- </Popover>
- );
- }
-}
diff --git a/webapp/components/suggestion/search_user_provider.jsx b/webapp/components/suggestion/search_user_provider.jsx
deleted file mode 100644
index f3191c408..000000000
--- a/webapp/components/suggestion/search_user_provider.jsx
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Suggestion from './suggestion.jsx';
-import Provider from './provider.jsx';
-
-import {autocompleteUsersInTeam} from 'actions/user_actions.jsx';
-
-import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
-import * as Utils from 'utils/utils.jsx';
-import {ActionTypes} from 'utils/constants.jsx';
-
-import React from 'react';
-
-class SearchUserSuggestion extends Suggestion {
- render() {
- const {item, isSelection} = this.props;
-
- let className = 'search-autocomplete__item';
- if (isSelection) {
- className += ' selected';
- }
-
- const username = item.username;
- let description = '';
-
- if ((item.first_name || item.last_name) && item.nickname) {
- description = `- ${Utils.getFullName(item)} (${item.nickname})`;
- } else if (item.nickname) {
- description = `- (${item.nickname})`;
- } else if (item.first_name || item.last_name) {
- description = `- ${Utils.getFullName(item)}`;
- }
-
- return (
- <div
- className={className}
- onClick={this.handleClick}
- >
- <i className='fa fa fa-plus-square'/>
- <img
- className='profile-img rounded'
- src={Utils.imageURLForUser(item)}
- />
- <div className='mention--align'>
- <span>
- {username}
- </span>
- <span className='mention__fullname'>
- {' '}
- {description}
- </span>
- </div>
- </div>
- );
- }
-}
-
-export default class SearchUserProvider extends Provider {
- handlePretextChanged(suggestionId, pretext) {
- const captured = (/\bfrom:\s*(\S*)$/i).exec(pretext.toLowerCase());
- if (captured) {
- const usernamePrefix = captured[1];
-
- this.startNewRequest(suggestionId, usernamePrefix);
-
- autocompleteUsersInTeam(
- usernamePrefix,
- (data) => {
- if (this.shouldCancelDispatch(usernamePrefix)) {
- return;
- }
-
- const users = Object.assign([], data.users);
- const mentions = users.map((user) => user.username);
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.SUGGESTION_RECEIVED_SUGGESTIONS,
- id: suggestionId,
- matchedPretext: usernamePrefix,
- terms: mentions,
- items: users,
- component: SearchUserSuggestion
- });
- }
- );
- }
-
- return Boolean(captured);
- }
-}
diff --git a/webapp/components/suggestion/suggestion.jsx b/webapp/components/suggestion/suggestion.jsx
deleted file mode 100644
index ddfdabc7d..000000000
--- a/webapp/components/suggestion/suggestion.jsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import PropTypes from 'prop-types';
-
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import React from 'react';
-
-export default class Suggestion extends React.Component {
- static get propTypes() {
- return {
- item: PropTypes.object.isRequired,
- term: PropTypes.string.isRequired,
- matchedPretext: PropTypes.string.isRequired,
- isSelection: PropTypes.bool,
- onClick: PropTypes.func
- };
- }
-
- constructor(props) {
- super(props);
-
- this.handleClick = this.handleClick.bind(this);
- }
-
- handleClick(e) {
- e.preventDefault();
-
- this.props.onClick(this.props.term, this.props.matchedPretext);
- }
-}
diff --git a/webapp/components/suggestion/suggestion_box.jsx b/webapp/components/suggestion/suggestion_box.jsx
deleted file mode 100644
index 8f50a23ef..000000000
--- a/webapp/components/suggestion/suggestion_box.jsx
+++ /dev/null
@@ -1,441 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from 'utils/constants.jsx';
-import * as GlobalActions from 'actions/global_actions.jsx';
-import SuggestionStore from 'stores/suggestion_store.jsx';
-import * as Utils from 'utils/utils.jsx';
-
-import AutosizeTextarea from 'components/autosize_textarea.jsx';
-
-const KeyCodes = Constants.KeyCodes;
-
-import PropTypes from 'prop-types';
-
-import React from 'react';
-
-export default class SuggestionBox extends React.Component {
- static propTypes = {
-
- /**
- * The list component to render, usually SuggestionList
- */
- listComponent: PropTypes.func.isRequired,
-
- /**
- * The HTML input box type
- */
- type: PropTypes.oneOf(['input', 'textarea', 'search']).isRequired,
-
- /**
- * The value of in the input
- */
- value: PropTypes.string.isRequired,
-
- /**
- * Array of suggestion providers
- */
- providers: PropTypes.arrayOf(PropTypes.object),
-
- /**
- * Where the list will be displayed relative to the input box, defaults to 'top'
- */
- listStyle: PropTypes.string,
-
- /**
- * Set to true to draw dividers between types of list items, defaults to false
- */
- renderDividers: PropTypes.bool,
-
- /**
- * Set to allow TAB to select an item in the list, defaults to true
- */
- completeOnTab: PropTypes.bool,
-
- /**
- * Function called when input box gains focus
- */
- onFocus: PropTypes.func,
-
- /**
- * Function called when input box loses focus
- */
- onBlur: PropTypes.func,
-
- /**
- * Function called when input box value changes
- */
- onChange: PropTypes.func,
-
- /**
- * Function called when a key is pressed and the input box is in focus
- */
- onKeyDown: PropTypes.func,
-
- /**
- * Function called when an item is selected
- */
- onItemSelected: PropTypes.func,
-
- /**
- * Flags if the suggestion_box is for the RHS (Reply).
- */
- isRHS: PropTypes.bool,
-
- /**
- * Function called when @mention is clicked
- */
- popoverMentionKeyClick: PropTypes.bool,
-
- /**
- * The number of characters required to show the suggestion list, defaults to 1
- */
- requiredCharacters: PropTypes.number,
-
- /**
- * If true, the suggestion box is opened on focus, default to false
- */
- openOnFocus: PropTypes.bool
- }
-
- static defaultProps = {
- type: 'input',
- listStyle: 'top',
- renderDividers: false,
- completeOnTab: true,
- isRHS: false,
- requiredCharacters: 1,
- openOnFocus: false
- }
-
- constructor(props) {
- super(props);
-
- this.handleBlur = this.handleBlur.bind(this);
- this.handleFocus = this.handleFocus.bind(this);
-
- this.handlePopoverMentionKeyClick = this.handlePopoverMentionKeyClick.bind(this);
- this.handleCompleteWord = this.handleCompleteWord.bind(this);
- this.handleChange = this.handleChange.bind(this);
- this.handleCompositionStart = this.handleCompositionStart.bind(this);
- this.handleCompositionUpdate = this.handleCompositionUpdate.bind(this);
- this.handleCompositionEnd = this.handleCompositionEnd.bind(this);
- this.handleKeyDown = this.handleKeyDown.bind(this);
- this.handlePretextChanged = this.handlePretextChanged.bind(this);
- this.blur = this.blur.bind(this);
-
- this.suggestionId = Utils.generateId();
- SuggestionStore.registerSuggestionBox(this.suggestionId);
-
- // Keep track of whether we're composing a CJK character so we can make suggestions for partial characters
- this.composing = false;
- }
-
- componentDidMount() {
- if (this.props.popoverMentionKeyClick) {
- SuggestionStore.addPopoverMentionKeyClickListener(this.props.isRHS, this.handlePopoverMentionKeyClick);
- }
- SuggestionStore.addPretextChangedListener(this.suggestionId, this.handlePretextChanged);
- }
-
- componentWillUnmount() {
- if (this.props.popoverMentionKeyClick) {
- SuggestionStore.removePopoverMentionKeyClickListener(this.props.isRHS, this.handlePopoverMentionKeyClick);
- }
- SuggestionStore.removePretextChangedListener(this.suggestionId, this.handlePretextChanged);
-
- SuggestionStore.unregisterSuggestionBox(this.suggestionId);
- }
-
- componentDidUpdate(prevProps) {
- if (this.props.providers !== prevProps.providers) {
- const textbox = this.getTextbox();
- const pretext = textbox.value.substring(0, textbox.selectionEnd);
- GlobalActions.emitSuggestionPretextChanged(this.suggestionId, pretext);
- }
- }
-
- getTextbox() {
- if (this.props.type === 'textarea' && this.refs.textbox) {
- const node = this.refs.textbox.getDOMNode();
- return node;
- }
-
- return this.refs.textbox;
- }
-
- recalculateSize() {
- if (this.props.type === 'textarea') {
- this.refs.textbox.recalculateSize();
- }
- }
-
- handleBlur() {
- setTimeout(() => {
- // Delay this slightly so that we don't clear the suggestions before we run click handlers on SuggestionList
- GlobalActions.emitClearSuggestions(this.suggestionId);
- }, 200);
-
- if (this.props.onBlur) {
- this.props.onBlur();
- }
- }
-
- handleFocus() {
- if (this.props.openOnFocus) {
- setTimeout(() => {
- const textbox = this.getTextbox();
- if (textbox) {
- const pretext = textbox.value.substring(0, textbox.selectionEnd);
- if (pretext.length >= this.props.requiredCharacters) {
- GlobalActions.emitSuggestionPretextChanged(this.suggestionId, pretext);
- }
- }
- });
- }
-
- if (this.props.onFocus) {
- this.props.onFocus();
- }
- }
-
- handleChange(e) {
- const textbox = this.getTextbox();
- const pretext = textbox.value.substring(0, textbox.selectionEnd);
-
- if (!this.composing && SuggestionStore.getPretext(this.suggestionId) !== pretext && pretext.length >= this.props.requiredCharacters) {
- GlobalActions.emitSuggestionPretextChanged(this.suggestionId, pretext);
- }
-
- if (this.props.onChange) {
- this.props.onChange(e);
- }
- }
-
- handleCompositionStart() {
- this.composing = true;
- }
-
- handleCompositionUpdate(e) {
- if (!e.data) {
- return;
- }
-
- // The caret appears before the CJK character currently being composed, so re-add it to the pretext
- const textbox = this.getTextbox();
- const pretext = textbox.value.substring(0, textbox.selectionStart) + e.data;
-
- if (SuggestionStore.getPretext(this.suggestionId) !== pretext) {
- GlobalActions.emitSuggestionPretextChanged(this.suggestionId, pretext);
- }
- }
-
- handleCompositionEnd() {
- this.composing = false;
- }
-
- handlePopoverMentionKeyClick(mentionKey) {
- let insertText = '@' + mentionKey;
-
- // if the current text does not end with a whitespace, then insert a space
- if (this.refs.textbox.value && (/[^\s]$/).test(this.refs.textbox.value)) {
- insertText = ' ' + insertText;
- }
-
- this.handleCompleteWord(insertText, '', false);
- }
-
- handleCompleteWord(term, matchedPretext, shouldEmitWordSuggestion = true) {
- const textbox = this.getTextbox();
- const caret = textbox.selectionEnd;
- const text = this.props.value;
- const pretext = textbox.value.substring(0, textbox.selectionEnd);
-
- let prefix;
- if (pretext.endsWith(matchedPretext)) {
- prefix = pretext.substring(0, pretext.length - matchedPretext.length);
- } else {
- // the pretext has changed since we got a term to complete so see if the term still fits the pretext
- const termWithoutMatched = term.substring(matchedPretext.length);
- const overlap = SuggestionBox.findOverlap(pretext, termWithoutMatched);
-
- prefix = pretext.substring(0, pretext.length - overlap.length - matchedPretext.length);
- }
-
- const suffix = text.substring(caret);
-
- const newValue = prefix + term + ' ' + suffix;
- this.refs.textbox.value = newValue;
-
- if (this.props.onChange) {
- // fake an input event to send back to parent components
- const e = {
- target: this.refs.textbox
- };
-
- // don't call handleChange or we'll get into an event loop
- this.props.onChange(e);
- }
-
- if (this.props.onItemSelected) {
- const items = SuggestionStore.getItems(this.suggestionId);
- const terms = SuggestionStore.getTerms(this.suggestionId);
- for (let i = 0; i < terms.length; i++) {
- if (terms[i] === term) {
- this.props.onItemSelected(items[i]);
- break;
- }
- }
- }
-
- textbox.focus();
-
- // set the caret position after the next rendering
- window.requestAnimationFrame(() => {
- if (textbox.value === newValue) {
- Utils.setCaretPosition(textbox, prefix.length + term.length + 1);
- }
- });
-
- for (const provider of this.props.providers) {
- if (provider.handleCompleteWord) {
- provider.handleCompleteWord(term, matchedPretext);
- }
- }
- if (shouldEmitWordSuggestion) {
- GlobalActions.emitCompleteWordSuggestion(this.suggestionId);
- }
- }
-
- handleKeyDown(e) {
- if (this.props.value && SuggestionStore.hasSuggestions(this.suggestionId)) {
- if (e.which === KeyCodes.UP) {
- GlobalActions.emitSelectPreviousSuggestion(this.suggestionId);
- e.preventDefault();
- } else if (e.which === KeyCodes.DOWN) {
- GlobalActions.emitSelectNextSuggestion(this.suggestionId);
- e.preventDefault();
- } else if (e.which === KeyCodes.ENTER || (this.props.completeOnTab && e.which === KeyCodes.TAB)) {
- this.handleCompleteWord(SuggestionStore.getSelection(this.suggestionId), SuggestionStore.getSelectedMatchedPretext(this.suggestionId));
- if (this.props.onKeyDown) {
- this.props.onKeyDown(e);
- }
- e.preventDefault();
- } else if (e.which === KeyCodes.ESCAPE) {
- GlobalActions.emitClearSuggestions(this.suggestionId);
- e.preventDefault();
- e.stopPropagation();
- } else if (this.props.onKeyDown) {
- this.props.onKeyDown(e);
- }
- } else if (this.props.onKeyDown) {
- this.props.onKeyDown(e);
- }
- }
-
- handlePretextChanged(pretext) {
- let handled = false;
- for (const provider of this.props.providers) {
- handled = provider.handlePretextChanged(this.suggestionId, pretext) || handled;
- }
-
- if (!handled) {
- SuggestionStore.clearSuggestions(this.suggestionId);
- }
- }
-
- blur() {
- this.refs.textbox.blur();
- }
-
- render() {
- const {
- type,
- listComponent,
- listStyle,
- renderDividers,
- ...props
- } = this.props;
-
- // Don't pass props used by SuggestionBox
- Reflect.deleteProperty(props, 'providers');
- Reflect.deleteProperty(props, 'onChange'); // We use onInput instead of onChange on the actual input
- Reflect.deleteProperty(props, 'onItemSelected');
- Reflect.deleteProperty(props, 'completeOnTab');
- Reflect.deleteProperty(props, 'isRHS');
- Reflect.deleteProperty(props, 'popoverMentionKeyClick');
- Reflect.deleteProperty(props, 'requiredCharacters');
- Reflect.deleteProperty(props, 'openOnFocus');
-
- const childProps = {
- ref: 'textbox',
- onBlur: this.handleBlur,
- onFocus: this.handleFocus,
- onInput: this.handleChange,
- onChange() { /* this is only here to suppress warnings about onChange not being implemented for read-write inputs */ },
- onCompositionStart: this.handleCompositionStart,
- onCompositionUpdate: this.handleCompositionUpdate,
- onCompositionEnd: this.handleCompositionEnd,
- onKeyDown: this.handleKeyDown
- };
-
- let textbox = null;
- if (type === 'input') {
- textbox = (
- <input
- type='text'
- autoComplete='off'
- {...props}
- {...childProps}
- />
- );
- } else if (type === 'search') {
- textbox = (
- <input
- type='search'
- autoComplete='off'
- {...props}
- {...childProps}
- />
- );
- } else if (type === 'textarea') {
- textbox = (
- <AutosizeTextarea
- {...props}
- {...childProps}
- />
- );
- }
-
- // This needs to be upper case so React doesn't think it's an html tag
- const SuggestionListComponent = listComponent;
-
- return (
- <div ref='container'>
- {textbox}
- {this.props.value.length >= this.props.requiredCharacters &&
- <SuggestionListComponent
- suggestionId={this.suggestionId}
- location={listStyle}
- renderDividers={renderDividers}
- onCompleteWord={this.handleCompleteWord}
- />
- }
- </div>
- );
- }
-
- // Finds the longest substring that's at both the end of b and the start of a. For example,
- // if a = "firepit" and b = "pitbull", findOverlap would return "pit".
- static findOverlap(a, b) {
- for (let i = b.length; i > 0; i--) {
- const substring = b.substring(0, i);
-
- if (a.endsWith(substring)) {
- return substring;
- }
- }
-
- return '';
- }
-}
diff --git a/webapp/components/suggestion/suggestion_list.jsx b/webapp/components/suggestion/suggestion_list.jsx
deleted file mode 100644
index 64e8713c5..000000000
--- a/webapp/components/suggestion/suggestion_list.jsx
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SuggestionStore from 'stores/suggestion_store.jsx';
-
-import $ from 'jquery';
-import React from 'react';
-import ReactDOM from 'react-dom';
-import PropTypes from 'prop-types';
-import {FormattedMessage} from 'react-intl';
-
-export default class SuggestionList extends React.Component {
- static propTypes = {
- suggestionId: PropTypes.string.isRequired,
- location: PropTypes.string,
- renderDividers: PropTypes.bool,
- onCompleteWord: PropTypes.func.isRequired
- };
-
- static defaultProps = {
- renderDividers: false
- };
-
- constructor(props) {
- super(props);
-
- this.getStateFromStores = this.getStateFromStores.bind(this);
-
- this.getContent = this.getContent.bind(this);
-
- this.handleSuggestionsChanged = this.handleSuggestionsChanged.bind(this);
-
- this.scrollToItem = this.scrollToItem.bind(this);
-
- this.state = this.getStateFromStores(props.suggestionId);
- }
-
- getStateFromStores(suggestionId) {
- const suggestions = SuggestionStore.getSuggestions(suggestionId || this.props.suggestionId);
-
- return {
- matchedPretext: suggestions.matchedPretext,
- items: suggestions.items,
- terms: suggestions.terms,
- components: suggestions.components,
- selection: suggestions.selection
- };
- }
-
- componentDidMount() {
- SuggestionStore.addSuggestionsChangedListener(this.props.suggestionId, this.handleSuggestionsChanged);
- }
-
- componentDidUpdate(prevProps, prevState) {
- if (this.state.selection !== prevState.selection && this.state.selection) {
- this.scrollToItem(this.state.selection);
- }
- }
-
- componentWillUnmount() {
- SuggestionStore.removeSuggestionsChangedListener(this.props.suggestionId, this.handleSuggestionsChanged);
- }
-
- getContent() {
- return $(ReactDOM.findDOMNode(this.refs.content));
- }
-
- handleSuggestionsChanged() {
- this.setState(this.getStateFromStores());
- }
-
- scrollToItem(term) {
- const content = this.getContent();
- if (!content || content.length === 0) {
- return;
- }
-
- const visibleContentHeight = content[0].clientHeight;
- const actualContentHeight = content[0].scrollHeight;
-
- if (visibleContentHeight < actualContentHeight) {
- const contentTop = content.scrollTop();
- const contentTopPadding = parseInt(content.css('padding-top'), 10);
- const contentBottomPadding = parseInt(content.css('padding-top'), 10);
-
- const item = $(ReactDOM.findDOMNode(this.refs[term]));
- const itemTop = item[0].offsetTop - parseInt(item.css('margin-top'), 10);
- const itemBottomMargin = parseInt(item.css('margin-bottom'), 10) + parseInt(item.css('padding-bottom'), 10);
- const itemBottom = item[0].offsetTop + item.height() + itemBottomMargin;
-
- if (itemTop - contentTopPadding < contentTop) {
- // the item is off the top of the visible space
- content.scrollTop(itemTop - contentTopPadding);
- } else if (itemBottom + contentTopPadding + contentBottomPadding > contentTop + visibleContentHeight) {
- // the item has gone off the bottom of the visible space
- content.scrollTop((itemBottom - visibleContentHeight) + contentTopPadding + contentBottomPadding);
- }
- }
- }
-
- renderDivider(type) {
- return (
- <div
- key={type + '-divider'}
- className='suggestion-list__divider'
- >
- <span>
- <FormattedMessage id={'suggestion.' + type}/>
- </span>
- </div>
- );
- }
-
- renderLoading(type) {
- return (
- <div
- key={type + '-loading'}
- className='suggestion-loader'
- >
- <i className='fa fa-spinner fa-pulse fa-fw margin-bottom'/>
- </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 (this.props.renderDividers && item.type !== lastType) {
- items.push(this.renderDivider(item.type));
- lastType = item.type;
- }
-
- if (item.loading) {
- items.push(this.renderLoading(item.type));
- continue;
- }
-
- items.push(
- <Component
- key={term}
- ref={term}
- item={this.state.items[i]}
- term={term}
- matchedPretext={this.state.matchedPretext[i]}
- isSelection={isSelection}
- onClick={this.props.onCompleteWord}
- />
- );
- }
-
- const mainClass = 'suggestion-list suggestion-list--' + this.props.location;
- const contentClass = 'suggestion-list__content suggestion-list__content--' + this.props.location;
-
- return (
- <div className={mainClass}>
- <div
- ref='content'
- className={contentClass}
- >
- {items}
- </div>
- </div>
- );
- }
-}
diff --git a/webapp/components/suggestion/switch_channel_provider.jsx b/webapp/components/suggestion/switch_channel_provider.jsx
deleted file mode 100644
index ac4af9c8b..000000000
--- a/webapp/components/suggestion/switch_channel_provider.jsx
+++ /dev/null
@@ -1,283 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Suggestion from './suggestion.jsx';
-import Provider from './provider.jsx';
-
-import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
-import {Constants, ActionTypes} from 'utils/constants.jsx';
-import * as Utils from 'utils/utils.jsx';
-import {sortChannelsByDisplayName, getChannelDisplayName} from 'utils/channel_utils.jsx';
-
-import React from 'react';
-
-import store from 'stores/redux_store.jsx';
-const getState = store.getState;
-
-import {Client4} from 'mattermost-redux/client';
-
-import {getCurrentUserId, searchProfiles} from 'mattermost-redux/selectors/entities/users';
-import {getChannelsInCurrentTeam, getMyChannelMemberships, getGroupChannels} from 'mattermost-redux/selectors/entities/channels';
-import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
-import {getBool} from 'mattermost-redux/selectors/entities/preferences';
-import {Preferences} from 'mattermost-redux/constants';
-
-class SwitchChannelSuggestion extends Suggestion {
- render() {
- const {item, isSelection} = this.props;
- const channel = item.channel;
- const globeIcon = Constants.GLOBE_ICON_SVG;
- const lockIcon = Constants.LOCK_ICON_SVG;
-
- let className = 'mentions__name';
- if (isSelection) {
- className += ' suggestion--selected';
- }
-
- let displayName = channel.display_name;
- let icon = null;
- if (channel.type === Constants.OPEN_CHANNEL) {
- icon = (
- <span
- className='icon icon__globe icon--body'
- dangerouslySetInnerHTML={{__html: globeIcon}}
- />
- );
- } else if (channel.type === Constants.PRIVATE_CHANNEL) {
- icon = (
- <span
- className='icon icon__lock icon--body'
- dangerouslySetInnerHTML={{__html: lockIcon}}
- />
- );
- } else if (channel.type === Constants.GM_CHANNEL) {
- displayName = getChannelDisplayName(channel);
- icon = <div className='status status--group'>{'G'}</div>;
- } else {
- icon = (
- <div className='pull-left'>
- <img
- className='mention__image'
- src={Utils.imageURLForUser(channel)}
- />
- </div>
- );
- }
-
- return (
- <div
- onClick={this.handleClick}
- className={className}
- >
- {icon}
- {displayName}
- </div>
- );
- }
-}
-
-let prefix = '';
-
-function quickSwitchSorter(wrappedA, wrappedB) {
- if (wrappedA.type === Constants.MENTION_CHANNELS && wrappedB.type === Constants.MENTION_MORE_CHANNELS) {
- return -1;
- } else if (wrappedB.type === Constants.MENTION_CHANNELS && wrappedA.type === Constants.MENTION_MORE_CHANNELS) {
- return 1;
- }
-
- const a = wrappedA.channel;
- const b = wrappedB.channel;
-
- let aDisplayName = getChannelDisplayName(a).toLowerCase();
- let bDisplayName = getChannelDisplayName(b).toLowerCase();
-
- if (a.type === Constants.DM_CHANNEL) {
- aDisplayName = aDisplayName.substring(1);
- }
-
- if (b.type === Constants.DM_CHANNEL) {
- bDisplayName = bDisplayName.substring(1);
- }
-
- const aStartsWith = aDisplayName.startsWith(prefix);
- const bStartsWith = bDisplayName.startsWith(prefix);
- if (aStartsWith && bStartsWith) {
- return sortChannelsByDisplayName(a, b);
- } else if (!aStartsWith && !bStartsWith) {
- return sortChannelsByDisplayName(a, b);
- } else if (aStartsWith) {
- return -1;
- }
-
- return 1;
-}
-
-export default class SwitchChannelProvider extends Provider {
- handlePretextChanged(suggestionId, channelPrefix) {
- if (channelPrefix) {
- prefix = channelPrefix;
- this.startNewRequest(suggestionId, channelPrefix);
-
- // Dispatch suggestions for local data
- const channels = getChannelsInCurrentTeam(getState()).concat(getGroupChannels(getState()));
- const users = Object.assign([], searchProfiles(getState(), channelPrefix, true));
- this.formatChannelsAndDispatch(channelPrefix, suggestionId, channels, users, true);
-
- // Fetch data from the server and dispatch
- this.fetchUsersAndChannels(channelPrefix, suggestionId);
-
- return true;
- }
-
- return false;
- }
-
- async fetchUsersAndChannels(channelPrefix, suggestionId) {
- let teamId = '';
- if (global.window.mm_config.RestrictDirectMessage === 'team') {
- teamId = store.getState().entities.teams.currentTeamId;
- }
- const usersAsync = Client4.autocompleteUsers(channelPrefix, teamId, '');
- const channelsAsync = Client4.searchChannels(getCurrentTeamId(getState()), channelPrefix);
-
- let usersFromServer = [];
- let channelsFromServer = [];
- try {
- usersFromServer = await usersAsync;
- channelsFromServer = await channelsAsync;
- } catch (err) {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_ERROR,
- err
- });
- }
-
- if (this.shouldCancelDispatch(channelPrefix)) {
- return;
- }
-
- const users = Object.assign([], searchProfiles(getState(), channelPrefix, true), usersFromServer.users);
- const channels = getChannelsInCurrentTeam(getState()).concat(getGroupChannels(getState())).concat(channelsFromServer);
- this.formatChannelsAndDispatch(channelPrefix, suggestionId, channels, users);
- }
-
- formatChannelsAndDispatch(channelPrefix, suggestionId, allChannels, users, skipNotInChannel = false) {
- const channels = [];
- const members = getMyChannelMemberships(getState());
-
- if (this.shouldCancelDispatch(channelPrefix)) {
- return;
- }
-
- const currentId = getCurrentUserId(getState());
-
- const completedChannels = {};
-
- for (const id of Object.keys(allChannels)) {
- const channel = allChannels[id];
-
- if (completedChannels[channel.id]) {
- continue;
- }
-
- const member = members[channel.id];
-
- if (channel.display_name.toLowerCase().indexOf(channelPrefix.toLowerCase()) !== -1) {
- const newChannel = Object.assign({}, channel);
- const wrappedChannel = {channel: newChannel, name: newChannel.name};
- if (newChannel.type === Constants.GM_CHANNEL) {
- newChannel.name = getChannelDisplayName(newChannel);
- wrappedChannel.name = newChannel.name;
- const isGMVisible = getBool(getState(), Preferences.CATEGORY_GROUP_CHANNEL_SHOW, newChannel.id, false);
- if (isGMVisible) {
- wrappedChannel.type = Constants.MENTION_CHANNELS;
- } else {
- wrappedChannel.type = Constants.MENTION_MORE_CHANNELS;
- if (skipNotInChannel) {
- continue;
- }
- }
- } else if (member) {
- wrappedChannel.type = Constants.MENTION_CHANNELS;
- } else {
- wrappedChannel.type = Constants.MENTION_MORE_CHANNELS;
- if (skipNotInChannel || !newChannel.display_name.toLowerCase().startsWith(channelPrefix)) {
- continue;
- }
- }
-
- completedChannels[channel.id] = true;
- channels.push(wrappedChannel);
- }
- }
-
- for (let i = 0; i < users.length; i++) {
- const user = users[i];
-
- if (completedChannels[user.id]) {
- continue;
- }
-
- const isDMVisible = getBool(getState(), Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, user.id, false);
- let displayName = `@${user.username} `;
-
- if (user.id === currentId) {
- continue;
- }
-
- if ((user.first_name || user.last_name) && user.nickname) {
- displayName += `- ${Utils.getFullName(user)} (${user.nickname})`;
- } else if (user.nickname) {
- displayName += `- (${user.nickname})`;
- } else if (user.first_name || user.last_name) {
- displayName += `- ${Utils.getFullName(user)}`;
- }
-
- const wrappedChannel = {
- channel: {
- display_name: displayName,
- name: user.username,
- id: user.id,
- update_at: user.update_at,
- type: Constants.DM_CHANNEL,
- last_picture_update: user.last_picture_update || 0
- },
- name: user.username
- };
-
- if (isDMVisible) {
- wrappedChannel.type = Constants.MENTION_CHANNELS;
- } else {
- wrappedChannel.type = Constants.MENTION_MORE_CHANNELS;
- if (skipNotInChannel) {
- continue;
- }
- }
-
- completedChannels[user.id] = true;
- channels.push(wrappedChannel);
- }
-
- const channelNames = channels.
- sort(quickSwitchSorter).
- map((wrappedChannel) => wrappedChannel.channel.name);
-
- if (skipNotInChannel) {
- channels.push({
- type: Constants.MENTION_MORE_CHANNELS,
- loading: true
- });
- }
-
- setTimeout(() => {
- AppDispatcher.handleServerAction({
- type: ActionTypes.SUGGESTION_RECEIVED_SUGGESTIONS,
- id: suggestionId,
- matchedPretext: channelPrefix,
- terms: channelNames,
- items: channels,
- component: SwitchChannelSuggestion
- });
- }, 0);
- }
-}
diff --git a/webapp/components/suggestion/switch_team_provider.jsx b/webapp/components/suggestion/switch_team_provider.jsx
deleted file mode 100644
index ff2a8f24b..000000000
--- a/webapp/components/suggestion/switch_team_provider.jsx
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Suggestion from './suggestion.jsx';
-import Provider from './provider.jsx';
-
-import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
-import {ActionTypes} from 'utils/constants.jsx';
-import LocalizationStore from 'stores/localization_store.jsx';
-
-import React from 'react';
-
-// Redux actions
-import store from 'stores/redux_store.jsx';
-const getState = store.getState;
-
-import * as Selectors from 'mattermost-redux/selectors/entities/teams';
-
-class SwitchTeamSuggestion extends Suggestion {
- render() {
- const {item, isSelection} = this.props;
-
- let className = 'mentions__name';
- if (isSelection) {
- className += ' suggestion--selected';
- }
-
- return (
- <div
- onClick={this.handleClick}
- className={className}
- >
- <div className='status'><i className='fa fa-group'/></div>
- {item.display_name}
- </div>
- );
- }
-}
-
-let prefix = '';
-
-function quickSwitchSorter(a, b) {
- const aDisplayName = a.display_name.toLowerCase();
- const bDisplayName = b.display_name.toLowerCase();
- const aStartsWith = aDisplayName.startsWith(prefix);
- const bStartsWith = bDisplayName.startsWith(prefix);
-
- if (aStartsWith && bStartsWith) {
- const locale = LocalizationStore.getLocale();
-
- if (aDisplayName !== bDisplayName) {
- return aDisplayName.localeCompare(bDisplayName, locale, {numeric: true});
- }
-
- return a.name.localeCompare(b.name, locale, {numeric: true});
- } else if (aStartsWith) {
- return -1;
- }
-
- return 1;
-}
-
-export default class SwitchTeamProvider extends Provider {
- handlePretextChanged(suggestionId, teamPrefix) {
- if (teamPrefix) {
- prefix = teamPrefix;
- this.startNewRequest(suggestionId, teamPrefix);
-
- const allTeams = Selectors.getMyTeams(getState());
-
- const teams = allTeams.filter((team) => {
- return team.display_name.toLowerCase().indexOf(teamPrefix) !== -1 ||
- team.name.indexOf(teamPrefix) !== -1;
- });
-
- const teamNames = teams.
- sort(quickSwitchSorter).
- map((team) => team.name);
-
- setTimeout(() => {
- AppDispatcher.handleServerAction({
- type: ActionTypes.SUGGESTION_RECEIVED_SUGGESTIONS,
- id: suggestionId,
- matchedPretext: teamPrefix,
- terms: teamNames,
- items: teams,
- component: SwitchTeamSuggestion
- });
- }, 0);
-
- return true;
- }
-
- return false;
- }
-}