Browse Source

Fix commands entities on sending

Fix emojis without FE0F for Apple's devices
Shortened autocomplete helpers
master
morethanwords 4 years ago
parent
commit
b2e3683f93
  1. 6
      src/components/chat/autocompleteHelperController.ts
  2. 114
      src/components/chat/autocompletePeerHelper.ts
  3. 11
      src/components/chat/bubbles.ts
  4. 83
      src/components/chat/commandsHelper.ts
  5. 10
      src/components/chat/emojiHelper.ts
  6. 94
      src/components/chat/input.ts
  7. 24
      src/components/chat/mentionsHelper.ts
  8. 19
      src/components/chat/stickersHelper.ts
  9. 2
      src/components/emoticonsDropdown/tabs/emoji.ts
  10. 12
      src/lib/appManagers/appProfileManager.ts
  11. 7
      src/lib/richtextprocessor.ts
  12. 6
      src/lib/searchIndex.ts
  13. 5
      src/scss/partials/_autocompleteHelper.scss
  14. 44
      src/scss/partials/_autocompletePeerHelper.scss
  15. 33
      src/scss/partials/_chatCommandsHelper.scss
  16. 2
      src/scss/partials/_chatEmojiHelper.scss
  17. 11
      src/scss/partials/_chatStickersHelper.scss
  18. 2
      src/scss/style.scss

6
src/components/chat/autocompleteHelperController.ts

@ -1,3 +1,9 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { getMiddleware } from "../../helpers/middleware"; import { getMiddleware } from "../../helpers/middleware";
import AutocompleteHelper from "./autocompleteHelper"; import AutocompleteHelper from "./autocompleteHelper";

114
src/components/chat/autocompletePeerHelper.ts

@ -0,0 +1,114 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import RichTextProcessor from "../../lib/richtextprocessor";
import AvatarElement from "../avatar";
import PeerTitle from "../peerTitle";
import Scrollable from "../scrollable";
import AutocompleteHelper from "./autocompleteHelper";
import AutocompleteHelperController from "./autocompleteHelperController";
export default class AutocompletePeerHelper extends AutocompleteHelper {
protected static BASE_CLASS = 'autocomplete-peer-helper';
protected static BASE_CLASS_LIST_ELEMENT = AutocompletePeerHelper.BASE_CLASS + '-list-element';
private scrollable: Scrollable;
constructor(appendTo: HTMLElement, controller: AutocompleteHelperController, protected className: string, onSelect: (target: Element) => boolean | void) {
super({
appendTo,
controller,
listType: 'y',
onSelect
});
this.container.classList.add(AutocompletePeerHelper.BASE_CLASS, className);
}
protected init() {
this.list = document.createElement('div');
this.list.classList.add(AutocompletePeerHelper.BASE_CLASS + '-list');
this.container.append(this.list);
this.scrollable = new Scrollable(this.container);
this.addEventListener('visible', () => {
setTimeout(() => { // it is not rendered yet
this.scrollable.container.scrollTop = 0;
}, 0);
});
}
public render(data: {peerId: number, name?: string, description?: string}[]) {
if(this.init) {
if(!data.length) {
return;
}
this.init();
this.init = null;
}
if(data.length) {
this.list.innerHTML = '';
data.forEach(d => {
const div = AutocompletePeerHelper.listElement({
className: this.className,
peerId: d.peerId,
name: d.name,
description: d.description
});
this.list.append(div);
});
}
this.toggle(!data.length);
}
public static listElement(options: {
className: string,
peerId: number,
name?: string,
description?: string
}) {
const BASE = AutocompletePeerHelper.BASE_CLASS_LIST_ELEMENT;
options.className += '-list-element';
const div = document.createElement('div');
div.classList.add(BASE, options.className);
div.dataset.peerId = '' + options.peerId;
const avatar = new AvatarElement();
avatar.classList.add('avatar-30');
avatar.setAttribute('dialog', '0');
avatar.setAttribute('peer', '' + options.peerId);
const name = document.createElement('div');
name.classList.add(BASE + '-name', options.className + '-name');
if(!options.name) {
name.append(new PeerTitle({
peerId: options.peerId,
dialog: false,
onlyFirstName: false,
plainText: false
}).element);
} else {
name.innerHTML = RichTextProcessor.wrapEmojiText(options.name);
}
div.append(avatar, name);
if(options.description) {
const description = document.createElement('div');
description.classList.add(BASE + '-description', options.className + '-description');
description.innerHTML = RichTextProcessor.wrapEmojiText(options.description);
div.append(description);
}
return div;
}
}

