Browse Source

Fix showing autocomplete helper when it's unneeded

master
Eduard Kuzmenko 4 years ago
parent
commit
b8c3310d78
  1. 10
      src/components/chat/autocompleteHelper.ts
  2. 12
      src/components/chat/autocompleteHelperController.ts
  3. 47
      src/components/chat/commandsHelper.ts
  4. 24
      src/components/chat/emojiHelper.ts
  5. 59
      src/components/chat/input.ts
  6. 31
      src/components/chat/mentionsHelper.ts

10
src/components/chat/autocompleteHelper.ts

@ -86,12 +86,14 @@ export default class AutocompleteHelper extends EventListenerBase<{
this.addEventListener('visible', this.onVisible); this.addEventListener('visible', this.onVisible);
} }
public toggle(hide?: boolean) { public toggle(hide?: boolean, fromController = false) {
if(this.init) { if(this.init) {
return; return;
} }
hide = hide === undefined ? this.container.classList.contains('is-visible') && !this.container.classList.contains('backwards') : hide; if(hide === undefined) {
hide = this.container.classList.contains('is-visible') && !this.container.classList.contains('backwards');
}
if(this.hidden === hide) { if(this.hidden === hide) {
if(!hide && this.resetTarget) { if(!hide && this.resetTarget) {
@ -103,10 +105,10 @@ export default class AutocompleteHelper extends EventListenerBase<{
this.hidden = hide; this.hidden = hide;
if(!this.hidden) { if(!hide) {
this.controller.hideOtherHelpers(this); this.controller.hideOtherHelpers(this);
this.dispatchEvent('visible'); // fire it before so target will be set this.dispatchEvent('visible'); // fire it before so target will be set
} else { } else if(!fromController) {
this.controller.hideOtherHelpers(); this.controller.hideOtherHelpers();
} }

12
src/components/chat/autocompleteHelperController.ts

@ -29,11 +29,15 @@ export default class AutocompleteHelperController {
this.helpers.add(helper); this.helpers.add(helper);
} }
public hideOtherHelpers(helper?: AutocompleteHelper) { public hideOtherHelpers(preserveHelper?: AutocompleteHelper) {
this.helpers.forEach(h => { this.helpers.forEach(helper => {
if(h !== helper) { if(helper !== preserveHelper) {
h.toggle(true); helper.toggle(true, true);
} }
}); });
if(!preserveHelper) {
this.middleware.clean();
}
} }
} }

47
src/components/chat/commandsHelper.ts

@ -5,11 +5,19 @@
*/ */
import type ChatInput from "./input"; import type ChatInput from "./input";
import type { AppProfileManager } from "../../lib/appManagers/appProfileManager";
import type { AppUsersManager } from "../../lib/appManagers/appUsersManager";
import type { BotInfo } from "../../layer";
import AutocompleteHelperController from "./autocompleteHelperController"; import AutocompleteHelperController from "./autocompleteHelperController";
import AutocompletePeerHelper from "./autocompletePeerHelper"; import AutocompletePeerHelper from "./autocompletePeerHelper";
import SearchIndex from "../../lib/searchIndex";
export default class CommandsHelper extends AutocompletePeerHelper { export default class CommandsHelper extends AutocompletePeerHelper {
constructor(appendTo: HTMLElement, controller: AutocompleteHelperController, private chatInput: ChatInput) { constructor(appendTo: HTMLElement,
controller: AutocompleteHelperController,
chatInput: ChatInput,
private appProfileManager: AppProfileManager,
private appUsersManager: AppUsersManager) {
super(appendTo, super(appendTo,
controller, controller,
'commands-helper', 'commands-helper',
@ -20,4 +28,41 @@ export default class CommandsHelper extends AutocompletePeerHelper {
} }
); );
} }
public checkQuery(query: string, peerId: number) {
if(!this.appUsersManager.isBot(peerId)) {
return false;
}
const middleware = this.controller.getMiddleware();
this.appProfileManager.getProfileByPeerId(peerId).then(full => {
if(!middleware()) {
return;
}
const botInfos: BotInfo.botInfo[] = [].concat(full.bot_info);
const index = new SearchIndex<string>(false, false);
const commands: Map<string, {peerId: number, name: string, description: string}> = new Map();
botInfos.forEach(botInfo => {
botInfo.commands.forEach(botCommand => {
const c = '/' + botCommand.command;
commands.set(botCommand.command, {
peerId: botInfo.user_id,
name: c,
description: botCommand.description
});
index.indexObject(botCommand.command, c);
});
});
const found = index.search(query);
const filtered = Array.from(found).map(command => commands.get(command));
this.render(filtered);
// console.log('found commands', found, filtered);
});
return true;
}
} }

24
src/components/chat/emojiHelper.ts

@ -5,6 +5,7 @@
*/ */
import type ChatInput from "./input"; import type ChatInput from "./input";
import type { AppEmojiManager } from "../../lib/appManagers/appEmojiManager";
import { appendEmoji, getEmojiFromElement } from "../emoticonsDropdown/tabs/emoji"; import { appendEmoji, getEmojiFromElement } from "../emoticonsDropdown/tabs/emoji";
import { ScrollableX } from "../scrollable"; import { ScrollableX } from "../scrollable";
import AutocompleteHelper from "./autocompleteHelper"; import AutocompleteHelper from "./autocompleteHelper";
@ -13,13 +14,16 @@ import AutocompleteHelperController from "./autocompleteHelperController";
export default class EmojiHelper extends AutocompleteHelper { export default class EmojiHelper extends AutocompleteHelper {
private scrollable: ScrollableX; private scrollable: ScrollableX;
constructor(appendTo: HTMLElement, controller: AutocompleteHelperController, private chatInput: ChatInput) { constructor(appendTo: HTMLElement,
controller: AutocompleteHelperController,
chatInput: ChatInput,
private appEmojiManager: AppEmojiManager) {
super({ super({
appendTo, appendTo,
controller, controller,
listType: 'x', listType: 'x',
onSelect: (target) => { onSelect: (target) => {
this.chatInput.onEmojiSelected(getEmojiFromElement(target as any), true); chatInput.onEmojiSelected(getEmojiFromElement(target as any), true);
} }
}); });
@ -51,6 +55,8 @@ export default class EmojiHelper extends AutocompleteHelper {
this.init = null; this.init = null;
} }
emojis = emojis.slice(0, 80);
if(emojis.length) { if(emojis.length) {
this.list.innerHTML = ''; this.list.innerHTML = '';
emojis.forEach(emoji => { emojis.forEach(emoji => {
@ -65,4 +71,18 @@ export default class EmojiHelper extends AutocompleteHelper {
this.container.style.width = (3 * 2) + (emojis.length * 44) + 'px'; this.container.style.width = (3 * 2) + (emojis.length * 44) + 'px';
}); */ }); */
} }
public checkQuery(query: string, firstChar: string) {
const middleware = this.controller.getMiddleware();
this.appEmojiManager.getBothEmojiKeywords().then(() => {
if(!middleware()) {
return;
}
const q = query.replace(/^:/, '');
const emojis = this.appEmojiManager.searchEmojis(q);
this.render(emojis, firstChar !== ':');
//console.log(emojis);
});
}
} }

59
src/components/chat/input.ts

@ -60,11 +60,9 @@ import { MarkdownType, markdownTags } from '../../helpers/dom/getRichElementValu
import getRichValueWithCaret from '../../helpers/dom/getRichValueWithCaret'; import getRichValueWithCaret from '../../helpers/dom/getRichValueWithCaret';
import EmojiHelper from './emojiHelper'; import EmojiHelper from './emojiHelper';
import setRichFocus from '../../helpers/dom/setRichFocus'; import setRichFocus from '../../helpers/dom/setRichFocus';
import SearchIndex from '../../lib/searchIndex';
import CommandsHelper from './commandsHelper'; import CommandsHelper from './commandsHelper';
import AutocompleteHelperController from './autocompleteHelperController'; import AutocompleteHelperController from './autocompleteHelperController';
import AutocompleteHelper from './autocompleteHelper'; import AutocompleteHelper from './autocompleteHelper';
import appUsersManager from '../../lib/appManagers/appUsersManager';
import MentionsHelper from './mentionsHelper'; import MentionsHelper from './mentionsHelper';
const RECORD_MIN_TIME = 500; const RECORD_MIN_TIME = 500;
@ -372,9 +370,9 @@ export default class ChatInput {
this.rowsWrapper.append(this.replyElements.container); this.rowsWrapper.append(this.replyElements.container);
this.autocompleteHelperController = new AutocompleteHelperController(); this.autocompleteHelperController = new AutocompleteHelperController();
this.stickersHelper = new StickersHelper(this.rowsWrapper, this.autocompleteHelperController); this.stickersHelper = new StickersHelper(this.rowsWrapper, this.autocompleteHelperController);
this.emojiHelper = new EmojiHelper(this.rowsWrapper, this.autocompleteHelperController, this); this.emojiHelper = new EmojiHelper(this.rowsWrapper, this.autocompleteHelperController, this, this.appEmojiManager);
this.commandsHelper = new CommandsHelper(this.rowsWrapper, this.autocompleteHelperController, this); this.commandsHelper = new CommandsHelper(this.rowsWrapper, this.autocompleteHelperController, this, this.chat.appProfileManager, this.chat.appUsersManager);
this.mentionsHelper = new MentionsHelper(this.rowsWrapper, this.autocompleteHelperController, this); this.mentionsHelper = new MentionsHelper(this.rowsWrapper, this.autocompleteHelperController, this, this.chat.appProfileManager, this.chat.appUsersManager);
this.rowsWrapper.append(this.newMessageWrapper); this.rowsWrapper.append(this.newMessageWrapper);
this.btnCancelRecord = ButtonIcon('delete danger btn-circle z-depth-1 btn-record-cancel'); this.btnCancelRecord = ButtonIcon('delete danger btn-circle z-depth-1 btn-record-cancel');
@ -1258,61 +1256,18 @@ export default class ChatInput {
//console.log('autocomplete matches', matches); //console.log('autocomplete matches', matches);
if(firstChar === '@') { // mentions if(firstChar === '@') { // mentions
const trimmed = query.trim(); // check that there is no whitespace const topMsgId = this.chat.threadId ? this.appMessagesManager.getServerMessageId(this.chat.threadId) : undefined;
if(this.chat.peerId < 0 && query.length === trimmed.length) { if(this.mentionsHelper.checkQuery(query, this.chat.peerId, topMsgId)) {
foundHelper = this.mentionsHelper; foundHelper = this.mentionsHelper;
const topMsgId = this.chat.threadId ? this.appMessagesManager.getServerMessageId(this.chat.threadId) : undefined;
this.chat.appProfileManager.getMentions(-this.chat.peerId, trimmed, topMsgId).then(peerIds => {
const username = trimmed.slice(1).toLowerCase();
this.mentionsHelper.render(peerIds.map(peerId => {
const user = this.chat.appUsersManager.getUser(peerId);
if(user.username && user.username.toLowerCase() === username) { // hide full matched suggestion
return;
}
return {
peerId,
description: user.username ? '@' + user.username : undefined
};
}).filter(Boolean));
});
} }
} else if(!matches[1] && firstChar === '/') { // commands } else if(!matches[1] && firstChar === '/') { // commands
if(appUsersManager.isBot(this.chat.peerId)) { if(this.commandsHelper.checkQuery(query, this.chat.peerId)) {
foundHelper = this.commandsHelper; foundHelper = this.commandsHelper;
this.chat.appProfileManager.getProfileByPeerId(this.chat.peerId).then(full => {
const botInfos: BotInfo.botInfo[] = [].concat(full.bot_info);
const index = new SearchIndex<string>(true, false);
const commands: Map<string, {peerId: number, name: string, description: string}> = new Map();
botInfos.forEach(botInfo => {
botInfo.commands.forEach(botCommand => {
const c = '/' + botCommand.command;
commands.set(botCommand.command, {
peerId: botInfo.user_id,
name: c,
description: botCommand.description
});
index.indexObject(botCommand.command, c);
});
});
const found = index.search(query);
const filtered = Array.from(found).map(command => commands.get(command));
this.commandsHelper.render(filtered);
// console.log('found commands', found, filtered);
});
} }
} else if(rootScope.settings.emoji.suggest) { // emoji } else if(rootScope.settings.emoji.suggest) { // emoji
if(!value.match(/^\s*:(.+):\s*$/)) { if(!value.match(/^\s*:(.+):\s*$/)) {
foundHelper = this.emojiHelper; foundHelper = this.emojiHelper;
this.appEmojiManager.getBothEmojiKeywords().then(() => { this.emojiHelper.checkQuery(query, firstChar);
const q = query.replace(/^:/, '');
const emojis = this.appEmojiManager.searchEmojis(q);
this.emojiHelper.render(emojis, firstChar !== ':');
//console.log(emojis);
});
} }
} }

31
src/components/chat/mentionsHelper.ts

@ -6,12 +6,17 @@
import type ChatInput from "./input"; import type ChatInput from "./input";
import type { MessageEntity } from "../../layer"; import type { MessageEntity } from "../../layer";
import type { AppProfileManager } from "../../lib/appManagers/appProfileManager";
import type { AppUsersManager } from "../../lib/appManagers/appUsersManager";
import AutocompleteHelperController from "./autocompleteHelperController"; import AutocompleteHelperController from "./autocompleteHelperController";
import AutocompletePeerHelper from "./autocompletePeerHelper"; import AutocompletePeerHelper from "./autocompletePeerHelper";
import appUsersManager from "../../lib/appManagers/appUsersManager";
export default class MentionsHelper extends AutocompletePeerHelper { export default class MentionsHelper extends AutocompletePeerHelper {
constructor(appendTo: HTMLElement, controller: AutocompleteHelperController, private chatInput: ChatInput) { constructor(appendTo: HTMLElement,
controller: AutocompleteHelperController,
chatInput: ChatInput,
private appProfileManager: AppProfileManager,
private appUsersManager: AppUsersManager) {
super(appendTo, super(appendTo,
controller, controller,
'mentions-helper', 'mentions-helper',
@ -35,4 +40,26 @@ export default class MentionsHelper extends AutocompletePeerHelper {
} }
); );
} }
public checkQuery(query: string, peerId: number, topMsgId: number) {
const trimmed = query.trim(); // check that there is no whitespace
if(peerId > 0 || query.length !== trimmed.length) return false;
this.appProfileManager.getMentions(-peerId, trimmed, topMsgId).then(peerIds => {
const username = trimmed.slice(1).toLowerCase();
this.render(peerIds.map(peerId => {
const user = this.appUsersManager.getUser(peerId);
if(user.username && user.username.toLowerCase() === username) { // hide full matched suggestion
return;
}
return {
peerId,
description: user.username ? '@' + user.username : undefined
};
}).filter(Boolean));
});
return true;
}
} }

Loading…
Cancel
Save