Chat input autocomplete helper
Fix iOS 13 blank page
This commit is contained in:
parent
f156557b47
commit
5505ef5b8b
@ -17,7 +17,6 @@ import appUsersManager from "../lib/appManagers/appUsersManager";
|
||||
import { logger } from "../lib/logger";
|
||||
import RichTextProcessor from "../lib/richtextprocessor";
|
||||
import rootScope from "../lib/rootScope";
|
||||
import searchIndexManager from "../lib/searchIndexManager";
|
||||
import AppMediaViewer from "./appMediaViewer";
|
||||
import { SearchGroup, SearchGroupType } from "./appSearch";
|
||||
import { horizontalMenu } from "./horizontalMenu";
|
||||
@ -39,6 +38,7 @@ import appSidebarRight from "./sidebarRight";
|
||||
import mediaSizes from "../helpers/mediaSizes";
|
||||
import appImManager from "../lib/appManagers/appImManager";
|
||||
import positionElementByIndex from "../helpers/dom/positionElementByIndex";
|
||||
import cleanSearchText from "../helpers/cleanSearchText";
|
||||
|
||||
//const testScroll = false;
|
||||
|
||||
@ -738,7 +738,7 @@ export default class AppSearchSuper {
|
||||
});
|
||||
|
||||
if(showMembersCount && (peer.participants_count || peer.participants)) {
|
||||
const regExp = new RegExp(`(${escapeRegExp(query)}|${escapeRegExp(searchIndexManager.cleanSearchText(query))})`, 'gi');
|
||||
const regExp = new RegExp(`(${escapeRegExp(query)}|${escapeRegExp(cleanSearchText(query))})`, 'gi');
|
||||
dom.titleSpan.innerHTML = dom.titleSpan.innerHTML.replace(regExp, '<i>$1</i>');
|
||||
dom.lastMessageSpan.append(appChatsManager.getChatMembersString(-peerId));
|
||||
} else if(peerId === rootScope.myId) {
|
||||
|
@ -19,6 +19,7 @@ import type { AppUsersManager } from "../../lib/appManagers/appUsersManager";
|
||||
import type { AppWebPagesManager } from "../../lib/appManagers/appWebPagesManager";
|
||||
import type { ApiManagerProxy } from "../../lib/mtproto/mtprotoworker";
|
||||
import type { AppDraftsManager } from "../../lib/appManagers/appDraftsManager";
|
||||
import type { AppEmojiManager } from "../../lib/appManagers/appEmojiManager";
|
||||
import type { ServerTimeManager } from "../../lib/mtproto/serverTimeManager";
|
||||
import type sessionStorage from '../../lib/sessionStorage';
|
||||
import EventListenerBase from "../../helpers/eventListenerBase";
|
||||
@ -64,7 +65,25 @@ export default class Chat extends EventListenerBase<{
|
||||
|
||||
public noAutoDownloadMedia: boolean;
|
||||
|
||||
constructor(public appImManager: AppImManager, public appChatsManager: AppChatsManager, public appDocsManager: AppDocsManager, public appInlineBotsManager: AppInlineBotsManager, public appMessagesManager: AppMessagesManager, public appPeersManager: AppPeersManager, public appPhotosManager: AppPhotosManager, public appProfileManager: AppProfileManager, public appStickersManager: AppStickersManager, public appUsersManager: AppUsersManager, public appWebPagesManager: AppWebPagesManager, public appPollsManager: AppPollsManager, public apiManager: ApiManagerProxy, public appDraftsManager: AppDraftsManager, public serverTimeManager: ServerTimeManager, public storage: typeof sessionStorage, public appNotificationsManager: AppNotificationsManager) {
|
||||
constructor(public appImManager: AppImManager,
|
||||
public appChatsManager: AppChatsManager,
|
||||
public appDocsManager: AppDocsManager,
|
||||
public appInlineBotsManager: AppInlineBotsManager,
|
||||
public appMessagesManager: AppMessagesManager,
|
||||
public appPeersManager: AppPeersManager,
|
||||
public appPhotosManager: AppPhotosManager,
|
||||
public appProfileManager: AppProfileManager,
|
||||
public appStickersManager: AppStickersManager,
|
||||
public appUsersManager: AppUsersManager,
|
||||
public appWebPagesManager: AppWebPagesManager,
|
||||
public appPollsManager: AppPollsManager,
|
||||
public apiManager: ApiManagerProxy,
|
||||
public appDraftsManager: AppDraftsManager,
|
||||
public serverTimeManager: ServerTimeManager,
|
||||
public storage: typeof sessionStorage,
|
||||
public appNotificationsManager: AppNotificationsManager,
|
||||
public appEmojiManager: AppEmojiManager
|
||||
) {
|
||||
super();
|
||||
|
||||
this.container = document.createElement('div');
|
||||
@ -150,7 +169,7 @@ export default class Chat extends EventListenerBase<{
|
||||
|
||||
this.topbar = new ChatTopbar(this, appSidebarRight, this.appMessagesManager, this.appPeersManager, this.appChatsManager, this.appNotificationsManager);
|
||||
this.bubbles = new ChatBubbles(this, this.appMessagesManager, this.appStickersManager, this.appUsersManager, this.appInlineBotsManager, this.appPhotosManager, this.appDocsManager, this.appPeersManager, this.appChatsManager, this.storage);
|
||||
this.input = new ChatInput(this, this.appMessagesManager, this.appDocsManager, this.appChatsManager, this.appPeersManager, this.appWebPagesManager, this.appImManager, this.appDraftsManager, this.serverTimeManager, this.appNotificationsManager);
|
||||
this.input = new ChatInput(this, this.appMessagesManager, this.appDocsManager, this.appChatsManager, this.appPeersManager, this.appWebPagesManager, this.appImManager, this.appDraftsManager, this.serverTimeManager, this.appNotificationsManager, this.appEmojiManager);
|
||||
this.selection = new ChatSelection(this, this.bubbles, this.input, this.appMessagesManager);
|
||||
this.contextMenu = new ChatContextMenu(this.bubbles.bubblesContainer, this, this.appMessagesManager, this.appChatsManager, this.appPeersManager, this.appPollsManager);
|
||||
|
||||
|
58
src/components/chat/emojiHelper.ts
Normal file
58
src/components/chat/emojiHelper.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import type ChatInput from "./input";
|
||||
import attachListNavigation from "../../helpers/dom/attachlistNavigation";
|
||||
import { appendEmoji, getEmojiFromElement } from "../emoticonsDropdown/tabs/emoji";
|
||||
import { ScrollableX } from "../scrollable";
|
||||
import AutocompleteHelper from "./autocompleteHelper";
|
||||
|
||||
export default class EmojiHelper extends AutocompleteHelper {
|
||||
private emojisContainer: HTMLDivElement;
|
||||
private scrollable: ScrollableX;
|
||||
|
||||
constructor(appendTo: HTMLElement, private chatInput: ChatInput) {
|
||||
super(appendTo);
|
||||
|
||||
this.container.classList.add('emoji-helper');
|
||||
|
||||
this.addEventListener('visible', () => {
|
||||
const list = this.emojisContainer;
|
||||
const {detach} = attachListNavigation({
|
||||
list,
|
||||
type: 'x',
|
||||
onSelect: (target) => {
|
||||
this.chatInput.onEmojiSelected(getEmojiFromElement(target as any), true);
|
||||
},
|
||||
once: true
|
||||
});
|
||||
|
||||
this.addEventListener('hidden', () => {
|
||||
list.innerHTML = '';
|
||||
detach();
|
||||
}, true);
|
||||
});
|
||||
}
|
||||
|
||||
private init() {
|
||||
this.emojisContainer = document.createElement('div');
|
||||
this.emojisContainer.classList.add('emoji-helper-emojis', 'super-emojis');
|
||||
|
||||
this.container.append(this.emojisContainer);
|
||||
|
||||
this.scrollable = new ScrollableX(this.container);
|
||||
}
|
||||
|
||||
public renderEmojis(emojis: string[]) {
|
||||
if(this.init) {
|
||||
this.init();
|
||||
this.init = null;
|
||||
}
|
||||
|
||||
if(emojis.length) {
|
||||
this.emojisContainer.innerHTML = '';
|
||||
emojis.forEach(emoji => {
|
||||
appendEmoji(emoji, this.emojisContainer);
|
||||
});
|
||||
}
|
||||
|
||||
this.toggle(!emojis.length);
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ import type { AppPeersManager } from '../../lib/appManagers/appPeersManager';
|
||||
import type { AppWebPagesManager } from "../../lib/appManagers/appWebPagesManager";
|
||||
import type { AppImManager } from '../../lib/appManagers/appImManager';
|
||||
import type { AppDraftsManager, MyDraftMessage } from '../../lib/appManagers/appDraftsManager';
|
||||
import type { AppEmojiManager } from '../../lib/appManagers/appEmojiManager';
|
||||
import type { ServerTimeManager } from '../../lib/mtproto/serverTimeManager';
|
||||
import type Chat from './chat';
|
||||
import Recorder from '../../../public/recorder.min';
|
||||
@ -57,50 +58,52 @@ import isSendShortcutPressed from '../../helpers/dom/isSendShortcutPressed';
|
||||
import placeCaretAtEnd from '../../helpers/dom/placeCaretAtEnd';
|
||||
import { MarkdownType, markdownTags } from '../../helpers/dom/getRichElementValue';
|
||||
import getRichValueWithCaret from '../../helpers/dom/getRichValueWithCaret';
|
||||
import searchIndexManager from '../../lib/searchIndexManager';
|
||||
import cleanSearchText from '../../helpers/cleanSearchText';
|
||||
import EmojiHelper from './emojiHelper';
|
||||
|
||||
const RECORD_MIN_TIME = 500;
|
||||
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';
|
||||
|
||||
type ChatInputHelperType = 'edit' | 'webpage' | 'forward' | 'reply';
|
||||
|
||||
let selId = 0;
|
||||
|
||||
export default class ChatInput {
|
||||
public static AUTO_COMPLETE_REG_EXP = /(\s|^)(:|@|\/)([\S]*)$/;
|
||||
public pageEl = document.getElementById('page-chats') as HTMLDivElement;
|
||||
public static AUTO_COMPLETE_REG_EXP = /(\s|^)((?::|.)(?!.*:).*|(?:(?:@|\/)(?:[\S]*)))$/;
|
||||
public messageInput: HTMLElement;
|
||||
public messageInputField: InputField;
|
||||
public fileInput: HTMLInputElement;
|
||||
public inputMessageContainer: HTMLDivElement;
|
||||
public btnSend = document.getElementById('btn-send') as HTMLButtonElement;
|
||||
public btnCancelRecord: HTMLButtonElement;
|
||||
public lastUrl = '';
|
||||
public lastTimeType = 0;
|
||||
private fileInput: HTMLInputElement;
|
||||
private inputMessageContainer: HTMLDivElement;
|
||||
private btnSend: HTMLButtonElement;
|
||||
private btnCancelRecord: HTMLButtonElement;
|
||||
private lastUrl = '';
|
||||
private lastTimeType = 0;
|
||||
|
||||
public chatInput: HTMLElement;
|
||||
public inputContainer: HTMLElement;
|
||||
private inputContainer: HTMLElement;
|
||||
public rowsWrapper: HTMLDivElement;
|
||||
private newMessageWrapper: HTMLDivElement;
|
||||
private btnToggleEmoticons: HTMLButtonElement;
|
||||
public btnSendContainer: HTMLDivElement;
|
||||
private btnSendContainer: HTMLDivElement;
|
||||
|
||||
public attachMenu: HTMLButtonElement;
|
||||
private attachMenu: HTMLButtonElement;
|
||||
private attachMenuButtons: (ButtonMenuItemOptions & {verify: (peerId: number) => boolean})[];
|
||||
|
||||
public sendMenu: SendMenu;
|
||||
private sendMenu: SendMenu;
|
||||
|
||||
public replyElements: {
|
||||
private replyElements: {
|
||||
container?: HTMLElement,
|
||||
cancelBtn?: HTMLButtonElement,
|
||||
titleEl?: HTMLElement,
|
||||
subtitleEl?: HTMLElement
|
||||
} = {};
|
||||
|
||||
public willSendWebPage: any = null;
|
||||
public forwardingMids: number[] = [];
|
||||
public forwardingFromPeerId: number = 0;
|
||||
private willSendWebPage: any = null;
|
||||
private forwardingMids: number[] = [];
|
||||
private forwardingFromPeerId: number = 0;
|
||||
public replyToMsgId: number;
|
||||
public editMsgId: number;
|
||||
public noWebPage: true;
|
||||
private noWebPage: true;
|
||||
public scheduleDate: number;
|
||||
public sendSilent: true;
|
||||
|
||||
@ -127,23 +130,35 @@ export default class ChatInput {
|
||||
readonly executedHistory: string[] = [];
|
||||
private canUndoFromHTML = '';
|
||||
|
||||
public stickersHelper: StickersHelper;
|
||||
public listenerSetter: ListenerSetter;
|
||||
private emojiHelper: EmojiHelper;
|
||||
private stickersHelper: StickersHelper;
|
||||
private listenerSetter: ListenerSetter;
|
||||
|
||||
public pinnedControlBtn: HTMLButtonElement;
|
||||
private pinnedControlBtn: HTMLButtonElement;
|
||||
|
||||
public goDownBtn: HTMLButtonElement;
|
||||
public goDownUnreadBadge: HTMLElement;
|
||||
public btnScheduled: HTMLButtonElement;
|
||||
private goDownBtn: HTMLButtonElement;
|
||||
private goDownUnreadBadge: HTMLElement;
|
||||
private btnScheduled: HTMLButtonElement;
|
||||
|
||||
public saveDraftDebounced: () => void;
|
||||
private saveDraftDebounced: () => void;
|
||||
|
||||
public fakeRowsWrapper: HTMLDivElement;
|
||||
private fakeRowsWrapper: HTMLDivElement;
|
||||
private fakePinnedControlBtn: HTMLElement;
|
||||
|
||||
public previousQuery: string;
|
||||
private previousQuery: string;
|
||||
|
||||
constructor(private chat: Chat, private appMessagesManager: AppMessagesManager, private appDocsManager: AppDocsManager, private appChatsManager: AppChatsManager, private appPeersManager: AppPeersManager, private appWebPagesManager: AppWebPagesManager, private appImManager: AppImManager, private appDraftsManager: AppDraftsManager, private serverTimeManager: ServerTimeManager, private appNotificationsManager: AppNotificationsManager) {
|
||||
constructor(private chat: Chat,
|
||||
private appMessagesManager: AppMessagesManager,
|
||||
private appDocsManager: AppDocsManager,
|
||||
private appChatsManager: AppChatsManager,
|
||||
private appPeersManager: AppPeersManager,
|
||||
private appWebPagesManager: AppWebPagesManager,
|
||||
private appImManager: AppImManager,
|
||||
private appDraftsManager: AppDraftsManager,
|
||||
private serverTimeManager: ServerTimeManager,
|
||||
private appNotificationsManager: AppNotificationsManager,
|
||||
private appEmojiManager: AppEmojiManager
|
||||
) {
|
||||
this.listenerSetter = new ListenerSetter();
|
||||
}
|
||||
|
||||
@ -348,6 +363,7 @@ export default class ChatInput {
|
||||
this.newMessageWrapper.append(...[this.btnToggleEmoticons, this.inputMessageContainer, this.btnScheduled, this.attachMenu, this.recordTimeEl, this.fileInput].filter(Boolean));
|
||||
|
||||
this.rowsWrapper.append(this.replyElements.container);
|
||||
this.emojiHelper = new EmojiHelper(this.rowsWrapper, this);
|
||||
this.stickersHelper = new StickersHelper(this.rowsWrapper);
|
||||
this.rowsWrapper.append(this.newMessageWrapper);
|
||||
|
||||
@ -818,6 +834,9 @@ export default class ChatInput {
|
||||
}
|
||||
}); */
|
||||
this.listenerSetter.add(this.messageInput, 'input', this.onMessageInput);
|
||||
this.listenerSetter.add(this.messageInput, 'keyup', () => {
|
||||
this.checkAutocomplete();
|
||||
});
|
||||
|
||||
if(this.chat.type === 'chat' || this.chat.type === 'discussion') {
|
||||
this.listenerSetter.add(this.messageInput, 'focusin', () => {
|
||||
@ -1029,10 +1048,10 @@ export default class ChatInput {
|
||||
const {value: richValue, entities: markdownEntities, caretPos} = getRichValueWithCaret(this.messageInputField.input);
|
||||
|
||||
//const entities = RichTextProcessor.parseEntities(value);
|
||||
const value = RichTextProcessor.parseMarkdown(richValue, markdownEntities);
|
||||
const value = RichTextProcessor.parseMarkdown(richValue, markdownEntities, true);
|
||||
const entities = RichTextProcessor.mergeEntities(markdownEntities, RichTextProcessor.parseEntities(value));
|
||||
|
||||
this.chat.log('messageInput entities', richValue, value, markdownEntities, caretPos);
|
||||
//this.chat.log('messageInput entities', richValue, value, markdownEntities, caretPos);
|
||||
|
||||
if(this.stickersHelper &&
|
||||
rootScope.settings.stickers.suggest &&
|
||||
@ -1119,99 +1138,132 @@ export default class ChatInput {
|
||||
this.saveDraftDebounced();
|
||||
}
|
||||
|
||||
this.checkAutocomplete(richValue, caretPos);
|
||||
|
||||
this.updateSendBtn();
|
||||
};
|
||||
|
||||
private checkAutocomplete(value: string, markdownEntities: MessageEntity[], entities: MessageEntity[]) {
|
||||
const matches = value.match(ChatInput.AUTO_COMPLETE_REG_EXP);
|
||||
if(matches) {
|
||||
if(this.previousQuery == matches[0]) {
|
||||
return
|
||||
}
|
||||
this.previousQuery = matches[0]
|
||||
var query = searchIndexManager.cleanSearchText(matches[3])
|
||||
public onEmojiSelected = (emoji: string, autocomplete: boolean) => {
|
||||
if(autocomplete) {
|
||||
const {value: fullValue, caretPos} = getRichValueWithCaret(this.messageInput);
|
||||
const pos = caretPos >= 0 ? caretPos : fullValue.length;
|
||||
const suffix = fullValue.substr(pos);
|
||||
const prefix = fullValue.substr(0, pos);
|
||||
const matches = prefix.match(ChatInput.AUTO_COMPLETE_REG_EXP);
|
||||
console.log(matches);
|
||||
|
||||
/* if (matches[2] == '@') { // mentions
|
||||
if (this.mentions && this.mentions.index) {
|
||||
if (query.length) {
|
||||
var foundObject = SearchIndexManager.search(query, this.mentions.index)
|
||||
var foundUsers = []
|
||||
var user
|
||||
for (var i = 0, length = this.mentions.users.length; i < length; i++) {
|
||||
user = this.mentions.users[i]
|
||||
if (foundObject[user.id]) {
|
||||
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] == '/') { // commands
|
||||
if (this.commands && this.commands.index) {
|
||||
if (query.length) {
|
||||
var foundObject = SearchIndexManager.search(query, this.commands.index)
|
||||
var foundCommands = []
|
||||
var command
|
||||
for (var i = 0, length = this.commands.list.length; i < length; i++) {
|
||||
command = this.commands.list[i]
|
||||
if (foundObject[command.value]) {
|
||||
foundCommands.push(command)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var foundCommands = this.commands.list
|
||||
}
|
||||
if (foundCommands.length) {
|
||||
this.showCommandsSuggestions(foundCommands)
|
||||
} else {
|
||||
this.hideSuggestions()
|
||||
}
|
||||
} else {
|
||||
this.hideSuggestions()
|
||||
}
|
||||
} else *//* if(matches[2] === ':') { // emoji
|
||||
if(value.match(/^\s*:(.+):\s*$/)) {
|
||||
return;
|
||||
}
|
||||
|
||||
EmojiHelper.getPopularEmoji((function (popular) {
|
||||
if (query.length) {
|
||||
var found = EmojiHelper.searchEmojis(query)
|
||||
if (found.length) {
|
||||
var popularFound = [],
|
||||
code
|
||||
var pos
|
||||
for (var i = 0, len = popular.length; i < len; i++) {
|
||||
code = popular[i].code
|
||||
pos = found.indexOf(code)
|
||||
if (pos >= 0) {
|
||||
popularFound.push(code)
|
||||
found.splice(pos, 1)
|
||||
if (!found.length) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
this.showEmojiSuggestions(popularFound.concat(found))
|
||||
} else {
|
||||
this.hideSuggestions()
|
||||
}
|
||||
} else {
|
||||
this.showEmojiSuggestions(popular)
|
||||
}
|
||||
}).bind(this))
|
||||
const idx = matches.index + matches[1].length;
|
||||
|
||||
//const str =
|
||||
|
||||
/* var newValuePrefix
|
||||
if(matches && matches[0]) {
|
||||
newValuePrefix = prefix.substr(0, matches.index) + ':' + emoji[1] + ':'
|
||||
} else {
|
||||
newValuePrefix = prefix + ':' + emoji[1] + ':'
|
||||
}
|
||||
} else {
|
||||
delete this.previousQuery
|
||||
this.hideSuggestions() */
|
||||
|
||||
if(suffix.length) {
|
||||
const html = this.getRichHtml(newValuePrefix) + ' <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) + ' '
|
||||
this.richTextareaEl.html(html)
|
||||
setRichFocus(textarea)
|
||||
} */
|
||||
}
|
||||
};
|
||||
|
||||
private checkAutocomplete(value?: string, caretPos?: number) {
|
||||
return;
|
||||
|
||||
if(value === undefined) {
|
||||
const r = getRichValueWithCaret(this.messageInputField.input, false);
|
||||
value = r.value;
|
||||
caretPos = r.caretPos;
|
||||
}
|
||||
|
||||
if(caretPos === -1) {
|
||||
caretPos = value.length;
|
||||
}
|
||||
value = value.substr(0, caretPos);
|
||||
|
||||
const matches = value.match(ChatInput.AUTO_COMPLETE_REG_EXP);
|
||||
if(!matches) {
|
||||
delete this.previousQuery;
|
||||
//this.hideSuggestions();
|
||||
this.emojiHelper.toggle(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.previousQuery === matches[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.previousQuery = matches[0];
|
||||
//let query = cleanSearchText(matches[2]);
|
||||
//const firstChar = matches[2][0];
|
||||
|
||||
//console.log('autocomplete matches', matches);
|
||||
|
||||
/*if (matches[2] == '@') { // mentions
|
||||
if (this.mentions && this.mentions.index) {
|
||||
if (query.length) {
|
||||
var foundObject = SearchIndexManager.search(query, this.mentions.index)
|
||||
var foundUsers = []
|
||||
var user
|
||||
for (var i = 0, length = this.mentions.users.length; i < length; i++) {
|
||||
user = this.mentions.users[i]
|
||||
if (foundObject[user.id]) {
|
||||
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] == '/') { // commands
|
||||
if (this.commands && this.commands.index) {
|
||||
if (query.length) {
|
||||
var foundObject = SearchIndexManager.search(query, this.commands.index)
|
||||
var foundCommands = []
|
||||
var command
|
||||
for (var i = 0, length = this.commands.list.length; i < length; i++) {
|
||||
command = this.commands.list[i]
|
||||
if (foundObject[command.value]) {
|
||||
foundCommands.push(command)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var foundCommands = this.commands.list
|
||||
}
|
||||
if (foundCommands.length) {
|
||||
this.showCommandsSuggestions(foundCommands)
|
||||
} else {
|
||||
this.hideSuggestions()
|
||||
}
|
||||
} else {
|
||||
this.hideSuggestions()
|
||||
}
|
||||
} else *//* if(firstChar === ':') */ { // emoji
|
||||
if(value.match(/^\s*:(.+):\s*$/)) {
|
||||
this.emojiHelper.toggle(true);
|
||||
return;
|
||||
}
|
||||
|
||||
this.appEmojiManager.getBothEmojiKeywords().then(() => {
|
||||
const emojis = this.appEmojiManager.searchEmojis(matches[2]);
|
||||
this.emojiHelper.renderEmojis(emojis);
|
||||
//console.log(emojis);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,92 @@ import { putPreloader } from "../../misc";
|
||||
import Scrollable from "../../scrollable";
|
||||
import StickyIntersector from "../../stickyIntersector";
|
||||
|
||||
const loadedURLs: Set<string> = new Set();
|
||||
export function appendEmoji(emoji: string, container: HTMLElement, prepend = false/* , unified = false */) {
|
||||
//const emoji = details.unified;
|
||||
//const emoji = (details.unified as string).split('-')
|
||||
//.reduce((prev, curr) => prev + String.fromCodePoint(parseInt(curr, 16)), '');
|
||||
|
||||
const spanEmoji = document.createElement('span');
|
||||
spanEmoji.classList.add('super-emoji');
|
||||
|
||||
let kek: string;
|
||||
/* if(unified) {
|
||||
kek = RichTextProcessor.wrapRichText('_', {
|
||||
entities: [{
|
||||
_: 'messageEntityEmoji',
|
||||
offset: 0,
|
||||
length: emoji.split('-').length,
|
||||
unicode: emoji
|
||||
}]
|
||||
});
|
||||
} else { */
|
||||
kek = RichTextProcessor.wrapEmojiText(emoji);
|
||||
//}
|
||||
|
||||
/* if(!kek.includes('emoji')) {
|
||||
console.log(emoji, kek, spanEmoji, emoji.length, new TextEncoder().encode(emoji), emojiUnicode(emoji));
|
||||
return;
|
||||
} */
|
||||
|
||||
//console.log(kek);
|
||||
|
||||
spanEmoji.innerHTML = kek;
|
||||
|
||||
if(spanEmoji.children.length > 1) {
|
||||
const first = spanEmoji.firstElementChild;
|
||||
spanEmoji.innerHTML = '';
|
||||
spanEmoji.append(first);
|
||||
}
|
||||
|
||||
if(spanEmoji.firstElementChild && !RichTextProcessor.emojiSupported) {
|
||||
const image = spanEmoji.firstElementChild as HTMLImageElement;
|
||||
image.setAttribute('loading', 'lazy');
|
||||
|
||||
const url = image.src;
|
||||
if(!loadedURLs.has(url)) {
|
||||
const placeholder = document.createElement('span');
|
||||
placeholder.classList.add('emoji-placeholder');
|
||||
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
image.style.opacity = '0';
|
||||
placeholder.style.opacity = '1';
|
||||
}
|
||||
|
||||
image.addEventListener('load', () => {
|
||||
fastRaf(() => {
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
image.style.opacity = '';
|
||||
placeholder.style.opacity = '';
|
||||
}
|
||||
|
||||
spanEmoji.classList.remove('empty');
|
||||
|
||||
loadedURLs.add(url);
|
||||
});
|
||||
}, {once: true});
|
||||
|
||||
spanEmoji.append(placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
//spanEmoji = spanEmoji.firstElementChild as HTMLSpanElement;
|
||||
//spanEmoji.setAttribute('emoji', emoji);
|
||||
if(prepend) container.prepend(spanEmoji);
|
||||
else container.appendChild(spanEmoji);
|
||||
}
|
||||
|
||||
export function getEmojiFromElement(element: HTMLElement) {
|
||||
if(element.nodeType === 3) return element.nodeValue;
|
||||
if(element.tagName === 'SPAN' && !element.classList.contains('emoji')) {
|
||||
element = element.firstElementChild as HTMLElement;
|
||||
}
|
||||
|
||||
return element.getAttribute('alt') || element.innerText;
|
||||
}
|
||||
|
||||
export default class EmojiTab implements EmoticonsTab {
|
||||
public content: HTMLElement;
|
||||
private content: HTMLElement;
|
||||
|
||||
private recent: string[] = [];
|
||||
private recentItemsDiv: HTMLElement;
|
||||
@ -26,8 +110,6 @@ export default class EmojiTab implements EmoticonsTab {
|
||||
private scroll: Scrollable;
|
||||
private stickyIntersector: StickyIntersector;
|
||||
|
||||
private loadedURLs: Set<string> = new Set();
|
||||
|
||||
init() {
|
||||
this.content = document.getElementById('content-emoji') as HTMLDivElement;
|
||||
|
||||
@ -84,7 +166,7 @@ export default class EmojiTab implements EmoticonsTab {
|
||||
titleDiv.append(i18n(category));
|
||||
|
||||
const itemsDiv = document.createElement('div');
|
||||
itemsDiv.classList.add('category-items');
|
||||
itemsDiv.classList.add('super-emojis');
|
||||
|
||||
div.append(titleDiv, itemsDiv);
|
||||
|
||||
@ -95,7 +177,7 @@ export default class EmojiTab implements EmoticonsTab {
|
||||
|
||||
emoji = emoji.split('-').reduce((prev, curr) => prev + String.fromCodePoint(parseInt(curr, 16)), '');
|
||||
|
||||
this.appendEmoji(emoji/* .replace(/[\ufe0f\u2640\u2642\u2695]/g, '') */, itemsDiv, false/* , false */);
|
||||
appendEmoji(emoji/* .replace(/[\ufe0f\u2640\u2642\u2695]/g, '') */, itemsDiv, false/* , false */);
|
||||
|
||||
/* if(category === 'Smileys & Emotion') {
|
||||
console.log('appended emoji', emoji, itemsDiv.children[itemsDiv.childElementCount - 1].innerHTML, emojiUnicode(emoji));
|
||||
@ -125,9 +207,9 @@ export default class EmojiTab implements EmoticonsTab {
|
||||
]).then(() => {
|
||||
preloader.remove();
|
||||
|
||||
this.recentItemsDiv = divs['Emoji.Recent'].querySelector('.category-items');
|
||||
this.recentItemsDiv = divs['Emoji.Recent'].querySelector('.super-emojis');
|
||||
for(const emoji of this.recent) {
|
||||
this.appendEmoji(emoji, this.recentItemsDiv);
|
||||
appendEmoji(emoji, this.recentItemsDiv);
|
||||
}
|
||||
|
||||
this.recentItemsDiv.parentElement.classList.toggle('hide', !this.recent.length);
|
||||
@ -151,95 +233,12 @@ export default class EmojiTab implements EmoticonsTab {
|
||||
this.init = null;
|
||||
}
|
||||
|
||||
private appendEmoji(emoji: string, container: HTMLElement, prepend = false/* , unified = false */) {
|
||||
//const emoji = details.unified;
|
||||
//const emoji = (details.unified as string).split('-')
|
||||
//.reduce((prev, curr) => prev + String.fromCodePoint(parseInt(curr, 16)), '');
|
||||
|
||||
const spanEmoji = document.createElement('span');
|
||||
spanEmoji.classList.add('category-item');
|
||||
|
||||
let kek: string;
|
||||
/* if(unified) {
|
||||
kek = RichTextProcessor.wrapRichText('_', {
|
||||
entities: [{
|
||||
_: 'messageEntityEmoji',
|
||||
offset: 0,
|
||||
length: emoji.split('-').length,
|
||||
unicode: emoji
|
||||
}]
|
||||
});
|
||||
} else { */
|
||||
kek = RichTextProcessor.wrapEmojiText(emoji);
|
||||
//}
|
||||
|
||||
/* if(!kek.includes('emoji')) {
|
||||
console.log(emoji, kek, spanEmoji, emoji.length, new TextEncoder().encode(emoji), emojiUnicode(emoji));
|
||||
return;
|
||||
} */
|
||||
|
||||
//console.log(kek);
|
||||
|
||||
spanEmoji.innerHTML = kek;
|
||||
|
||||
if(spanEmoji.children.length > 1) {
|
||||
const first = spanEmoji.firstElementChild;
|
||||
spanEmoji.innerHTML = '';
|
||||
spanEmoji.append(first);
|
||||
}
|
||||
|
||||
if(spanEmoji.firstElementChild && !RichTextProcessor.emojiSupported) {
|
||||
const image = spanEmoji.firstElementChild as HTMLImageElement;
|
||||
image.setAttribute('loading', 'lazy');
|
||||
|
||||
const url = image.src;
|
||||
if(!this.loadedURLs.has(url)) {
|
||||
const placeholder = document.createElement('span');
|
||||
placeholder.classList.add('emoji-placeholder');
|
||||
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
image.style.opacity = '0';
|
||||
placeholder.style.opacity = '1';
|
||||
}
|
||||
|
||||
image.addEventListener('load', () => {
|
||||
fastRaf(() => {
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
image.style.opacity = '';
|
||||
placeholder.style.opacity = '';
|
||||
}
|
||||
|
||||
spanEmoji.classList.remove('empty');
|
||||
|
||||
this.loadedURLs.add(url);
|
||||
});
|
||||
}, {once: true});
|
||||
|
||||
spanEmoji.append(placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
//spanEmoji = spanEmoji.firstElementChild as HTMLSpanElement;
|
||||
//spanEmoji.setAttribute('emoji', emoji);
|
||||
if(prepend) container.prepend(spanEmoji);
|
||||
else container.appendChild(spanEmoji);
|
||||
}
|
||||
|
||||
private getEmojiFromElement(element: HTMLElement) {
|
||||
if(element.nodeType === 3) return element.nodeValue;
|
||||
if(element.tagName === 'SPAN' && !element.classList.contains('emoji')) {
|
||||
element = element.firstElementChild as HTMLElement;
|
||||
}
|
||||
|
||||
return element.getAttribute('alt') || element.innerText;
|
||||
}
|
||||
|
||||
onContentClick = (e: MouseEvent) => {
|
||||
let target = e.target as HTMLElement;
|
||||
//if(target.tagName !== 'SPAN') return;
|
||||
|
||||
if(target.tagName === 'SPAN' && !target.classList.contains('emoji')) {
|
||||
target = findUpClassName(target, 'category-item');
|
||||
target = findUpClassName(target, 'super-emoji');
|
||||
if(!target) {
|
||||
return;
|
||||
}
|
||||
@ -253,15 +252,15 @@ export default class EmojiTab implements EmoticonsTab {
|
||||
target.outerHTML;
|
||||
|
||||
// Recent
|
||||
const emoji = this.getEmojiFromElement(target);
|
||||
const emoji = getEmojiFromElement(target);
|
||||
(Array.from(this.recentItemsDiv.children) as HTMLElement[]).forEach((el, idx) => {
|
||||
const _emoji = this.getEmojiFromElement(el);
|
||||
const _emoji = getEmojiFromElement(el);
|
||||
if(emoji === _emoji) {
|
||||
el.remove();
|
||||
}
|
||||
});
|
||||
const scrollHeight = this.recentItemsDiv.scrollHeight;
|
||||
this.appendEmoji(emoji, this.recentItemsDiv, true);
|
||||
//const scrollHeight = this.recentItemsDiv.scrollHeight;
|
||||
appendEmoji(emoji, this.recentItemsDiv, true);
|
||||
|
||||
this.recent.findAndSplice(e => e === emoji);
|
||||
this.recent.unshift(emoji);
|
||||
|
@ -12,7 +12,7 @@ import apiManager from "../../../lib/mtproto/mtprotoworker";
|
||||
import appDocsManager, {MyDocument} from "../../../lib/appManagers/appDocsManager";
|
||||
|
||||
export default class GifsTab implements EmoticonsTab {
|
||||
public content: HTMLElement;
|
||||
private content: HTMLElement;
|
||||
|
||||
init() {
|
||||
this.content = document.getElementById('content-gifs');
|
||||
@ -45,4 +45,4 @@ export default class GifsTab implements EmoticonsTab {
|
||||
onClose() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,8 @@ import StickyIntersector from "../../stickyIntersector";
|
||||
import { wrapSticker } from "../../wrappers";
|
||||
|
||||
export class SuperStickerRenderer {
|
||||
lazyLoadQueue: LazyLoadQueueRepeat;
|
||||
animatedDivs: Set<HTMLDivElement> = new Set();
|
||||
public lazyLoadQueue: LazyLoadQueueRepeat;
|
||||
private animatedDivs: Set<HTMLDivElement> = new Set();
|
||||
|
||||
constructor(private regularLazyLoadQueue: LazyLoadQueue, private group: string) {
|
||||
this.lazyLoadQueue = new LazyLoadQueueRepeat(undefined, (target, visible) => {
|
||||
@ -35,7 +35,7 @@ export class SuperStickerRenderer {
|
||||
});
|
||||
}
|
||||
|
||||
renderSticker(doc: MyDocument, div?: HTMLDivElement, loadPromises?: Promise<any>[]) {
|
||||
public renderSticker(doc: MyDocument, div?: HTMLDivElement, loadPromises?: Promise<any>[]) {
|
||||
if(!div) {
|
||||
div = document.createElement('div');
|
||||
div.classList.add('grid-item', 'super-sticker');
|
||||
@ -63,7 +63,7 @@ export class SuperStickerRenderer {
|
||||
return div;
|
||||
}
|
||||
|
||||
checkAnimationContainer = (div: HTMLElement, visible: boolean) => {
|
||||
private checkAnimationContainer = (div: HTMLElement, visible: boolean) => {
|
||||
//console.error('checkAnimationContainer', div, visible);
|
||||
const players = animationIntersector.getAnimations(div);
|
||||
players.forEach(player => {
|
||||
@ -75,7 +75,7 @@ export class SuperStickerRenderer {
|
||||
});
|
||||
};
|
||||
|
||||
processVisibleDiv = (div: HTMLElement) => {
|
||||
private processVisibleDiv = (div: HTMLElement) => {
|
||||
const docId = div.dataset.docId;
|
||||
const doc = appDocsManager.getDoc(docId);
|
||||
|
||||
@ -107,7 +107,7 @@ export class SuperStickerRenderer {
|
||||
return promise;
|
||||
};
|
||||
|
||||
processInvisibleDiv = (div: HTMLElement) => {
|
||||
public processInvisibleDiv = (div: HTMLElement) => {
|
||||
const docId = div.dataset.docId;
|
||||
const doc = appDocsManager.getDoc(docId);
|
||||
|
||||
@ -121,7 +121,7 @@ export class SuperStickerRenderer {
|
||||
}
|
||||
|
||||
export default class StickersTab implements EmoticonsTab {
|
||||
public content: HTMLElement;
|
||||
private content: HTMLElement;
|
||||
private stickersDiv: HTMLElement;
|
||||
|
||||
private stickerSets: {[id: string]: {
|
||||
@ -381,4 +381,4 @@ export default class StickersTab implements EmoticonsTab {
|
||||
onClose() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
33
src/helpers/cleanSearchText.ts
Normal file
33
src/helpers/cleanSearchText.ts
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*
|
||||
* Originally from:
|
||||
* https://github.com/zhukov/webogram
|
||||
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
|
||||
* https://github.com/zhukov/webogram/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import Config from "../lib/config";
|
||||
|
||||
const badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<]+/g;
|
||||
const trimRe = /^\s+|\s$/g;
|
||||
|
||||
export default function cleanSearchText(text: string, latinize = true) {
|
||||
const hasTag = text.charAt(0) === '%';
|
||||
text = text.replace(badCharsRe, '').replace(trimRe, '');
|
||||
if(latinize) {
|
||||
text = text.replace(/[^A-Za-z0-9]/g, (ch) => {
|
||||
const latinizeCh = Config.LatinizeMap[ch];
|
||||
return latinizeCh !== undefined ? latinizeCh : ch;
|
||||
});
|
||||
}
|
||||
|
||||
text = text.toLowerCase();
|
||||
if(hasTag) {
|
||||
text = '%' + text;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
14
src/helpers/cleanUsername.ts
Normal file
14
src/helpers/cleanUsername.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*
|
||||
* Originally from:
|
||||
* https://github.com/zhukov/webogram
|
||||
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
|
||||
* https://github.com/zhukov/webogram/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
export default function cleanUsername(username: string) {
|
||||
return username && username.toLowerCase() || '';
|
||||
}
|
@ -27,7 +27,7 @@ export default function attachListNavigation({list, type, onSelect, once}: {
|
||||
return target || list.querySelector('.' + ACTIVE_CLASS_NAME) || list.firstElementChild;
|
||||
};
|
||||
|
||||
const setCurrentTarget = (_target: Element) => {
|
||||
const setCurrentTarget = (_target: Element, scrollTo: boolean) => {
|
||||
if(target === _target) {
|
||||
return;
|
||||
}
|
||||
@ -41,7 +41,7 @@ export default function attachListNavigation({list, type, onSelect, once}: {
|
||||
target = _target;
|
||||
target.classList.add(ACTIVE_CLASS_NAME);
|
||||
|
||||
if(hadTarget && scrollable) {
|
||||
if(hadTarget && scrollable && scrollTo) {
|
||||
fastSmoothScroll(scrollable, target as HTMLElement, 'center', undefined, undefined, undefined, 100, type === 'x' ? 'x' : 'y');
|
||||
}
|
||||
};
|
||||
@ -97,7 +97,7 @@ export default function attachListNavigation({list, type, onSelect, once}: {
|
||||
if(list.childElementCount > 1) {
|
||||
let currentTarget = getCurrentTarget();
|
||||
currentTarget = handleArrowKey(currentTarget, e.key as any);
|
||||
setCurrentTarget(currentTarget);
|
||||
setCurrentTarget(currentTarget, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -112,7 +112,7 @@ export default function attachListNavigation({list, type, onSelect, once}: {
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentTarget(target);
|
||||
setCurrentTarget(target, false);
|
||||
};
|
||||
|
||||
const onClick = (e: Event) => {
|
||||
@ -123,7 +123,7 @@ export default function attachListNavigation({list, type, onSelect, once}: {
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentTarget(target);
|
||||
setCurrentTarget(target, false);
|
||||
fireSelect(getCurrentTarget());
|
||||
};
|
||||
|
||||
@ -142,7 +142,7 @@ export default function attachListNavigation({list, type, onSelect, once}: {
|
||||
};
|
||||
|
||||
const resetTarget = () => {
|
||||
setCurrentTarget(list.firstElementChild);
|
||||
setCurrentTarget(list.firstElementChild, false);
|
||||
};
|
||||
|
||||
resetTarget();
|
||||
|
@ -15,7 +15,7 @@ const set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoE
|
||||
export default function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string, callback?: (err?: Event) => void, useCache = true): boolean {
|
||||
if(!url) {
|
||||
console.error('renderImageFromUrl: no url?', elem, url);
|
||||
//callback && callback();
|
||||
callback && callback();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
185
src/lib/appManagers/appEmojiManager.ts
Normal file
185
src/lib/appManagers/appEmojiManager.ts
Normal file
@ -0,0 +1,185 @@
|
||||
import App from "../../config/app";
|
||||
import { MOUNT_CLASS_TO } from "../../config/debug";
|
||||
import { validateInitObject } from "../../helpers/object";
|
||||
import I18n from "../langPack";
|
||||
import { isObject } from "../mtproto/bin_utils";
|
||||
import apiManager from "../mtproto/mtprotoworker";
|
||||
import SearchIndex from "../searchIndex";
|
||||
import sessionStorage from "../sessionStorage";
|
||||
|
||||
type EmojiLangPack = {
|
||||
keywords: {
|
||||
[keyword: string]: string[],
|
||||
},
|
||||
version: number,
|
||||
langCode: string
|
||||
};
|
||||
|
||||
const EMOJI_LANG_PACK: EmojiLangPack = {
|
||||
keywords: {},
|
||||
version: 0,
|
||||
langCode: App.langPackCode
|
||||
};
|
||||
|
||||
export class AppEmojiManager {
|
||||
private keywordLangPacks: {
|
||||
[langCode: string]: EmojiLangPack
|
||||
} = {};
|
||||
|
||||
private index: SearchIndex<EmojiLangPack['keywords'][keyof EmojiLangPack['keywords']]>;
|
||||
private indexedLangPacks: {[langCode: string]: boolean} = {};
|
||||
|
||||
private getKeywordsPromises: {[langCode: string]: Promise<EmojiLangPack>} = {};
|
||||
|
||||
/* public getPopularEmoji() {
|
||||
return sessionStorage.get('emojis_popular').then(popEmojis => {
|
||||
var result = []
|
||||
if (popEmojis && popEmojis.length) {
|
||||
for (var i = 0, len = popEmojis.length; i < len; i++) {
|
||||
result.push({code: popEmojis[i][0], rate: popEmojis[i][1]})
|
||||
}
|
||||
callback(result)
|
||||
return
|
||||
}
|
||||
|
||||
return sessionStorage.get('emojis_recent').then(recentEmojis => {
|
||||
recentEmojis = recentEmojis || popular || []
|
||||
var shortcut
|
||||
var code
|
||||
for (var i = 0, len = recentEmojis.length; i < len; i++) {
|
||||
shortcut = recentEmojis[i]
|
||||
if (Array.isArray(shortcut)) {
|
||||
shortcut = shortcut[0]
|
||||
}
|
||||
if (shortcut && typeof shortcut === 'string') {
|
||||
if (shortcut.charAt(0) == ':') {
|
||||
shortcut = shortcut.substr(1, shortcut.length - 2)
|
||||
}
|
||||
if (code = shortcuts[shortcut]) {
|
||||
result.push({code: code, rate: 1})
|
||||
}
|
||||
}
|
||||
}
|
||||
callback(result)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function pushPopularEmoji (code) {
|
||||
getPopularEmoji(function (popularEmoji) {
|
||||
var exists = false
|
||||
var count = popularEmoji.length
|
||||
var result = []
|
||||
for (var i = 0; i < count; i++) {
|
||||
if (popularEmoji[i].code == code) {
|
||||
exists = true
|
||||
popularEmoji[i].rate++
|
||||
}
|
||||
result.push([popularEmoji[i].code, popularEmoji[i].rate])
|
||||
}
|
||||
if (exists) {
|
||||
result.sort(function (a, b) {
|
||||
return b[1] - a[1]
|
||||
})
|
||||
} else {
|
||||
if (result.length > 41) {
|
||||
result = result.slice(0, 41)
|
||||
}
|
||||
result.push([code, 1])
|
||||
}
|
||||
ConfigStorage.set({emojis_popular: result})
|
||||
})
|
||||
} */
|
||||
|
||||
public getEmojiKeywords(langCode: string = App.langPackCode) {
|
||||
const promise = this.getKeywordsPromises[langCode];
|
||||
if(promise) {
|
||||
return promise;
|
||||
}
|
||||
|
||||
const storageKey: any = 'emojiKeywords_' + langCode;
|
||||
return this.getKeywordsPromises[langCode] = sessionStorage.get(storageKey).then((pack: EmojiLangPack) => {
|
||||
if(!isObject(pack)) {
|
||||
pack = {} as any;
|
||||
}
|
||||
|
||||
validateInitObject(EMOJI_LANG_PACK, pack);
|
||||
|
||||
// important
|
||||
pack.langCode = langCode;
|
||||
this.keywordLangPacks[langCode] = pack;
|
||||
|
||||
return apiManager.invokeApi('messages.getEmojiKeywordsDifference', {
|
||||
lang_code: pack.langCode,
|
||||
from_version: pack.version
|
||||
}).then((keywordsDifference) => {
|
||||
pack.version = keywordsDifference.version;
|
||||
|
||||
const packKeywords = pack.keywords;
|
||||
const keywords = keywordsDifference.keywords;
|
||||
for(let i = 0, length = keywords.length; i < length; ++i) {
|
||||
const {keyword, emoticons} = keywords[i];
|
||||
packKeywords[keyword] = emoticons;
|
||||
}
|
||||
|
||||
sessionStorage.set({
|
||||
[storageKey]: pack
|
||||
});
|
||||
|
||||
return pack;
|
||||
}, () => {
|
||||
return pack;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public getBothEmojiKeywords() {
|
||||
const promises: ReturnType<AppEmojiManager['getEmojiKeywords']>[] = [
|
||||
this.getEmojiKeywords()
|
||||
];
|
||||
|
||||
if(I18n.lastRequestedLangCode !== App.langPackCode) {
|
||||
promises.push(this.getEmojiKeywords(I18n.lastRequestedLangCode));
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
public indexEmojis() {
|
||||
if(!this.index) {
|
||||
this.index = new SearchIndex();
|
||||
}
|
||||
|
||||
for(const langCode in this.keywordLangPacks) {
|
||||
if(this.indexedLangPacks[langCode]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const pack = this.keywordLangPacks[langCode];
|
||||
const keywords = pack.keywords;
|
||||
|
||||
for(const keyword in keywords) {
|
||||
const emoticons = keywords[keyword];
|
||||
this.index.indexObject(emoticons, keyword);
|
||||
}
|
||||
|
||||
this.indexedLangPacks[langCode] = true;
|
||||
}
|
||||
}
|
||||
|
||||
public searchEmojis(q: string) {
|
||||
this.indexEmojis();
|
||||
|
||||
//const perf = performance.now();
|
||||
const set = this.index.search(q);
|
||||
const flattened = Array.from(set).reduce((acc, v) => acc.concat(v), []);
|
||||
const emojis = Array.from(new Set(flattened));
|
||||
//console.log('searchEmojis', q, 'time', performance.now() - perf);
|
||||
|
||||
return emojis;
|
||||
}
|
||||
}
|
||||
|
||||
const appEmojiManager = new AppEmojiManager();
|
||||
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.appEmojiManager = appEmojiManager);
|
||||
export default appEmojiManager;
|
@ -56,6 +56,7 @@ import disableTransition from '../../helpers/dom/disableTransition';
|
||||
import placeCaretAtEnd from '../../helpers/dom/placeCaretAtEnd';
|
||||
import replaceContent from '../../helpers/dom/replaceContent';
|
||||
import whichChild from '../../helpers/dom/whichChild';
|
||||
import appEmojiManager from './appEmojiManager';
|
||||
|
||||
//console.log('appImManager included33!');
|
||||
|
||||
@ -726,7 +727,25 @@ export class AppImManager {
|
||||
}
|
||||
|
||||
private createNewChat() {
|
||||
const chat = new Chat(this, appChatsManager, appDocsManager, appInlineBotsManager, appMessagesManager, appPeersManager, appPhotosManager, appProfileManager, appStickersManager, appUsersManager, appWebPagesManager, appPollsManager, apiManager, appDraftsManager, serverTimeManager, sessionStorage, appNotificationsManager);
|
||||
const chat = new Chat(this,
|
||||
appChatsManager,
|
||||
appDocsManager,
|
||||
appInlineBotsManager,
|
||||
appMessagesManager,
|
||||
appPeersManager,
|
||||
appPhotosManager,
|
||||
appProfileManager,
|
||||
appStickersManager,
|
||||
appUsersManager,
|
||||
appWebPagesManager,
|
||||
appPollsManager,
|
||||
apiManager,
|
||||
appDraftsManager,
|
||||
serverTimeManager,
|
||||
sessionStorage,
|
||||
appNotificationsManager,
|
||||
appEmojiManager
|
||||
);
|
||||
|
||||
if(this.chats.length) {
|
||||
chat.backgroundEl.append(this.chat.backgroundEl.lastElementChild.cloneNode(true));
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
import { formatPhoneNumber } from "../../components/misc";
|
||||
import { MOUNT_CLASS_TO } from "../../config/debug";
|
||||
import cleanSearchText from "../../helpers/cleanSearchText";
|
||||
import cleanUsername from "../../helpers/cleanUsername";
|
||||
import { tsNow } from "../../helpers/date";
|
||||
import { safeReplaceObject, isObject } from "../../helpers/object";
|
||||
import { InputUser, Update, User as MTUser, UserStatus } from "../../layer";
|
||||
@ -21,7 +23,7 @@ import { REPLIES_PEER_ID } from "../mtproto/mtproto_config";
|
||||
import serverTimeManager from "../mtproto/serverTimeManager";
|
||||
import { RichTextProcessor } from "../richtextprocessor";
|
||||
import rootScope from "../rootScope";
|
||||
import searchIndexManager from "../searchIndexManager";
|
||||
import SearchIndex from "../searchIndex";
|
||||
import apiUpdatesManager from "./apiUpdatesManager";
|
||||
import appChatsManager from "./appChatsManager";
|
||||
import appPeersManager from "./appPeersManager";
|
||||
@ -36,7 +38,7 @@ export class AppUsersManager {
|
||||
|
||||
private users: {[userId: number]: User} = {};
|
||||
private usernames: {[username: string]: number} = {};
|
||||
private contactsIndex = searchIndexManager.createIndex();
|
||||
private contactsIndex = new SearchIndex<number>();
|
||||
private contactsFillPromise: Promise<Set<number>>;
|
||||
private contactsList: Set<number> = new Set();
|
||||
private updatedContactsList = false;
|
||||
@ -110,7 +112,7 @@ export class AppUsersManager {
|
||||
|
||||
rootScope.on('language_change', (e) => {
|
||||
const userId = this.getSelf().id;
|
||||
searchIndexManager.indexObject(userId, this.getUserSearchText(userId), this.contactsIndex);
|
||||
this.contactsIndex.indexObject(userId, this.getUserSearchText(userId));
|
||||
});
|
||||
|
||||
appStateManager.getState().then((state) => {
|
||||
@ -207,7 +209,7 @@ export class AppUsersManager {
|
||||
|
||||
public pushContact(userId: number) {
|
||||
this.contactsList.add(userId);
|
||||
searchIndexManager.indexObject(userId, this.getUserSearchText(userId), this.contactsIndex);
|
||||
this.contactsIndex.indexObject(userId, this.getUserSearchText(userId));
|
||||
appStateManager.requestPeer(userId, 'contacts');
|
||||
}
|
||||
|
||||
@ -233,8 +235,8 @@ export class AppUsersManager {
|
||||
return this.fillContacts().then(_contactsList => {
|
||||
let contactsList = [..._contactsList];
|
||||
if(query) {
|
||||
const results = searchIndexManager.search(query, this.contactsIndex);
|
||||
const filteredContactsList = [...contactsList].filter(id => !!results[id]);
|
||||
const results = this.contactsIndex.search(query);
|
||||
const filteredContactsList = [...contactsList].filter(id => results.has(id));
|
||||
|
||||
contactsList = filteredContactsList;
|
||||
}
|
||||
@ -288,9 +290,9 @@ export class AppUsersManager {
|
||||
|
||||
public testSelfSearch(query: string) {
|
||||
const user = this.getSelf();
|
||||
const index = searchIndexManager.createIndex();
|
||||
searchIndexManager.indexObject(user.id, this.getUserSearchText(user.id), index);
|
||||
return !!searchIndexManager.search(query, index)[user.id];
|
||||
const index = new SearchIndex();
|
||||
index.indexObject(user.id, this.getUserSearchText(user.id));
|
||||
return index.search(query).has(user.id);
|
||||
}
|
||||
|
||||
public saveApiUsers(apiUsers: any[], override?: boolean) {
|
||||
@ -320,11 +322,11 @@ export class AppUsersManager {
|
||||
|
||||
const fullName = user.first_name + ' ' + (user.last_name || '');
|
||||
if(user.username) {
|
||||
const searchUsername = searchIndexManager.cleanUsername(user.username);
|
||||
const searchUsername = cleanUsername(user.username);
|
||||
this.usernames[searchUsername] = userId;
|
||||
}
|
||||
|
||||
user.sortName = user.pFlags.deleted ? '' : searchIndexManager.cleanSearchText(fullName, false);
|
||||
user.sortName = user.pFlags.deleted ? '' : cleanSearchText(fullName, false);
|
||||
|
||||
user.initials = RichTextProcessor.getAbbreviation(fullName);
|
||||
|
||||
|
@ -16,6 +16,7 @@ import MTTransport from "./transports/transport";
|
||||
export class NetworkerFactory {
|
||||
public updatesProcessor: (obj: any) => void = null;
|
||||
public onConnectionStatusChange: (info: ConnectionStatusChange) => void = null;
|
||||
public akStopped = false;
|
||||
|
||||
public setUpdatesProcessor(callback: (obj: any) => void) {
|
||||
this.updatesProcessor = callback;
|
||||
@ -25,6 +26,17 @@ export class NetworkerFactory {
|
||||
//console.log('NetworkerFactory: creating new instance of MTPNetworker:', dcId, options);
|
||||
return new MTPNetworker(dcId, authKey, authKeyID, serverSalt, transport, options);
|
||||
}
|
||||
|
||||
public startAll() {
|
||||
if(this.akStopped) {
|
||||
this.akStopped = false;
|
||||
this.updatesProcessor && this.updatesProcessor({_: 'new_session_created'});
|
||||
}
|
||||
}
|
||||
|
||||
public stopAll() {
|
||||
this.akStopped = true;
|
||||
}
|
||||
}
|
||||
|
||||
export default new NetworkerFactory();
|
||||
|
113
src/lib/mtproto/singleInstance.ts
Normal file
113
src/lib/mtproto/singleInstance.ts
Normal file
@ -0,0 +1,113 @@
|
||||
import { MOUNT_CLASS_TO } from "../../config/debug";
|
||||
import { nextRandomInt } from "../../helpers/random";
|
||||
import { logger } from "../logger";
|
||||
import rootScope from "../rootScope";
|
||||
import sessionStorage from "../sessionStorage";
|
||||
|
||||
export type AppInstance = {
|
||||
id: number,
|
||||
idle: boolean,
|
||||
time: number
|
||||
};
|
||||
|
||||
export class SingleInstance {
|
||||
private instanceID = nextRandomInt(0xFFFFFFFF);
|
||||
private started = false;
|
||||
private masterInstance = false;
|
||||
private deactivateTimeout: number = 0;
|
||||
private deactivated = false;
|
||||
private initial = false;
|
||||
private log = logger('SI');
|
||||
|
||||
public start() {
|
||||
if(!this.started/* && !Config.Navigator.mobile && !Config.Modes.packed */) {
|
||||
this.started = true
|
||||
|
||||
//IdleManager.start();
|
||||
|
||||
rootScope.addEventListener('idle', this.checkInstance);
|
||||
setInterval(this.checkInstance, 5000);
|
||||
this.checkInstance();
|
||||
|
||||
try {
|
||||
document.documentElement.addEventListener('beforeunload', this.clearInstance);
|
||||
} catch(e) {}
|
||||
}
|
||||
}
|
||||
|
||||
public clearInstance() {
|
||||
if(this.masterInstance && !this.deactivated) {
|
||||
this.log.warn('clear master instance');
|
||||
sessionStorage.delete('xt_instance');
|
||||
}
|
||||
}
|
||||
|
||||
public deactivateInstance = () => {
|
||||
if(this.masterInstance || this.deactivated) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.log('deactivate');
|
||||
this.deactivateTimeout = 0;
|
||||
this.deactivated = true;
|
||||
this.clearInstance();
|
||||
//$modalStack.dismissAll();
|
||||
|
||||
//document.title = _('inactive_tab_title_raw')
|
||||
|
||||
rootScope.idle.deactivated = true;
|
||||
};
|
||||
|
||||
public checkInstance = () => {
|
||||
if(this.deactivated) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const time = Date.now();
|
||||
const idle = rootScope.idle && rootScope.idle.isIDLE;
|
||||
const newInstance: AppInstance = {
|
||||
id: this.instanceID,
|
||||
idle,
|
||||
time
|
||||
};
|
||||
|
||||
sessionStorage.get('xt_instance').then((curInstance: AppInstance) => {
|
||||
// console.log(dT(), 'check instance', newInstance, curInstance)
|
||||
if(!idle ||
|
||||
!curInstance ||
|
||||
curInstance.id == this.instanceID ||
|
||||
curInstance.time < time - 20000) {
|
||||
sessionStorage.set({xt_instance: newInstance});
|
||||
if(!this.masterInstance) {
|
||||
//MtpNetworkerFactory.startAll();
|
||||
if(!this.initial) {
|
||||
this.initial = true;
|
||||
} else {
|
||||
this.log.warn('now master instance', newInstance);
|
||||
}
|
||||
|
||||
this.masterInstance = true;
|
||||
}
|
||||
|
||||
if(this.deactivateTimeout) {
|
||||
clearTimeout(this.deactivateTimeout);
|
||||
this.deactivateTimeout = 0;
|
||||
}
|
||||
} else {
|
||||
if(this.masterInstance) {
|
||||
//MtpNetworkerFactory.stopAll();
|
||||
this.log.warn('now idle instance', newInstance);
|
||||
if(!this.deactivateTimeout) {
|
||||
this.deactivateTimeout = window.setTimeout(this.deactivateInstance, 30000);
|
||||
}
|
||||
|
||||
this.masterInstance = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const singleInstance = new SingleInstance();
|
||||
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.singleInstance = singleInstance);
|
||||
export default singleInstance;
|
@ -238,7 +238,7 @@ namespace RichTextProcessor {
|
||||
})
|
||||
} */
|
||||
|
||||
export function parseMarkdown(text: string, currentEntities: MessageEntity[], noTrim?: any): string {
|
||||
export function parseMarkdown(text: string, currentEntities: MessageEntity[], noTrim?: boolean): string {
|
||||
/* if(!markdownTestRegExp.test(text)) {
|
||||
return noTrim ? text : text.trim();
|
||||
} */
|
||||
|
@ -122,6 +122,7 @@ export class RootScope extends EventListenerBase<{
|
||||
public myId = 0;
|
||||
public idle = {
|
||||
isIDLE: true,
|
||||
deactivated: false,
|
||||
focusPromise: Promise.resolve(),
|
||||
focusResolve: () => {}
|
||||
};
|
||||
@ -158,20 +159,30 @@ export class RootScope extends EventListenerBase<{
|
||||
}
|
||||
|
||||
public setThemeListener() {
|
||||
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
const checkDarkMode = () => {
|
||||
//const theme = this.getTheme();
|
||||
this.systemTheme = darkModeMediaQuery.matches ? 'night' : 'day';
|
||||
//const newTheme = this.getTheme();
|
||||
try {
|
||||
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
const checkDarkMode = () => {
|
||||
//const theme = this.getTheme();
|
||||
this.systemTheme = darkModeMediaQuery.matches ? 'night' : 'day';
|
||||
//const newTheme = this.getTheme();
|
||||
|
||||
if(this.myId) {
|
||||
this.broadcast('theme_change');
|
||||
} else {
|
||||
this.setTheme();
|
||||
if(this.myId) {
|
||||
this.broadcast('theme_change');
|
||||
} else {
|
||||
this.setTheme();
|
||||
}
|
||||
};
|
||||
|
||||
if('addEventListener' in darkModeMediaQuery) {
|
||||
darkModeMediaQuery.addEventListener('change', checkDarkMode);
|
||||
} else if('addListener' in darkModeMediaQuery) {
|
||||
(darkModeMediaQuery as any).addListener(checkDarkMode);
|
||||
}
|
||||
};
|
||||
darkModeMediaQuery.addEventListener('change', checkDarkMode);
|
||||
checkDarkMode();
|
||||
|
||||
checkDarkMode();
|
||||
} catch(err) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public setTheme() {
|
||||
|
@ -9,65 +9,26 @@
|
||||
* https://github.com/zhukov/webogram/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import Config from './config';
|
||||
import cleanSearchText from '../helpers/cleanSearchText';
|
||||
|
||||
export type SearchIndex = {
|
||||
fullTexts: {
|
||||
[peerId: string]: string
|
||||
}/* ,
|
||||
shortIndexes: {
|
||||
[shortStr: string]: number[]
|
||||
} */
|
||||
};
|
||||
export default class SearchIndex<SearchWhat> {
|
||||
private fullTexts: Map<SearchWhat, string> = new Map();
|
||||
|
||||
class SearchIndexManager {
|
||||
public static badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<]+/g;
|
||||
public static trimRe = /^\s+|\s$/g;
|
||||
|
||||
public createIndex(): SearchIndex {
|
||||
return {
|
||||
fullTexts: {}/* ,
|
||||
shortIndexes: {} */
|
||||
};
|
||||
}
|
||||
|
||||
public cleanSearchText(text: string, latinize = true) {
|
||||
const hasTag = text.charAt(0) === '%';
|
||||
text = text.replace(SearchIndexManager['badCharsRe'], '').replace(SearchIndexManager['trimRe'], '');
|
||||
if(latinize) {
|
||||
text = text.replace(/[^A-Za-z0-9]/g, (ch) => {
|
||||
const latinizeCh = Config.LatinizeMap[ch];
|
||||
return latinizeCh !== undefined ? latinizeCh : ch;
|
||||
});
|
||||
}
|
||||
|
||||
text = text.toLowerCase();
|
||||
if(hasTag) {
|
||||
text = '%' + text;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
public cleanUsername(username: string) {
|
||||
return username && username.toLowerCase() || '';
|
||||
}
|
||||
|
||||
public indexObject(id: number, searchText: string, searchIndex: SearchIndex) {
|
||||
public indexObject(id: SearchWhat, searchText: string) {
|
||||
/* if(searchIndex.fullTexts.hasOwnProperty(id)) {
|
||||
return false;
|
||||
} */
|
||||
|
||||
if(searchText.trim()) {
|
||||
searchText = this.cleanSearchText(searchText);
|
||||
searchText = cleanSearchText(searchText);
|
||||
}
|
||||
|
||||
if(!searchText) {
|
||||
delete searchIndex.fullTexts[id];
|
||||
this.fullTexts.delete(id);
|
||||
return false;
|
||||
}
|
||||
|
||||
searchIndex.fullTexts[id] = searchText;
|
||||
this.fullTexts.set(id, searchText);
|
||||
|
||||
/* const shortIndexes = searchIndex.shortIndexes;
|
||||
searchText.split(' ').forEach((searchWord) => {
|
||||
@ -84,17 +45,15 @@ class SearchIndexManager {
|
||||
}); */
|
||||
}
|
||||
|
||||
public search(query: string, searchIndex: SearchIndex) {
|
||||
const fullTexts = searchIndex.fullTexts;
|
||||
public search(query: string) {
|
||||
const fullTexts = this.fullTexts;
|
||||
//const shortIndexes = searchIndex.shortIndexes;
|
||||
|
||||
query = this.cleanSearchText(query);
|
||||
query = cleanSearchText(query);
|
||||
|
||||
const newFoundObjs: {[peerId: string]: true} = {};
|
||||
const newFoundObjs: Array<{fullText: string, what: SearchWhat}> = [];
|
||||
const queryWords = query.split(' ');
|
||||
for(const peerId in fullTexts) {
|
||||
const fullText = fullTexts[peerId];
|
||||
|
||||
fullTexts.forEach((fullText, what) => {
|
||||
let found = true;
|
||||
for(const word of queryWords) { // * verify that all words are found
|
||||
const idx = fullText.indexOf(word);
|
||||
@ -105,10 +64,12 @@ class SearchIndexManager {
|
||||
}
|
||||
|
||||
if(found) {
|
||||
newFoundObjs[peerId] = true;
|
||||
newFoundObjs.push({fullText, what});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
//newFoundObjs.sort((a, b) => a.fullText.localeCompare(b.fullText));
|
||||
const newFoundObjs2: Set<SearchWhat> = new Set(newFoundObjs.map(o => o.what));
|
||||
|
||||
/* const queryWords = query.split(' ');
|
||||
let foundArr: number[];
|
||||
@ -139,8 +100,6 @@ class SearchIndexManager {
|
||||
}
|
||||
} */
|
||||
|
||||
return newFoundObjs;
|
||||
return newFoundObjs2;
|
||||
}
|
||||
}
|
||||
|
||||
export default new SearchIndexManager();
|
@ -7,6 +7,7 @@
|
||||
import type { ChatSavedPosition } from './appManagers/appImManager';
|
||||
import type { State } from './appManagers/appStateManager';
|
||||
import type { AppDraftsManager } from './appManagers/appDraftsManager';
|
||||
import type { AppInstance } from './mtproto/singleInstance';
|
||||
import { MOUNT_CLASS_TO } from '../config/debug';
|
||||
import { LangPackDifference } from '../layer';
|
||||
import AppStorage from './storage';
|
||||
@ -21,6 +22,7 @@ const sessionStorage = new AppStorage<{
|
||||
dc5_auth_key: any,
|
||||
max_seen_msg: number,
|
||||
server_time_offset: number,
|
||||
xt_instance: AppInstance,
|
||||
|
||||
chatPositions: {
|
||||
[peerId_threadId: string]: ChatSavedPosition
|
||||
|
@ -20,7 +20,7 @@ import type { ApiUpdatesManager } from "../appManagers/apiUpdatesManager";
|
||||
import type { ServerTimeManager } from "../mtproto/serverTimeManager";
|
||||
import { tsNow } from "../../helpers/date";
|
||||
import apiManager from "../mtproto/mtprotoworker";
|
||||
import searchIndexManager from "../searchIndexManager";
|
||||
import SearchIndex from "../searchIndex";
|
||||
import { forEachReverse, insertInDescendSortedArray } from "../../helpers/array";
|
||||
import rootScope from "../rootScope";
|
||||
import { safeReplaceObject } from "../../helpers/object";
|
||||
@ -38,7 +38,7 @@ export default class DialogsStorage {
|
||||
private pinnedOrders: {[folder_id: number]: number[]};
|
||||
private dialogsNum: number;
|
||||
|
||||
private dialogsIndex = searchIndexManager.createIndex();
|
||||
private dialogsIndex = new SearchIndex<number>();
|
||||
|
||||
private cachedResults: {
|
||||
query: string,
|
||||
@ -71,7 +71,7 @@ export default class DialogsStorage {
|
||||
const dialog = this.getDialogOnly(peerId);
|
||||
if(dialog) {
|
||||
const peerText = appPeersManager.getPeerSearchText(peerId);
|
||||
searchIndexManager.indexObject(peerId, peerText, this.dialogsIndex);
|
||||
this.dialogsIndex.indexObject(peerId, peerText);
|
||||
}
|
||||
});
|
||||
|
||||
@ -340,7 +340,7 @@ export default class DialogsStorage {
|
||||
if(foundDialog[0]) {
|
||||
this.byFolders[foundDialog[0].folder_id].splice(foundDialog[1], 1);
|
||||
delete this.dialogs[peerId];
|
||||
searchIndexManager.indexObject(peerId, '', this.dialogsIndex);
|
||||
this.dialogsIndex.indexObject(peerId, '');
|
||||
|
||||
// clear from state
|
||||
this.appStateManager.keepPeerSingle(0, 'topMessage_' + peerId);
|
||||
@ -435,7 +435,7 @@ export default class DialogsStorage {
|
||||
}
|
||||
|
||||
const peerText = this.appPeersManager.getPeerSearchText(peerId);
|
||||
searchIndexManager.indexObject(peerId, peerText, this.dialogsIndex);
|
||||
this.dialogsIndex.indexObject(peerId, peerText);
|
||||
|
||||
let mid: number, message;
|
||||
if(dialog.top_message) {
|
||||
@ -537,13 +537,13 @@ export default class DialogsStorage {
|
||||
this.cachedResults.query = query;
|
||||
this.cachedResults.folderId = folderId;
|
||||
|
||||
const results = searchIndexManager.search(query, this.dialogsIndex);
|
||||
const results = this.dialogsIndex.search(query);
|
||||
|
||||
this.cachedResults.dialogs = [];
|
||||
|
||||
for(const peerId in this.dialogs) {
|
||||
const dialog = this.dialogs[peerId];
|
||||
if(results[dialog.peerId] && dialog.folder_id === folderId) {
|
||||
if(results.has(dialog.peerId) && dialog.folder_id === folderId) {
|
||||
this.cachedResults.dialogs.push(dialog);
|
||||
}
|
||||
}
|
||||
|
19
src/scss/partials/_chatEmojiHelper.scss
Normal file
19
src/scss/partials/_chatEmojiHelper.scss
Normal file
@ -0,0 +1,19 @@
|
||||
.emoji-helper {
|
||||
height: 50px;
|
||||
padding: .25rem !important;
|
||||
|
||||
> .scrollable {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.super-emojis {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.super-emoji:not(.active) {
|
||||
@include hover() {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
}
|
@ -163,65 +163,6 @@
|
||||
position: relative;
|
||||
margin: 0 -.125rem;
|
||||
|
||||
.category-items {
|
||||
// ! No chrome 56 support
|
||||
display: grid;
|
||||
grid-column-gap: 2.44px;
|
||||
grid-template-columns: repeat(auto-fill, 42px);
|
||||
justify-content: space-between;
|
||||
|
||||
font-size: 2.25rem;
|
||||
line-height: 2.25rem;
|
||||
|
||||
.category-item {
|
||||
display: inline-block;
|
||||
margin: 0 1.44px; // ! magic number
|
||||
padding: 4px 4px;
|
||||
line-height: inherit;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
|
||||
html:not(.emoji-supported) & {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.emoji-placeholder {
|
||||
position: absolute;
|
||||
left: 7px;
|
||||
top: 7px;
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
border-radius: 50%;
|
||||
background-color: var(--light-secondary-text-color);
|
||||
|
||||
@include animation-level(2) {
|
||||
opacity: 0;
|
||||
transition: opacity .2s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
@include animation-level(2) {
|
||||
img {
|
||||
opacity: 1;
|
||||
transition: opacity .2s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
vertical-align: unset;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@include hover-background-effect();
|
||||
}
|
||||
}
|
||||
|
||||
/* &:first-child {
|
||||
//padding-top: 5px;
|
||||
} */
|
||||
|
@ -235,6 +235,7 @@ html.night {
|
||||
@import "partials/chatPinned";
|
||||
@import "partials/chatMarkupTooltip";
|
||||
@import "partials/chatStickersHelper";
|
||||
@import "partials/chatEmojiHelper";
|
||||
@import "partials/chatSearch";
|
||||
@import "partials/chatDrop";
|
||||
@import "partials/crop";
|
||||
@ -1213,3 +1214,62 @@ middle-ellipsis-element {
|
||||
border-radius: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.super-emojis {
|
||||
// ! No chrome 56 support
|
||||
display: grid;
|
||||
grid-column-gap: 2.44px;
|
||||
grid-template-columns: repeat(auto-fill, 42px);
|
||||
justify-content: space-between;
|
||||
|
||||
font-size: 2.25rem;
|
||||
line-height: 2.25rem;
|
||||
|
||||
.super-emoji {
|
||||
display: inline-block;
|
||||
margin: 0 1.44px; // ! magic number
|
||||
padding: 4px 4px;
|
||||
line-height: inherit;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
|
||||
html:not(.emoji-supported) & {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.emoji-placeholder {
|
||||
position: absolute;
|
||||
left: 7px;
|
||||
top: 7px;
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
border-radius: 50%;
|
||||
background-color: var(--light-secondary-text-color);
|
||||
|
||||
@include animation-level(2) {
|
||||
opacity: 0;
|
||||
transition: opacity .2s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
@include animation-level(2) {
|
||||
img {
|
||||
opacity: 1;
|
||||
transition: opacity .2s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
vertical-align: unset;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@include hover-background-effect();
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user