11
src/components/chat/bubbles.ts

@ -143,6 +143,10 @@ export default class ChatBubbles {
private fetchNewPromise: Promise<void>; private fetchNewPromise: Promise<void>;
private historyStorage: HistoryStorage; private historyStorage: HistoryStorage;
private passEntities: Partial<{
[_ in MessageEntity['_']]: boolean
}> = {};
constructor(private chat: Chat, private appMessagesManager: AppMessagesManager, private appStickersManager: AppStickersManager, private appUsersManager: AppUsersManager, private appInlineBotsManager: AppInlineBotsManager, private appPhotosManager: AppPhotosManager, private appDocsManager: AppDocsManager, private appPeersManager: AppPeersManager, private appChatsManager: AppChatsManager, private storage: typeof sessionStorage) { constructor(private chat: Chat, private appMessagesManager: AppMessagesManager, private appStickersManager: AppStickersManager, private appUsersManager: AppUsersManager, private appInlineBotsManager: AppInlineBotsManager, private appPhotosManager: AppPhotosManager, private appDocsManager: AppDocsManager, private appPeersManager: AppPeersManager, private appChatsManager: AppChatsManager, private storage: typeof sessionStorage) {
//this.chat.log.error('Bubbles construction'); //this.chat.log.error('Bubbles construction');
@ -1544,6 +1548,10 @@ export default class ChatBubbles {
this.peerId = peerId; this.peerId = peerId;
this.replyFollowHistory.length = 0; this.replyFollowHistory.length = 0;
this.passEntities = {
messageEntityBotCommand: this.appPeersManager.isAnyGroup(this.peerId) || this.appUsersManager.isBot(this.peerId)
};
} }
if(DEBUG) { if(DEBUG) {
@ -2029,7 +2037,8 @@ export default class ChatBubbles {
entities: totalEntities entities: totalEntities
}); */ }); */
let richText = RichTextProcessor.wrapRichText(messageMessage, { let richText = RichTextProcessor.wrapRichText(messageMessage, {
entities: totalEntities entities: totalEntities,
passEntities: this.passEntities
}); });
let canHaveTail = true; let canHaveTail = true;

83
src/components/chat/commandsHelper.ts

@ -1,78 +1,23 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import type ChatInput from "./input"; import type ChatInput from "./input";
import { BotCommand } from "../../layer";
import RichTextProcessor from "../../lib/richtextprocessor";
import AvatarElement from "../avatar";
import Scrollable from "../scrollable";
import AutocompleteHelper from "./autocompleteHelper";
import AutocompleteHelperController from "./autocompleteHelperController"; import AutocompleteHelperController from "./autocompleteHelperController";
import AutocompletePeerHelper from "./autocompletePeerHelper";
export default class CommandsHelper extends AutocompleteHelper { export default class CommandsHelper extends AutocompletePeerHelper {
private scrollable: Scrollable;
constructor(appendTo: HTMLElement, controller: AutocompleteHelperController, private chatInput: ChatInput) { constructor(appendTo: HTMLElement, controller: AutocompleteHelperController, private chatInput: ChatInput) {
super({ super(appendTo,
appendTo,
controller, controller,
listType: 'y', 'commands-helper',
onSelect: (target) => { (target) => {
const command = target.querySelector('.commands-helper-command-name').innerHTML; const innerHTML = target.querySelector(`.${AutocompletePeerHelper.BASE_CLASS_LIST_ELEMENT}-name`).innerHTML;
chatInput.messageInput.innerHTML = command; chatInput.messageInput.innerHTML = innerHTML;
chatInput.sendMessage(); chatInput.sendMessage();
} }
}); );
this.container.classList.add('commands-helper');
}
protected init() {
this.list = document.createElement('div');
this.list.classList.add('commands-helper-commands');
this.container.append(this.list);
this.scrollable = new Scrollable(this.container);
this.addEventListener('visible', () => {
setTimeout(() => { // it is not rendered yet
this.scrollable.container.scrollTop = 0;
}, 0);
});
}
public render(commands: {userId: number, command: BotCommand}[]) {
if(this.init) {
if(!commands.length) {
return;
}
this.init();
this.init = null;
}
if(commands.length) {
this.list.innerHTML = '';
commands.forEach(command => {
const div = document.createElement('div');
div.classList.add('commands-helper-command');
const avatar = new AvatarElement();
avatar.classList.add('avatar-30');
avatar.setAttribute('dialog', '0');
avatar.setAttribute('peer', '' + command.userId);
const name = document.createElement('div');
name.classList.add('commands-helper-command-name');
name.innerText = '/' + command.command.command;
const description = document.createElement('div');
description.classList.add('commands-helper-command-description');
description.innerHTML = RichTextProcessor.wrapEmojiText(command.command.description);
div.append(avatar, name, description);
this.list.append(div);
});
}
this.toggle(!commands.length);
} }
} }

10
src/components/chat/emojiHelper.ts

@ -1,3 +1,9 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import type ChatInput from "./input"; import type ChatInput from "./input";
import { appendEmoji, getEmojiFromElement } from "../emoticonsDropdown/tabs/emoji"; import { appendEmoji, getEmojiFromElement } from "../emoticonsDropdown/tabs/emoji";
import { ScrollableX } from "../scrollable"; import { ScrollableX } from "../scrollable";
@ -54,5 +60,9 @@ export default class EmojiHelper extends AutocompleteHelper {
this.waitForKey = waitForKey ? 'ArrowUp' : undefined; this.waitForKey = waitForKey ? 'ArrowUp' : undefined;
this.toggle(!emojis.length); this.toggle(!emojis.length);
/* window.requestAnimationFrame(() => {
this.container.style.width = (3 * 2) + (emojis.length * 44) + 'px';
}); */
} }
} }

