|
|
|
/*
|
|
|
|
* https://github.com/morethanwords/tweb
|
|
|
|
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
|
|
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
|
|
|
*/
|
|
|
|
|
|
|
|
import appDialogsManager from "../lib/appManagers/appDialogsManager";
|
|
|
|
import Scrollable from "./scrollable";
|
|
|
|
import appMessagesManager from "../lib/appManagers/appMessagesManager";
|
|
|
|
import InputSearch from "./inputSearch";
|
|
|
|
import replaceContent from "../helpers/dom/replaceContent";
|
|
|
|
import { i18n, LangPackKey } from "../lib/langPack";
|
|
|
|
|
|
|
|
export class SearchGroup {
|
|
|
|
container: HTMLDivElement;
|
|
|
|
nameEl: HTMLDivElement;
|
|
|
|
list: HTMLUListElement;
|
|
|
|
|
|
|
|
constructor(public name: LangPackKey | boolean, public type: string, private clearable = true, className?: string, clickable = true, public autonomous = true, public onFound?: () => void) {
|
|
|
|
this.list = appDialogsManager.createChatList();
|
|
|
|
this.container = document.createElement('div');
|
|
|
|
if(className) this.container.className = className;
|
|
|
|
|
|
|
|
if(name) {
|
|
|
|
this.nameEl = document.createElement('div');
|
|
|
|
this.nameEl.classList.add('search-group__name');
|
|
|
|
if(typeof(name) === 'string') {
|
|
|
|
this.nameEl.append(i18n(name));
|
|
|
|
}
|
|
|
|
this.container.append(this.nameEl);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.container.classList.add('search-group', 'search-group-' + type);
|
|
|
|
this.container.append(this.list);
|
|
|
|
this.container.style.display = 'none';
|
|
|
|
|
|
|
|
if(clickable) {
|
|
|
|
appDialogsManager.setListClickListener(this.list, onFound, undefined, autonomous);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
clear() {
|
|
|
|
this.container.style.display = 'none';
|
|
|
|
|
|
|
|
if(this.clearable) {
|
|
|
|
this.list.innerHTML = '';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setActive() {
|
|
|
|
this.container.style.display = '';
|
|
|
|
}
|
|
|
|
|
|
|
|
toggle() {
|
|
|
|
if(this.list.childElementCount) {
|
|
|
|
this.setActive();
|
|
|
|
} else {
|
|
|
|
this.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export type SearchGroupType = 'contacts' | 'globalContacts' | 'messages' | string;
|
|
|
|
|
|
|
|
export default class AppSearch {
|
|
|
|
private minMsgId = 0;
|
|
|
|
private loadedCount = -1;
|
|
|
|
private foundCount = -1;
|
|
|
|
|
|
|
|
private searchPromise: Promise<void> = null;
|
|
|
|
private searchTimeout: number = 0;
|
|
|
|
|
|
|
|
private query = '';
|
|
|
|
|
|
|
|
public listsContainer: HTMLDivElement = null;
|
|
|
|
|
|
|
|
private peerId = 0; // 0 - means global
|
|
|
|
private threadId = 0;
|
|
|
|
|
|
|
|
private scrollable: Scrollable;
|
|
|
|
|
|
|
|
constructor(public container: HTMLElement, public searchInput: InputSearch, public searchGroups: {[group in SearchGroupType]: SearchGroup}, public onSearch?: (count: number) => void) {
|
|
|
|
this.scrollable = new Scrollable(this.container);
|
|
|
|
this.listsContainer = this.scrollable.container as HTMLDivElement;
|
|
|
|
for(let i in this.searchGroups) {
|
|
|
|
this.listsContainer.append(this.searchGroups[i as SearchGroupType].container);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(this.searchGroups.messages) {
|
|
|
|
this.scrollable.setVirtualContainer(this.searchGroups.messages.list);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.searchInput.onChange = (value) => {
|
|
|
|
/* if(!value.trim()) {
|
|
|
|
//this.peerId = 0;
|
|
|
|
return;
|
|
|
|
} */
|
|
|
|
|
|
|
|
this.query = value;
|
|
|
|
this.reset(false);
|
|
|
|
this.searchMore();
|
|
|
|
};
|
|
|
|
|
|
|
|
this.scrollable.onScrolledBottom = () => {
|
|
|
|
if(!this.query.trim()) return;
|
|
|
|
|
|
|
|
if(!this.searchTimeout) {
|
|
|
|
this.searchTimeout = window.setTimeout(() => {
|
|
|
|
this.searchMore();
|
|
|
|
this.searchTimeout = 0;
|
|
|
|
}, 0);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
public reset(all = true) {
|
|
|
|
if(all) {
|
|
|
|
this.searchInput.value = '';
|
|
|
|
this.query = '';
|
|
|
|
this.peerId = 0;
|
|
|
|
this.threadId = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.minMsgId = 0;
|
|
|
|
this.loadedCount = -1;
|
|
|
|
this.foundCount = -1;
|
|
|
|
|
|
|
|
for(let i in this.searchGroups) {
|
|
|
|
this.searchGroups[i as SearchGroupType].clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.searchPromise = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public beginSearch(peerId = 0, threadId = 0) {
|
|
|
|
this.peerId = peerId;
|
|
|
|
this.threadId = threadId;
|
|
|
|
this.searchInput.input.focus();
|
|
|
|
}
|
|
|
|
|
|
|
|
public searchMore() {
|
|
|
|
if(this.searchPromise) return this.searchPromise;
|
|
|
|
|
|
|
|
const query = this.query;
|
|
|
|
|
|
|
|
if(!query.trim()) {
|
|
|
|
this.onSearch && this.onSearch(0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(this.foundCount !== -1 && this.loadedCount >= this.foundCount) {
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
const maxId = this.minMsgId || 0;
|
|
|
|
|
|
|
|
return this.searchPromise = appMessagesManager.getSearch({
|
|
|
|
peerId: this.peerId,
|
|
|
|
query,
|
|
|
|
inputFilter: {_: 'inputMessagesFilterEmpty'},
|
|
|
|
maxId,
|
|
|
|
limit: 20,
|
|
|
|
threadId: this.threadId
|
|
|
|
}).then(res => {
|
|
|
|
this.searchPromise = null;
|
|
|
|
|
|
|
|
if(this.searchInput.value !== query) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//console.log('input search result:', this.peerId, query, null, maxId, 20, res);
|
|
|
|
|
|
|
|
const {count, history} = res;
|
|
|
|
|
|
|
|
if(history.length && history[0].mid === this.minMsgId) {
|
|
|
|
history.shift();
|
|
|
|
}
|
|
|
|
|
|
|
|
const searchGroup = this.searchGroups.messages;
|
|
|
|
|
|
|
|
history.forEach((message) => {
|
|
|
|
const peerId = this.peerId ? message.fromId : message.peerId;
|
|
|
|
const {dialog, dom} = appDialogsManager.addDialogNew({
|
|
|
|
dialog: peerId,
|
|
|
|
container: this.scrollable/* searchGroup.list */,
|
|
|
|
drawStatus: false,
|
|
|
|
avatarSize: 54,
|
|
|
|
meAsSaved: false
|
|
|
|
});
|
|
|
|
|
|
|
|
if(message.peerId !== peerId) {
|
|
|
|
dom.listEl.dataset.peerId = '' + message.peerId;
|
|
|
|
}
|
|
|
|
|
|
|
|
appDialogsManager.setLastMessage(dialog, message, dom, query);
|
|
|
|
});
|
|
|
|
|
|
|
|
searchGroup.toggle();
|
|
|
|
|
|
|
|
this.minMsgId = history.length && history[history.length - 1].mid;
|
|
|
|
|
|
|
|
if(this.loadedCount === -1) {
|
|
|
|
this.loadedCount = 0;
|
|
|
|
}
|
|
|
|
this.loadedCount += history.length;
|
|
|
|
|
|
|
|
if(this.foundCount === -1) {
|
|
|
|
this.foundCount = count;
|
|
|
|
|
|
|
|
if(searchGroup.nameEl) {
|
|
|
|
replaceContent(searchGroup.nameEl, i18n(count ? 'Chat.Search.MessagesFound' : 'Chat.Search.NoMessagesFound', [count]));
|
|
|
|
}
|
|
|
|
|
|
|
|
this.onSearch && this.onSearch(this.foundCount);
|
|
|
|
}
|
|
|
|
}).catch(err => {
|
|
|
|
console.error('search error', err);
|
|
|
|
this.searchPromise = null;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|