Remove race between multiple autocomplete requests (#4860)

Этот коммит содержится в:
Joram Wilander
2016-12-21 12:45:21 -05:00
коммит произвёл Harrison Healey
родитель 26e43c671c
Коммит cadc9e11e4
8 изменённых файлов: 80 добавлений и 20 удалений

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

@@ -2,6 +2,7 @@
// See License.txt for license information.
import Suggestion from './suggestion.jsx';
import Provider from './provider.jsx';
import ChannelStore from 'stores/channel_store.jsx';
@@ -100,8 +101,10 @@ class AtMentionSuggestion extends Suggestion {
}
}
export default class AtMentionProvider {
export default class AtMentionProvider extends Provider {
constructor(channelId) {
super();
this.channelId = channelId;
}
@@ -110,10 +113,16 @@ export default class AtMentionProvider {
if (captured) {
const prefix = captured[1];
this.startNewRequest(prefix);
autocompleteUsersInChannel(
prefix,
this.channelId,
(data) => {
if (this.shouldCancelDispatch(prefix)) {
return;
}
const members = data.in_channel;
for (const id of Object.keys(members)) {
members[id].type = Constants.MENTION_MEMBERS;

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

@@ -2,6 +2,7 @@
// See License.txt for license information.
import Suggestion from './suggestion.jsx';
import Provider from './provider.jsx';
import {autocompleteChannels} from 'actions/channel_actions.jsx';
@@ -49,15 +50,21 @@ class ChannelMentionSuggestion extends Suggestion {
}
}
export default class ChannelMentionProvider {
export default class ChannelMentionProvider extends Provider {
handlePretextChanged(suggestionId, pretext) {
const captured = (/(^|\s)(~([^~]*))$/i).exec(pretext.toLowerCase());
if (captured) {
const prefix = captured[3];
this.startNewRequest(prefix);
autocompleteChannels(
prefix,
(data) => {
if (this.shouldCancelDispatch(prefix)) {
return;
}
const channels = data;
// Wrap channels in an outer object to avoid overwriting the 'type' property.

28
webapp/components/suggestion/provider.jsx Обычный файл
Просмотреть файл

@@ -0,0 +1,28 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
export default class Provider {
constructor() {
this.latestPrefix = '';
this.latestComplete = true;
}
handlePretextChanged(suggestionId, pretext) { // eslint-disable-line no-unused-vars
// NO-OP for inherited classes to override
}
startNewRequest(prefix) {
this.latestPrefix = prefix;
this.latestComplete = false;
}
shouldCancelDispatch(prefix) {
if (prefix === this.latestPrefix) {
this.latestComplete = true;
} else if (this.latestComplete) {
return true;
}
return false;
}
}

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

@@ -2,6 +2,7 @@
// See License.txt for license information.
import Suggestion from './suggestion.jsx';
import Provider from './provider.jsx';
import {autocompleteChannels} from 'actions/channel_actions.jsx';
@@ -32,15 +33,21 @@ class SearchChannelSuggestion extends Suggestion {
}
}
export default class SearchChannelProvider {
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(channelPrefix);
autocompleteChannels(
channelPrefix,
(data) => {
if (this.shouldCancelDispatch(channelPrefix)) {
return;
}
const publicChannels = data;
const localChannels = ChannelStore.getAll();

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

@@ -2,6 +2,7 @@
// See License.txt for license information.
import Suggestion from './suggestion.jsx';
import Provider from './provider.jsx';
import {autocompleteUsersInTeam} from 'actions/user_actions.jsx';
@@ -56,15 +57,21 @@ class SearchUserSuggestion extends Suggestion {
}
}
export default class SearchUserProvider {
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(usernamePrefix);
autocompleteUsersInTeam(
usernamePrefix,
(data) => {
if (this.shouldCancelDispatch(usernamePrefix)) {
return;
}
const users = data.in_team;
const mentions = users.map((user) => user.username);

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

@@ -38,14 +38,6 @@ export default class SuggestionBox extends React.Component {
SuggestionStore.addPretextChangedListener(this.suggestionId, this.handlePretextChanged);
}
componentWillReceiveProps(nextProps) {
// Clear any suggestions when the SuggestionBox is cleared
if (nextProps.value === '' && this.props.value !== nextProps.value) {
// TODO - Find a better way to not "dispatch during dispatch"
setTimeout(() => GlobalActions.emitClearSuggestions(this.suggestionId), 1);
}
}
componentWillUnmount() {
SuggestionStore.removeCompleteWordListener(this.suggestionId, this.handleCompleteWord);
SuggestionStore.removePretextChangedListener(this.suggestionId, this.handlePretextChanged);

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

@@ -2,6 +2,7 @@
// See License.txt for license information.
import Suggestion from './suggestion.jsx';
import Provider from './provider.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import UserStore from 'stores/user_store.jsx';
@@ -58,15 +59,21 @@ class SwitchChannelSuggestion extends Suggestion {
}
}
export default class SwitchChannelProvider {
export default class SwitchChannelProvider extends Provider {
handlePretextChanged(suggestionId, channelPrefix) {
if (channelPrefix) {
this.startNewRequest(channelPrefix);
const allChannels = ChannelStore.getAll();
const channels = [];
autocompleteUsers(
channelPrefix,
(users) => {
if (this.shouldCancelDispatch(channelPrefix)) {
return;
}
const currentId = UserStore.getCurrentId();
for (const id of Object.keys(allChannels)) {