94
src/components/chat/input.ts

@ -65,6 +65,7 @@ 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 appUsersManager from '../../lib/appManagers/appUsersManager';
import MentionsHelper from './mentionsHelper';
const RECORD_MIN_TIME = 500; const RECORD_MIN_TIME = 500;
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.'; const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';
@ -136,9 +137,10 @@ export default class ChatInput {
private canUndoFromHTML = ''; private canUndoFromHTML = '';
private autocompleteHelperController: AutocompleteHelperController; private autocompleteHelperController: AutocompleteHelperController;
private commandsHelper: CommandsHelper;
private emojiHelper: EmojiHelper;
private stickersHelper: StickersHelper; private stickersHelper: StickersHelper;
private emojiHelper: EmojiHelper;
private commandsHelper: CommandsHelper;
private mentionsHelper: MentionsHelper;
private listenerSetter: ListenerSetter; private listenerSetter: ListenerSetter;
private pinnedControlBtn: HTMLButtonElement; private pinnedControlBtn: HTMLButtonElement;
@ -374,6 +376,7 @@ export default class ChatInput {
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.commandsHelper = new CommandsHelper(this.rowsWrapper, this.autocompleteHelperController, this); this.commandsHelper = new CommandsHelper(this.rowsWrapper, this.autocompleteHelperController, this);
this.mentionsHelper = new MentionsHelper(this.rowsWrapper, this.autocompleteHelperController, this);
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');
@ -1179,34 +1182,19 @@ export default class ChatInput {
RichTextProcessor.mergeEntities(entities, addEntities); RichTextProcessor.mergeEntities(entities, addEntities);
//const saveExecuted = this.prepareDocumentExecute(); //const saveExecuted = this.prepareDocumentExecute();
this.messageInputField.value = RichTextProcessor.wrapDraftText(newValue, {entities}); // can't exec .value here because it will instantly check for autocomplete
this.messageInputField.setValueSilently(RichTextProcessor.wrapDraftText(newValue, {entities}), true);
const caret = this.messageInput.querySelector('.composer-sel'); const caret = this.messageInput.querySelector('.composer-sel');
setRichFocus(this.messageInput, caret); setRichFocus(this.messageInput, caret);
caret.remove(); caret.remove();
// but it's needed to be checked only here
this.onMessageInput();
//saveExecuted(); //saveExecuted();
//document.execCommand('insertHTML', true, RichTextProcessor.wrapEmojiText(emoji)); //document.execCommand('insertHTML', true, RichTextProcessor.wrapEmojiText(emoji));
//const str =
/* var newValuePrefix
if(matches && matches[0]) {
newValuePrefix = prefix.substr(0, matches.index) + ':' + emoji[1] + ':'
} else {
newValuePrefix = prefix + ':' + emoji[1] + ':'
}
if(suffix.length) {
const html = this.getRichHtml(newValuePrefix) + '&nbsp;<span id="composer_sel' + ++selId + '"></span>' + this.getRichHtml(suffix)
this.richTextareaEl.html(html)
setRichFocus(textarea, $('#composer_sel' + this.selId)[0])
} else {
const html = this.getRichHtml(newValuePrefix) + '&nbsp;'
this.richTextareaEl.html(html)
setRichFocus(textarea)
} */
} }
}; };
@ -1247,6 +1235,9 @@ export default class ChatInput {
let foundHelper: AutocompleteHelper; let foundHelper: AutocompleteHelper;
const entity = entities[0]; const entity = entities[0];
const query = matches[2];
const firstChar = query[0];
if(this.stickersHelper && if(this.stickersHelper &&
rootScope.settings.stickers.suggest && rootScope.settings.stickers.suggest &&
(this.chat.peerId > 0 || this.appChatsManager.hasRights(this.chat.peerId, 'send_stickers')) && (this.chat.peerId > 0 || this.appChatsManager.hasRights(this.chat.peerId, 'send_stickers')) &&
@ -1254,50 +1245,45 @@ export default class ChatInput {
foundHelper = this.stickersHelper; foundHelper = this.stickersHelper;
this.stickersHelper.checkEmoticon(value); this.stickersHelper.checkEmoticon(value);
} else } else
//let query = cleanSearchText(matches[2]); //let query = cleanSearchText(query);
//const firstChar = matches[2][0];
//console.log('autocomplete matches', matches); //console.log('autocomplete matches', matches);
/*if (matches[2] == '@') { // mentions /* if(firstChar === '@') { // mentions
if (this.mentions && this.mentions.index) { if(this.chat.peerId < 0) {
if (query.length) { foundHelper = this.mentionsHelper;
var foundObject = SearchIndexManager.search(query, this.mentions.index) this.chat.appProfileManager.getMentions(-this.chat.peerId, query).then(peerIds => {
var foundUsers = [] this.mentionsHelper.render(peerIds.map(peerId => {
var user const user = this.chat.appUsersManager.getUser(peerId);
for (var i = 0, length = this.mentions.users.length; i < length; i++) { return {
user = this.mentions.users[i] peerId,
if (foundObject[user.id]) { description: user.username ? '@' + user.username : undefined
foundUsers.push(user) };
} }));
} });
} else {
var foundUsers = this.mentions.users
}
if (foundUsers.length) {
this.showMentionSuggestions(foundUsers)
} else {
this.hideSuggestions()
}
} else {
this.hideSuggestions()
} }
} else */ if(!matches[1] && matches[2][0] === '/') { // commands } else */if(!matches[1] && firstChar === '/') { // commands
if(appUsersManager.isBot(this.chat.peerId)) { if(appUsersManager.isBot(this.chat.peerId)) {
foundHelper = this.commandsHelper; foundHelper = this.commandsHelper;
this.chat.appProfileManager.getProfileByPeerId(this.chat.peerId).then(full => { this.chat.appProfileManager.getProfileByPeerId(this.chat.peerId).then(full => {
const botInfos: BotInfo.botInfo[] = [].concat(full.bot_info); const botInfos: BotInfo.botInfo[] = [].concat(full.bot_info);
const index = new SearchIndex<string>(false, false); const index = new SearchIndex<string>(true, false);
const commands: Map<string, {userId: number, command: BotCommand}> = new Map(); const commands: Map<string, {peerId: number, name: string, description: string}> = new Map();
botInfos.forEach(botInfo => { botInfos.forEach(botInfo => {
botInfo.commands.forEach(botCommand => { botInfo.commands.forEach(botCommand => {
commands.set(botCommand.command, {userId: botInfo.user_id, command: botCommand}); const c = '/' + botCommand.command;
index.indexObject(botCommand.command, '/' + botCommand.command); commands.set(botCommand.command, {
peerId: botInfo.user_id,
name: c,
description: botCommand.description
});
index.indexObject(botCommand.command, c);
}); });
}); });
const found = index.search(matches[2]); const found = index.search(query);
const filtered = Array.from(found).map(command => commands.get(command)); const filtered = Array.from(found).map(command => commands.get(command));
this.commandsHelper.render(filtered); this.commandsHelper.render(filtered);
// console.log('found commands', found, filtered); // console.log('found commands', found, filtered);
@ -1307,9 +1293,9 @@ export default class ChatInput {
if(!value.match(/^\s*:(.+):\s*$/)) { if(!value.match(/^\s*:(.+):\s*$/)) {
foundHelper = this.emojiHelper; foundHelper = this.emojiHelper;
this.appEmojiManager.getBothEmojiKeywords().then(() => { this.appEmojiManager.getBothEmojiKeywords().then(() => {
const q = matches[2].replace(/^:/, ''); const q = query.replace(/^:/, '');
const emojis = this.appEmojiManager.searchEmojis(q); const emojis = this.appEmojiManager.searchEmojis(q);
this.emojiHelper.render(emojis, matches[2][0] !== ':'); this.emojiHelper.render(emojis, firstChar !== ':');
//console.log(emojis); //console.log(emojis);
}); });
} }

24
src/components/chat/mentionsHelper.ts

@ -0,0 +1,24 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import type ChatInput from "./input";
import AutocompleteHelperController from "./autocompleteHelperController";
import AutocompletePeerHelper from "./autocompletePeerHelper";
import placeCaretAtEnd from "../../helpers/dom/placeCaretAtEnd";
export default class MentionsHelper extends AutocompletePeerHelper {
constructor(appendTo: HTMLElement, controller: AutocompleteHelperController, private chatInput: ChatInput) {
super(appendTo,
controller,
'mentions-helper',
(target) => {
const innerHTML = target.querySelector(`.${AutocompletePeerHelper.BASE_CLASS_LIST_ELEMENT}-description`).innerHTML;
chatInput.messageInputField.value = innerHTML + ' ';
placeCaretAtEnd(chatInput.messageInput);
}
);
}
}

19
src/components/chat/stickersHelper.ts

@ -4,6 +4,8 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
import mediaSizes from "../../helpers/mediaSizes";
import { clamp } from "../../helpers/number";
import { MyDocument } from "../../lib/appManagers/appDocsManager"; import { MyDocument } from "../../lib/appManagers/appDocsManager";
import { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager"; import { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager";
import appStickersManager from "../../lib/appManagers/appStickersManager"; import appStickersManager from "../../lib/appManagers/appStickersManager";
@ -18,6 +20,7 @@ export default class StickersHelper extends AutocompleteHelper {
private scrollable: Scrollable; private scrollable: Scrollable;
private superStickerRenderer: SuperStickerRenderer; private superStickerRenderer: SuperStickerRenderer;
private lazyLoadQueue: LazyLoadQueue; private lazyLoadQueue: LazyLoadQueue;
private onChangeScreen: () => void;
constructor(appendTo: HTMLElement, controller: AutocompleteHelperController) { constructor(appendTo: HTMLElement, controller: AutocompleteHelperController) {
super({ super({
@ -37,6 +40,13 @@ export default class StickersHelper extends AutocompleteHelper {
this.scrollable.container.scrollTop = 0; this.scrollable.container.scrollTop = 0;
}, 0); }, 0);
}); });
this.addEventListener('hidden', () => {
if(this.onChangeScreen) {
mediaSizes.removeEventListener('changeScreen', this.onChangeScreen);
this.onChangeScreen = undefined;
}
});
} }
public checkEmoticon(emoticon: string) { public checkEmoticon(emoticon: string) {
@ -79,6 +89,15 @@ export default class StickersHelper extends AutocompleteHelper {
this.list.replaceWith(container); this.list.replaceWith(container);
this.list = container; this.list = container;
if(!this.onChangeScreen) {
this.onChangeScreen = () => {
this.list.style.width = this.list.childElementCount * mediaSizes.active.esgSticker.width + 'px';
};
mediaSizes.addEventListener('changeScreen', this.onChangeScreen);
}
this.onChangeScreen();
this.toggle(!stickers.length); this.toggle(!stickers.length);
this.scrollable.scrollTop = 0; this.scrollable.scrollTop = 0;
}); });

2
src/components/emoticonsDropdown/tabs/emoji.ts

@ -29,7 +29,7 @@ export function appendEmoji(emoji: string, container: HTMLElement, prepend = fal
spanEmoji.classList.add('super-emoji'); spanEmoji.classList.add('super-emoji');
let kek: string; let kek: string;
if(unify) { if(unify && !RichTextProcessor.emojiSupported) {
kek = RichTextProcessor.wrapSingleEmoji(emoji); kek = RichTextProcessor.wrapSingleEmoji(emoji);
} else { } else {
emoji = RichTextProcessor.fixEmoji(emoji); emoji = RichTextProcessor.fixEmoji(emoji);

12
src/lib/appManagers/appProfileManager.ts

@ -19,6 +19,7 @@ import { ChannelParticipantsFilter, ChannelsChannelParticipants, Chat, ChatFull,
import apiManager from '../mtproto/mtprotoworker'; import apiManager from '../mtproto/mtprotoworker';
import { RichTextProcessor } from "../richtextprocessor"; import { RichTextProcessor } from "../richtextprocessor";
import rootScope from "../rootScope"; import rootScope from "../rootScope";
import SearchIndex from "../searchIndex";
import apiUpdatesManager from "./apiUpdatesManager"; import apiUpdatesManager from "./apiUpdatesManager";
import appChatsManager from "./appChatsManager"; import appChatsManager from "./appChatsManager";
import appDownloadManager from "./appDownloadManager"; import appDownloadManager from "./appDownloadManager";
@ -387,6 +388,17 @@ export class AppProfileManager {
}) as any; }) as any;
} }
public getMentions(chatId: number, query: string): Promise<number[]> {
return (this.getChatFull(chatId) as Promise<ChatFull.chatFull>).then(chatFull => {
const index = new SearchIndex<number>(true, true);
(chatFull.participants as ChatParticipants.chatParticipants).participants.forEach(participant => {
index.indexObject(participant.user_id, appUsersManager.getUserSearchText(participant.user_id));
});
return Array.from(index.search(query));
});
}
public invalidateChannelParticipants(id: number) { public invalidateChannelParticipants(id: number) {
delete this.chatsFull[id]; delete this.chatsFull[id];
delete this.fullPromises[-id]; delete this.fullPromises[-id];

7
src/lib/richtextprocessor.ts

@ -413,7 +413,7 @@ namespace RichTextProcessor {
fromBot: boolean, fromBot: boolean,
noTextFormat: true, noTextFormat: true,
passEntities: Partial<{ passEntities: Partial<{
[_ in MessageEntity['_']]: true [_ in MessageEntity['_']]: boolean
}>, }>,
contextHashtag?: string contextHashtag?: string
@ -513,7 +513,8 @@ namespace RichTextProcessor {
} }
case 'messageEntityBotCommand': { case 'messageEntityBotCommand': {
if(!(options.noLinks || options.noCommands || contextExternal) && !entity.unsafe) { // if(!(options.noLinks || options.noCommands || contextExternal)/* && !entity.unsafe */) {
if(!options.noLinks && passEntities[entity._]) {
const entityText = text.substr(entity.offset, entity.length); const entityText = text.substr(entity.offset, entity.length);
let command = entityText.substr(1); let command = entityText.substr(1);
let bot: string | boolean; let bot: string | boolean;
@ -525,7 +526,7 @@ namespace RichTextProcessor {
bot = options.fromBot; bot = options.fromBot;
} }
insertPart(entity, `<a href="${encodeEntities('tg://bot_command?command=' + encodeURIComponent(command) + (bot ? '&bot=' + encodeURIComponent(bot) : ''))}" onclick="execBotCommand(this)">`, `</a>`); insertPart(entity, `<a href="${encodeEntities('tg://bot_command?command=' + encodeURIComponent(command) + (bot ? '&bot=' + encodeURIComponent(bot) : ''))}" ${contextExternal ? '' : 'onclick="execBotCommand(this)"'}>`, `</a>`);
} }
break; break;

6
src/lib/searchIndex.ts

@ -24,7 +24,7 @@ export default class SearchIndex<SearchWhat> {
} */ } */
if(searchText.trim() && this.cleanText) { if(searchText.trim() && this.cleanText) {
searchText = cleanSearchText(searchText); searchText = cleanSearchText(searchText, this.latinize);
} }
if(!searchText) { if(!searchText) {
@ -59,9 +59,11 @@ export default class SearchIndex<SearchWhat> {
const newFoundObjs: Array<{fullText: string, what: SearchWhat}> = []; const newFoundObjs: Array<{fullText: string, what: SearchWhat}> = [];
const queryWords = query.split(' '); const queryWords = query.split(' ');
const queryWordsLength = queryWords.length;
fullTexts.forEach((fullText, what) => { fullTexts.forEach((fullText, what) => {
let found = true; let found = true;
for(const word of queryWords) { // * verify that all words are found for(let i = 0; i < queryWordsLength; ++i) { // * verify that all words are found
const word = queryWords[i];
const idx = fullText.indexOf(word); const idx = fullText.indexOf(word);
if(idx === -1 || (idx !== 0 && fullText[idx - 1] !== ' ')) { // * search only from word beginning if(idx === -1 || (idx !== 0 && fullText[idx - 1] !== ' ')) { // * search only from word beginning
found = false; found = false;

5
src/scss/partials/_autocompleteHelper.scss

@ -11,6 +11,9 @@
overflow: hidden; overflow: hidden;
padding: 0 !important; padding: 0 !important;
border-radius: var(--border-radius) !important; border-radius: var(--border-radius) !important;
max-width: 100%;
left: 0;
width: auto !important;
&:not(.is-visible) { &:not(.is-visible) {
display: none; display: none;
@ -21,7 +24,7 @@
animation: fade-out-opacity .2s ease-in-out forwards; animation: fade-out-opacity .2s ease-in-out forwards;
&:not(.backwards) { &:not(.backwards) {
animation: fade-in-opacity .2s ease-in-out forwards; animation-name: fade-in-opacity;
} }
} }
} }

44
src/scss/partials/_autocompletePeerHelper.scss

@ -0,0 +1,44 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
.autocomplete-peer-helper {
.scrollable {
position: relative;
max-height: 232px;
}
&-list {
padding: .5rem 0;
&-element {
height: 3.125rem;
display: flex;
// padding: 0 .75rem;
padding: 0 2.125rem 0px 0.75rem;
align-items: center;
cursor: pointer;
user-select: none;
position: relative;
line-height: var(--line-height);
@include hover();
&-name {
margin-left: .875rem;
font-weight: 500;
}
&-description {
margin-left: .5625rem;
color: var(--secondary-text-color);
}
}
}
}
/* .mentions-helper {
width: 320px !important;
} */

33
src/scss/partials/_chatCommandsHelper.scss

@ -1,33 +0,0 @@
.commands-helper {
left: 0;
//width: 320px !important;
.scrollable {
position: relative;
max-height: 232px;
}
&-commands {
padding: .5rem 0;
}
&-command {
height: 3.125rem;
display: flex;
padding: 0 .75rem;
align-items: center;
cursor: pointer;
@include hover();
&-name {
margin-left: .875rem;
font-weight: 500;
}
&-description {
margin-left: .5625rem;
color: var(--secondary-text-color);
}
}
}

2
src/scss/partials/_chatEmojiHelper.scss

@ -1,9 +1,11 @@
.emoji-helper { .emoji-helper {
height: 50px; height: 50px;
padding: .25rem 0 !important; padding: .25rem 0 !important;
//transition: width .15s ease-in-out;
> .scrollable { > .scrollable {
position: relative; position: relative;
width: auto;
} }
.super-emojis { .super-emojis {

11
src/scss/partials/_chatStickersHelper.scss

@ -13,8 +13,9 @@
} }
&-stickers { &-stickers {
display: flex; max-width: 100%;
flex-wrap: wrap; /* display: flex !important;
flex-wrap: wrap; */
border-radius: var(--border-radius); border-radius: var(--border-radius);
} }
@ -23,4 +24,10 @@
background: none; background: none;
} }
} }
/* .super-sticker {
width: var(--esg-sticker-size);
height: var(--esg-sticker-size);
padding: 0;
} */
} }

2
src/scss/style.scss

@ -226,6 +226,7 @@ html.night {
@import "partials/button"; @import "partials/button";
@import "partials/animatedIcon"; @import "partials/animatedIcon";
@import "partials/autocompleteHelper"; @import "partials/autocompleteHelper";
@import "partials/autocompletePeerHelper";
@import "partials/badge"; @import "partials/badge";
@import "partials/checkbox"; @import "partials/checkbox";
@import "partials/chatlist"; @import "partials/chatlist";
@ -236,7 +237,6 @@ html.night {
@import "partials/chatMarkupTooltip"; @import "partials/chatMarkupTooltip";
@import "partials/chatStickersHelper"; @import "partials/chatStickersHelper";
@import "partials/chatEmojiHelper"; @import "partials/chatEmojiHelper";
@import "partials/chatCommandsHelper";
@import "partials/chatSearch"; @import "partials/chatSearch";
@import "partials/chatDrop"; @import "partials/chatDrop";
@import "partials/crop"; @import "partials/crop";

Loading…
Cancel
Save