Browse Source

Fix autocomplete regex

Increase download chunks limit
master
Eduard Kuzmenko 3 years ago
parent
commit
0f7522e32a
  1. 2
      src/components/appSearchSuper..ts
  2. 2
      src/components/chat/chat.ts
  3. 4
      src/components/chat/commandsHelper.ts
  4. 57
      src/components/chat/input.ts
  5. 4
      src/components/chat/mentionsHelper.ts
  6. 2
      src/components/sidebarLeft/index.ts
  7. 40
      src/helpers/cleanSearchText.ts
  8. 2
      src/lib/appManagers/appDialogsManager.ts
  9. 2
      src/lib/appManagers/appEmojiManager.ts
  10. 28
      src/lib/appManagers/appProfileManager.ts
  11. 11
      src/lib/appManagers/appStateManager.ts
  12. 42
      src/lib/appManagers/appUsersManager.ts
  13. 2
      src/lib/mtproto/apiFileManager.ts
  14. 13
      src/lib/searchIndex.ts
  15. 7
      src/lib/storages/dialogs.ts
  16. 6
      src/scss/partials/_chat.scss

2
src/components/appSearchSuper..ts

@ -823,7 +823,7 @@ export default class AppSearchSuper { @@ -823,7 +823,7 @@ export default class AppSearchSuper {
};
return Promise.all([
appUsersManager.getTopPeers().then(peers => {
appUsersManager.getTopPeers('correspondents').then(peers => {
if(!middleware()) return;
const idx = peers.indexOf(rootScope.myId);

2
src/components/chat/chat.ts

@ -173,7 +173,7 @@ export default class Chat extends EventListenerBase<{ @@ -173,7 +173,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.appPeersManager, this.appProfileManager);
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.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.appUsersManager);
this.selection = new ChatSelection(this, this.bubbles, this.input, this.appMessagesManager);
this.contextMenu = new ChatContextMenu(this.bubbles.bubblesContainer, this, this.appMessagesManager, this.appPeersManager, this.appPollsManager, this.appDocsManager);

4
src/components/chat/commandsHelper.ts

@ -41,7 +41,9 @@ export default class CommandsHelper extends AutocompletePeerHelper { @@ -41,7 +41,9 @@ export default class CommandsHelper extends AutocompletePeerHelper {
}
const botInfos: BotInfo.botInfo[] = [].concat(full.bot_info);
const index = new SearchIndex<string>(false, false);
const index = new SearchIndex<string>({
ignoreCase: true
});
const commands: Map<string, {peerId: number, name: string, description: string}> = new Map();
botInfos.forEach(botInfo => {

57
src/components/chat/input.ts

@ -14,6 +14,7 @@ import type { AppImManager } from '../../lib/appManagers/appImManager'; @@ -14,6 +14,7 @@ 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 { AppUsersManager } from '../../lib/appManagers/appUsersManager';
import type Chat from './chat';
import Recorder from '../../../public/recorder.min';
import { isTouchSupported } from "../../helpers/touchSupport";
@ -75,7 +76,7 @@ type ChatInputHelperType = 'edit' | 'webpage' | 'forward' | 'reply'; @@ -75,7 +76,7 @@ type ChatInputHelperType = 'edit' | 'webpage' | 'forward' | 'reply';
export default class ChatInput {
// private static AUTO_COMPLETE_REG_EXP = /(\s|^)((?::|.)(?!.*[:@]).*|(?:[@\/]\S*))$/;
private static AUTO_COMPLETE_REG_EXP = /(\s|^)((?:(?:@|^\/)\S*)|(?::|[^:@\/])(?!.*[:@\/]).*)$/;
private static AUTO_COMPLETE_REG_EXP = /(\s|^)((?:(?:@|^\/)\S*)|(?::|^[^:@\/])(?!.*[:@\/]).*)$/;
public messageInput: HTMLElement;
public messageInputField: InputField;
private fileInput: HTMLInputElement;
@ -169,7 +170,8 @@ export default class ChatInput { @@ -169,7 +170,8 @@ export default class ChatInput {
private appDraftsManager: AppDraftsManager,
private serverTimeManager: ServerTimeManager,
private appNotificationsManager: AppNotificationsManager,
private appEmojiManager: AppEmojiManager
private appEmojiManager: AppEmojiManager,
private appUsersManager: AppUsersManager
) {
this.listenerSetter = new ListenerSetter();
}
@ -448,7 +450,8 @@ export default class ChatInput { @@ -448,7 +450,8 @@ export default class ChatInput {
this.listenerSetter.add(rootScope)('settings_updated', () => {
if(this.stickersHelper || this.emojiHelper) {
this.previousQuery = undefined;
// this.previousQuery = undefined;
this.previousQuery = '';
this.checkAutocomplete();
/* if(!rootScope.settings.stickers.suggest) {
this.stickersHelper.checkEmoticon('');
@ -1242,20 +1245,27 @@ export default class ChatInput { @@ -1242,20 +1245,27 @@ export default class ChatInput {
value = value.substr(0, caretPos);
if(this.previousQuery === value) {
return;
}
this.previousQuery = value;
const matches = value.match(ChatInput.AUTO_COMPLETE_REG_EXP);
let foundHelper: AutocompleteHelper;
if(!matches) {
this.previousQuery = undefined;
this.autocompleteHelperController.hideOtherHelpers();
foundHelper = this.checkInlineAutocomplete(value);
// this.previousQuery = undefined;
this.autocompleteHelperController.hideOtherHelpers(foundHelper);
return;
}
if(this.previousQuery === matches[0]) {
/* if(this.previousQuery === matches[0]) {
return;
}
this.previousQuery = matches[0];
this.previousQuery = matches[0]; */
let foundHelper: AutocompleteHelper;
const entity = entities[0];
const query = matches[2];
@ -1267,14 +1277,9 @@ export default class ChatInput { @@ -1267,14 +1277,9 @@ export default class ChatInput {
entity?._ === 'messageEntityEmoji' && entity.length === value.length && !entity.offset) {
foundHelper = this.stickersHelper;
this.stickersHelper.checkEmoticon(value);
} else
//let query = cleanSearchText(query);
//console.log('autocomplete matches', matches);
if(firstChar === '@') { // mentions
} else if(firstChar === '@') { // mentions
const topMsgId = this.chat.threadId ? this.appMessagesManager.getServerMessageId(this.chat.threadId) : undefined;
if(this.mentionsHelper.checkQuery(query, this.chat.peerId, topMsgId)) {
if(this.mentionsHelper.checkQuery(query, this.chat.peerId > 0 ? 0 : this.chat.peerId, topMsgId)) {
foundHelper = this.mentionsHelper;
}
} else if(!matches[1] && firstChar === '/') { // commands
@ -1286,11 +1291,33 @@ export default class ChatInput { @@ -1286,11 +1291,33 @@ export default class ChatInput {
foundHelper = this.emojiHelper;
this.emojiHelper.checkQuery(query, firstChar);
}
} else {
foundHelper = this.checkInlineAutocomplete(value);
}
this.autocompleteHelperController.hideOtherHelpers(foundHelper);
}
private checkInlineAutocomplete(value: string): AutocompleteHelper {
return;
const inlineMatch = value.match(/^@([a-zA-Z\\d_]{3,32})\s/);
if(inlineMatch) {
const username = inlineMatch[1];
console.log('inline match username', username);
this.appUsersManager.resolveUsername(username).then(peer => {
if(peer._ === 'user') {
if(peer.bot_inline_placeholder) {
this.messageInput.dataset.inlinePlaceholder = peer.bot_inline_placeholder;
}
console.log(peer);
}
});
return;
}
}
private onBtnSendClick = (e: Event) => {
cancelEvent(e);

4
src/components/chat/mentionsHelper.ts

@ -43,9 +43,9 @@ export default class MentionsHelper extends AutocompletePeerHelper { @@ -43,9 +43,9 @@ export default class MentionsHelper extends AutocompletePeerHelper {
public checkQuery(query: string, peerId: number, topMsgId: number) {
const trimmed = query.trim(); // check that there is no whitespace
if(peerId > 0 || query.length !== trimmed.length) return false;
if(query.length !== trimmed.length) return false;
this.appProfileManager.getMentions(-peerId, trimmed, topMsgId).then(peerIds => {
this.appProfileManager.getMentions(peerId ? -peerId : 0, trimmed, topMsgId).then(peerIds => {
const username = trimmed.slice(1).toLowerCase();
this.render(peerIds.map(peerId => {
const user = this.appUsersManager.getUser(peerId);

2
src/components/sidebarLeft/index.ts

@ -246,7 +246,7 @@ export class AppSidebarLeft extends SidebarSlider { @@ -246,7 +246,7 @@ export class AppSidebarLeft extends SidebarSlider {
this.archivedCount.classList.toggle('hide', !e.count);
});
appUsersManager.getTopPeers();
appUsersManager.getTopPeers('correspondents');
appStateManager.getState().then(state => {
const recentSearch = state.recentSearch || [];

40
src/helpers/cleanSearchText.ts

@ -14,20 +14,40 @@ import Config from "../lib/config"; @@ -14,20 +14,40 @@ import Config from "../lib/config";
const badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<]+/g;
const trimRe = /^\s+|\s$/g;
export function clearBadCharsAndTrim(text: string) {
return text.replace(badCharsRe, '').replace(trimRe, '');
}
export function latinizeString(text: string) {
return text.replace(/[^A-Za-z0-9]/g, (ch) => {
const latinizeCh = Config.LatinizeMap[ch];
return latinizeCh !== undefined ? latinizeCh : ch;
});
}
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 = clearBadCharsAndTrim(text);
if(latinize) text = latinizeString(text);
text = text.toLowerCase();
if(hasTag) {
text = '%' + text;
}
if(hasTag) text = '%' + text;
return text;
}
export type ProcessSearchTextOptions = Partial<{
clearBadChars: boolean,
latinize: boolean,
ignoreCase: boolean,
includeTag: boolean
}>;
export function processSearchText(text: string, options: ProcessSearchTextOptions = {}) {
const hasTag = options.includeTag && text.charAt(0) === '%';
if(options.clearBadChars) text = clearBadCharsAndTrim(text);
if(options.latinize) text = latinizeString(text);
if(options.ignoreCase) text = text.toLowerCase();
if(hasTag) text = '%' + text;
return text;
}

2
src/lib/appManagers/appDialogsManager.ts

@ -834,7 +834,7 @@ export class AppDialogsManager { @@ -834,7 +834,7 @@ export class AppDialogsManager {
appUsersManager.getContacts().then(users => {
let key: LangPackKey, args: FormatterArguments;
if(users.length) {
if(users.length/* && false */) {
key = 'ChatList.Main.EmptyPlaceholder.Subtitle';
args = [i18n('Contacts.Count', [users.length])];
} else {

2
src/lib/appManagers/appEmojiManager.ts

@ -166,7 +166,7 @@ export class AppEmojiManager { @@ -166,7 +166,7 @@ export class AppEmojiManager {
public indexEmojis() {
if(!this.index) {
this.index = new SearchIndex(false, false, 2);
this.index = new SearchIndex(undefined, 2);
}
for(const langCode in this.keywordLangPacks) {

28
src/lib/appManagers/appProfileManager.ts

@ -397,11 +397,15 @@ export class AppProfileManager { @@ -397,11 +397,15 @@ export class AppProfileManager {
public getMentions(chatId: number, query: string, threadId?: number): Promise<number[]> {
const processUserIds = (userIds: number[]) => {
const startsWithAt = query.charAt(0) === '@';
if(startsWithAt) query = query.slice(1);
/* const startsWithAt = query.charAt(0) === '@';
if(startsWithAt) query = query.slice(1);
const index = new SearchIndex<number>(!startsWithAt, !startsWithAt); */
const index = new SearchIndex<number>(true, true);
const index = new SearchIndex<number>({
ignoreCase: true
});
userIds.forEach(userId => {
index.indexObject(userId, appUsersManager.getUserSearchText(userId));
});
@ -409,19 +413,31 @@ export class AppProfileManager { @@ -409,19 +413,31 @@ export class AppProfileManager {
return Array.from(index.search(query));
};
let promise: Promise<number[]>;
if(appChatsManager.isChannel(chatId)) {
return this.getChannelParticipants(chatId, {
promise = this.getChannelParticipants(chatId, {
_: 'channelParticipantsMentions',
q: query,
top_msg_id: threadId
}, 50, 0).then(cP => {
return processUserIds(cP.participants.map(p => appChatsManager.getParticipantPeerId(p)));
return cP.participants.map(p => appChatsManager.getParticipantPeerId(p));
});
} else {
return (this.getChatFull(chatId) as Promise<ChatFull.chatFull>).then(chatFull => {
return processUserIds((chatFull.participants as ChatParticipants.chatParticipants).participants.map(p => p.user_id));
} else if(chatId) {
promise = (this.getChatFull(chatId) as Promise<ChatFull.chatFull>).then(chatFull => {
return (chatFull.participants as ChatParticipants.chatParticipants).participants.map(p => p.user_id);
});
} else {
promise = Promise.resolve([]);
}
return Promise.all([
[],// appUsersManager.getTopPeers('bots_inline').catch(() => []),
promise
]).then(results => {
const peerIds = results[0].concat(results[1]);
return processUserIds(peerIds);
});
}
public invalidateChannelParticipants(id: number) {

11
src/lib/appManagers/appStateManager.ts

@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
import type { Dialog } from './appMessagesManager';
import type { UserAuth } from '../mtproto/mtproto_config';
import type { User } from './appUsersManager';
import type { TopPeerType, User } from './appUsersManager';
import type { AuthState } from '../../types';
import type FiltersStorage from '../storages/filters';
import type DialogsStorage from '../storages/dialogs';
@ -54,7 +54,12 @@ export type State = { @@ -54,7 +54,12 @@ export type State = {
maxSeenMsgId: number,
stateCreatedTime: number,
recentEmoji: string[],
topPeers: number[],
topPeersCache: {
[type in TopPeerType]?: {
peerIds: number[],
cachedTime: number
}
},
recentSearch: number[],
version: typeof STATE_VERSION,
authState: AuthState,
@ -103,7 +108,7 @@ export const STATE_INIT: State = { @@ -103,7 +108,7 @@ export const STATE_INIT: State = {
maxSeenMsgId: 0,
stateCreatedTime: Date.now(),
recentEmoji: [],
topPeers: [],
topPeersCache: {},
recentSearch: [],
version: STATE_VERSION,
authState: {

42
src/lib/appManagers/appUsersManager.ts

@ -16,7 +16,7 @@ import cleanSearchText from "../../helpers/cleanSearchText"; @@ -16,7 +16,7 @@ import cleanSearchText from "../../helpers/cleanSearchText";
import cleanUsername from "../../helpers/cleanUsername";
import { tsNow } from "../../helpers/date";
import { safeReplaceObject, isObject } from "../../helpers/object";
import { InputUser, User as MTUser, UserProfilePhoto, UserStatus } from "../../layer";
import { Chat, InputUser, User as MTUser, UserProfilePhoto, UserStatus } from "../../layer";
import I18n, { i18n, LangPackKey } from "../langPack";
//import apiManager from '../mtproto/apiManager';
import apiManager from '../mtproto/mtprotoworker';
@ -33,6 +33,7 @@ import appStateManager from "./appStateManager"; @@ -33,6 +33,7 @@ import appStateManager from "./appStateManager";
// TODO: updateUserBlocked
export type User = MTUser.user;
export type TopPeerType = 'correspondents' | 'bots_inline';
export class AppUsersManager {
private storage = appStateManager.storages.users;
@ -44,7 +45,7 @@ export class AppUsersManager { @@ -44,7 +45,7 @@ export class AppUsersManager {
private contactsList: Set<number>;
private updatedContactsList: boolean;
private getTopPeersPromise: Promise<number[]>;
private getTopPeersPromises: {[type in TopPeerType]?: Promise<number[]>};
constructor() {
this.clear(true);
@ -182,7 +183,8 @@ export class AppUsersManager { @@ -182,7 +183,8 @@ export class AppUsersManager {
this.usernames = {};
}
this.contactsIndex = new SearchIndex();
this.getTopPeersPromises = {};
this.contactsIndex = this.createSearchIndex();
this.contactsFillPromise = undefined;
this.contactsList = new Set();
this.updatedContactsList = false;
@ -219,7 +221,7 @@ export class AppUsersManager { @@ -219,7 +221,7 @@ export class AppUsersManager {
return this.contactsFillPromise || (this.contactsFillPromise = promise);
}
public resolveUsername(username: string) {
public resolveUsername(username: string): Promise<Chat | User> {
if(username[0] === '@') {
username = username.slice(1);
}
@ -314,11 +316,20 @@ export class AppUsersManager { @@ -314,11 +316,20 @@ export class AppUsersManager {
public testSelfSearch(query: string) {
const user = this.getSelf();
const index = new SearchIndex();
const index = this.createSearchIndex();
index.indexObject(user.id, this.getUserSearchText(user.id));
return index.search(query).has(user.id);
}
private createSearchIndex() {
return new SearchIndex<number>({
clearBadChars: true,
ignoreCase: true,
latinize: true,
includeTag: true
});
}
public saveApiUsers(apiUsers: any[], override?: boolean) {
apiUsers.forEach((user) => this.saveApiUser(user, override));
}
@ -721,19 +732,20 @@ export class AppUsersManager { @@ -721,19 +732,20 @@ export class AppUsersManager {
});
} */
public getTopPeers(): Promise<number[]> {
if(this.getTopPeersPromise) return this.getTopPeersPromise;
public getTopPeers(type: TopPeerType): Promise<number[]> {
if(this.getTopPeersPromises[type]) return this.getTopPeersPromises[type];
return this.getTopPeersPromise = appStateManager.getState().then((state) => {
if(state?.topPeers?.length) {
return state.topPeers;
return this.getTopPeersPromises[type] = appStateManager.getState().then((state) => {
const cached = state.topPeersCache[type];
if(cached?.peerIds?.length) {
return cached.peerIds;
}
return apiManager.invokeApi('contacts.getTopPeers', {
correspondents: true,
[type]: true,
offset: 0,
limit: 15,
hash: 0,
hash: 0
}).then((result) => {
let peerIds: number[] = [];
if(result._ === 'contacts.topPeers') {
@ -750,7 +762,11 @@ export class AppUsersManager { @@ -750,7 +762,11 @@ export class AppUsersManager {
}
}
appStateManager.pushToState('topPeers', peerIds);
state.topPeersCache[type] = {
peerIds,
cachedTime: Date.now()
};
appStateManager.pushToState('topPeersCache', state.topPeersCache);
return peerIds;
});

2
src/lib/mtproto/apiFileManager.ts

@ -133,7 +133,7 @@ export class ApiFileManager { @@ -133,7 +133,7 @@ export class ApiFileManager {
private downloadCheck(dcId: string | number) {
const downloadPull = this.downloadPulls[dcId];
const downloadLimit = dcId === 'upload' ? 24 : 24;
const downloadLimit = dcId === 'upload' ? 24 : 36;
//const downloadLimit = Infinity;
if(this.downloadActives[dcId] >= downloadLimit || !downloadPull || !downloadPull.length) {

13
src/lib/searchIndex.ts

@ -9,14 +9,13 @@ @@ -9,14 +9,13 @@
* https://github.com/zhukov/webogram/blob/master/LICENSE
*/
import cleanSearchText from '../helpers/cleanSearchText';
import { processSearchText, ProcessSearchTextOptions } from '../helpers/cleanSearchText';
export default class SearchIndex<SearchWhat> {
private fullTexts: Map<SearchWhat, string> = new Map();
// minChars can be 0 because it requires at least one word (one symbol) to be found
constructor(private cleanText = true, private latinize = true, private minChars: number = 0) {
constructor(private options?: ProcessSearchTextOptions, private minChars = 0) {
}
public indexObject(id: SearchWhat, searchText: string) {
@ -24,8 +23,8 @@ export default class SearchIndex<SearchWhat> { @@ -24,8 +23,8 @@ export default class SearchIndex<SearchWhat> {
return false;
} */
if(searchText.trim() && this.cleanText) {
searchText = cleanSearchText(searchText, this.latinize);
if(this.options && searchText.trim()) {
searchText = processSearchText(searchText, this.options);
}
if(!searchText) {
@ -54,8 +53,8 @@ export default class SearchIndex<SearchWhat> { @@ -54,8 +53,8 @@ export default class SearchIndex<SearchWhat> {
const fullTexts = this.fullTexts;
//const shortIndexes = searchIndex.shortIndexes;
if(this.cleanText) {
query = cleanSearchText(query, this.latinize);
if(this.options) {
query = processSearchText(query, this.options);
}
const newFoundObjs: Array<{fullText: string, fullTextLength: number, what: SearchWhat, foundChars: number}> = [];

7
src/lib/storages/dialogs.ts

@ -133,7 +133,12 @@ export default class DialogsStorage { @@ -133,7 +133,12 @@ export default class DialogsStorage {
1: []
};
this.dialogsNum = 0;
this.dialogsIndex = new SearchIndex<number>();
this.dialogsIndex = new SearchIndex<number>({
clearBadChars: true,
ignoreCase: true,
latinize: true,
includeTag: true
});
this.cachedResults = {
query: '',
count: 0,

6
src/scss/partials/_chat.scss

@ -142,6 +142,12 @@ $chat-helper-size: 39px; @@ -142,6 +142,12 @@ $chat-helper-size: 39px;
@include respond-to(handhelds) {
max-height: 10rem;
}
&[data-inline-placeholder]:after {
content: attr(data-inline-placeholder);
color: #a2acb4;
pointer-events: none;
}
}
.toggle-emoticons {

Loading…
Cancel
Save