tweb-i2p/src/lib/appManagers/appDialogsManager.ts
morethanwords 7deb5c7303 Added build number
Fix fetching top message
2021-10-07 17:14:09 +04:00

1724 lines
57 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import type DialogsStorage from "../storages/dialogs";
import type {MyDialogFilter as DialogFilter, MyDialogFilter} from "../storages/filters";
import type { LazyLoadQueueIntersector } from "../../components/lazyLoadQueue";
import AvatarElement from "../../components/avatar";
import DialogsContextMenu from "../../components/dialogsContextMenu";
import { horizontalMenu } from "../../components/horizontalMenu";
import { attachContextMenuListener, putPreloader } from "../../components/misc";
import { ripple } from "../../components/ripple";
//import Scrollable from "../../components/scrollable";
import Scrollable, { ScrollableX, SliceSides } from "../../components/scrollable";
import { formatDateAccordingToTodayNew } from "../../helpers/date";
import { IS_SAFARI } from "../../environment/userAgent";
import { logger, LogTypes } from "../logger";
import { RichTextProcessor } from "../richtextprocessor";
import rootScope from "../rootScope";
import apiUpdatesManager from "./apiUpdatesManager";
import appPeersManager from './appPeersManager';
import appImManager from "./appImManager";
import appMessagesManager, { Dialog, MyMessage } from "./appMessagesManager";
import appStateManager, { State } from "./appStateManager";
import appUsersManager from "./appUsersManager";
import Button from "../../components/button";
import SetTransition from "../../components/singleTransition";
import appDraftsManager, { MyDraftMessage } from "./appDraftsManager";
import DEBUG, { MOUNT_CLASS_TO } from "../../config/debug";
import appNotificationsManager from "./appNotificationsManager";
import PeerTitle from "../../components/peerTitle";
import I18n, { FormatterArguments, i18n, LangPackKey, _i18n } from "../langPack";
import findUpTag from "../../helpers/dom/findUpTag";
import lottieLoader from "../lottieLoader";
import { wrapLocalSticker, wrapPhoto } from "../../components/wrappers";
import AppEditFolderTab from "../../components/sidebarLeft/tabs/editFolder";
import appSidebarLeft, { SettingSection } from "../../components/sidebarLeft";
import { attachClickEvent } from "../../helpers/dom/clickEvent";
import positionElementByIndex from "../../helpers/dom/positionElementByIndex";
import replaceContent from "../../helpers/dom/replaceContent";
import ConnectionStatusComponent from "../../components/connectionStatus";
import appChatsManager from "./appChatsManager";
import { renderImageFromUrlPromise } from "../../helpers/dom/renderImageFromUrl";
import { fastRaf, fastRafConventional, fastRafPromise } from "../../helpers/schedulers";
import SortedUserList from "../../components/sortedUserList";
import { IS_TOUCH_SUPPORTED } from "../../environment/touchSupport";
import handleTabSwipe from "../../helpers/dom/handleTabSwipe";
import windowSize from "../../helpers/windowSize";
import isInDOM from "../../helpers/dom/isInDOM";
import appPhotosManager, { MyPhoto } from "./appPhotosManager";
import { MyDocument } from "./appDocsManager";
import { setSendingStatus } from "../../components/sendingStatus";
import SortedList, { SortedElementBase } from "../../helpers/sortedList";
import debounce from "../../helpers/schedulers/debounce";
import generateVerifiedIcon from "../../components/generateVerifiedIcon";
export type DialogDom = {
avatarEl: AvatarElement,
captionDiv: HTMLDivElement,
titleSpan: HTMLSpanElement,
titleSpanContainer: HTMLSpanElement,
statusSpan: HTMLSpanElement,
lastTimeSpan: HTMLSpanElement,
unreadBadge: HTMLElement,
mentionsBadge?: HTMLElement,
lastMessageSpan: HTMLSpanElement,
containerEl: HTMLElement,
listEl: HTMLLIElement,
subtitleEl: HTMLElement
};
interface SortedDialog extends SortedElementBase {
dom: DialogDom,
loadPromises?: Promise<any>[]
}
class SortedDialogList extends SortedList<SortedDialog> {
constructor(public list: HTMLUListElement, public indexKey: ReturnType<DialogsStorage['getDialogIndexKey']>) {
super({
getIndex: (id) => appMessagesManager.getDialogOnly(id)[this.indexKey],
onDelete: (element) => {
element.dom.listEl.remove();
appDialogsManager.onListLengthChange();
},
onSort: (element, idx) => {
const willChangeLength = element.dom.listEl.parentElement !== this.list;
positionElementByIndex(element.dom.listEl, this.list, idx);
if(willChangeLength) {
appDialogsManager.onListLengthChange();
}
},
onElementCreate: (base, batch) => {
const loadPromises: Promise<any>[] = batch ? [] : undefined;
const {dom} = appDialogsManager.addListDialog({dialog: base.id, loadPromises, isBatch: batch});
(base as SortedDialog).dom = dom;
if(loadPromises?.length) {
(base as SortedDialog).loadPromises = loadPromises;
Promise.all(loadPromises).finally(() => {
delete (base as SortedDialog).loadPromises;
});
}
return base as SortedDialog;
},
updateElementWith: fastRafConventional
});
}
public clear() {
this.list.innerHTML = '';
super.clear();
}
}
//const testScroll = false;
//let testTopSlice = 1;
export class AppDialogsManager {
private chatsContainer = document.getElementById('chatlist-container') as HTMLDivElement;
private chatsPreloader: HTMLElement;
private loadDialogsPromise: Promise<any>;
private scroll: Scrollable = null;
private log = logger('DIALOGS', LogTypes.Log | LogTypes.Error | LogTypes.Warn | LogTypes.Debug);
private contextMenu = new DialogsContextMenu();
public sortedList: SortedDialogList;
public sortedLists: {[filterId: number]: SortedDialogList} = {};
public scrollables: {[filterId: number]: Scrollable} = {};
public filterId: number;
private folders: {[k in 'menu' | 'container' | 'menuScrollContainer']: HTMLElement} = {
menu: document.getElementById('folders-tabs'),
menuScrollContainer: null,
container: document.getElementById('folders-container')
};
private filtersRendered: {
[filterId: string]: {
menu: HTMLElement,
container: HTMLElement,
unread: HTMLElement,
title: HTMLElement
}
} = {};
private showFiltersPromise: Promise<void>;
private allUnreadCount: HTMLElement;
private accumulateArchivedTimeout: number;
//private topOffsetIndex = 0;
private sliceTimeout: number;
private lastActiveElements: Set<HTMLElement> = new Set();
private offsets: {top: number, bottom: number} = {top: 0, bottom: 0};
private loadContacts: () => void;
private processContact: (peerId: number) => void;
private indexKey: ReturnType<DialogsStorage['getDialogIndexKey']>;
public onListLengthChange: () => Promise<void>;
constructor() {
this.chatsPreloader = putPreloader(null, true);
this.allUnreadCount = this.folders.menu.querySelector('.badge');
this.folders.menuScrollContainer = this.folders.menu.parentElement;
const bottomPart = document.createElement('div');
bottomPart.classList.add('connection-status-bottom');
bottomPart.append(this.folders.container);
/* if(isTouchSupported && isSafari) {
let allowUp: boolean, allowDown: boolean, slideBeginY: number;
const container = this.scroll.container;
container.addEventListener('touchstart', (event) => {
allowUp = container.scrollTop > 0;
allowDown = (container.scrollTop < container.scrollHeight - container.clientHeight);
// @ts-ignore
slideBeginY = event.pageY;
});
container.addEventListener('touchmove', (event: any) => {
var up = (event.pageY > slideBeginY);
var down = (event.pageY < slideBeginY);
slideBeginY = event.pageY;
if((up && allowUp) || (down && allowDown)) {
event.stopPropagation();
} else if(up || down) {
event.preventDefault();
}
});
} */
if(IS_TOUCH_SUPPORTED) {
handleTabSwipe(this.folders.container, (next) => {
const prevId = selectTab.prevId();
selectTab(next ? prevId + 1 : prevId - 1);
});
}
this.setFilterId(0);
this.addFilter({
id: this.filterId,
title: '',
titleEl: i18n('ChatList.Filter.AllChats'),
orderIndex: 0
});
this.sortedList = this.sortedLists[this.filterId];
this.scroll = this.scrollables[this.filterId];
/* if(testScroll) {
let i = 0;
let add = () => {
let li = document.createElement('li');
li.dataset.id = '' + i;
li.id = '' + i;
li.innerHTML = `<div class="rp"><avatar-element style="background-color: rgb(166, 149, 231); font-size: 0px;"><img src="assets/img/pepe.jpg"></avatar-element><div class="user-caption"><p><span class="user-title">${i}</span><span><span class="message-status"></span><span class="message-time">18:33</span></span></p><p><span class="user-last-message"><b>-_-_-_-: </b>qweasd</span><span></span></p></div></div>`;
i++;
this.scroll.append(li);
};
for(let i = 0; i < 500; ++i) {
add();
}
(window as any).addElement = add;
} */
rootScope.addEventListener('user_update', (userId) => {
//console.log('updating user:', user, dialog);
const dom = this.getDialogDom(userId);
if(dom && !appUsersManager.isBot(userId) && userId !== rootScope.myId) {
const user = appUsersManager.getUser(userId);
const online = user.status?._ === 'userStatusOnline';
dom.avatarEl.classList.toggle('is-online', online);
}
});
/* rootScope.$on('dialog_top', (e) => {
const dialog = e;
this.setLastMessage(dialog);
this.setDialogPosition(dialog);
this.setFiltersUnreadCount();
}); */
rootScope.addEventListener('dialog_flush', ({peerId}) => {
const dialog = appMessagesManager.getDialogOnly(peerId);
if(dialog) {
this.setLastMessage(dialog, undefined, undefined, undefined, undefined, undefined, true);
this.validateDialogForFilter(dialog);
this.setFiltersUnreadCount();
}
});
rootScope.addEventListener('dialogs_multiupdate', (dialogs) => {
for(const id in dialogs) {
const dialog = dialogs[id];
this.updateDialog(dialog);
if(this.processContact) {
this.processContact(+id);
}
this.validateDialogForFilter(dialog);
}
this.setFiltersUnreadCount();
});
rootScope.addEventListener('dialog_drop', ({peerId}) => {
this.deleteDialog(peerId);
this.setFiltersUnreadCount();
if(this.processContact) {
this.processContact(peerId);
}
});
rootScope.addEventListener('dialog_unread', ({peerId}) => {
const dialog = appMessagesManager.getDialogOnly(peerId);
if(dialog) {
this.setUnreadMessages(dialog);
this.validateDialogForFilter(dialog);
this.setFiltersUnreadCount();
}
});
rootScope.addEventListener('dialog_notify_settings', (dialog) => {
this.setUnreadMessages(dialog); // возможно это не нужно, но нужно менять is-muted
});
rootScope.addEventListener('dialog_draft', ({dialog, drop, peerId}) => {
if(drop) {
this.sortedList.delete(peerId);
} else {
this.updateDialog(dialog);
}
if(this.processContact) {
this.processContact(peerId);
}
});
rootScope.addEventListener('peer_changed', (peerId) => {
//const perf = performance.now();
for(const element of this.lastActiveElements) {
if(+element.dataset.peerId !== peerId) {
element.classList.remove('active');
this.lastActiveElements.delete(element);
}
}
const elements = Array.from(document.querySelectorAll(`[data-autonomous="0"] li[data-peer-id="${peerId}"]`)) as HTMLElement[];
elements.forEach(element => {
element.classList.add('active');
this.lastActiveElements.add(element);
});
//this.log('peer_changed total time:', performance.now() - perf);
});
rootScope.addEventListener('filter_update', (filter) => {
if(!this.filtersRendered[filter.id]) {
this.addFilter(filter);
return;
} else if(filter.id === this.filterId) { // это нет тут смысла вызывать, так как будет dialogs_multiupdate
//this.validateForFilter();
const folder = appMessagesManager.dialogsStorage.getFolder(filter.id);
this.validateListForFilter();
for(let i = 0, length = folder.length; i < length; ++i) {
const dialog = folder[i];
this.updateDialog(dialog);
}
this.setFiltersUnreadCount();
}
const elements = this.filtersRendered[filter.id];
elements.title.innerHTML = RichTextProcessor.wrapEmojiText(filter.title);
});
rootScope.addEventListener('filter_delete', (filter) => {
const elements = this.filtersRendered[filter.id];
if(!elements) return;
// set tab
//(this.folders.menu.firstElementChild.children[Math.max(0, filter.id - 2)] as HTMLElement).click();
(this.folders.menu.firstElementChild as HTMLElement).click();
elements.container.remove();
elements.menu.remove();
delete this.sortedLists[filter.id];
delete this.scrollables[filter.id];
delete this.filtersRendered[filter.id];
if(Object.keys(this.filtersRendered).length <= 1) {
this.folders.menuScrollContainer.classList.add('hide');
}
});
rootScope.addEventListener('filter_order', (order) => {
const containerToAppend = this.folders.menu as HTMLElement;
order.forEach((filterId) => {
const filter = appMessagesManager.filtersStorage.getFilter(filterId);
const renderedFilter = this.filtersRendered[filterId];
const sortedList = this.sortedLists[filterId];
sortedList.indexKey = appMessagesManager.dialogsStorage.getDialogIndexKey(filterId);
positionElementByIndex(renderedFilter.menu, containerToAppend, filter.orderIndex);
positionElementByIndex(renderedFilter.container, this.folders.container, filter.orderIndex);
});
this.indexKey = appMessagesManager.dialogsStorage.getDialogIndexKey(this.filterId);
/* if(this.filterId) {
const tabIndex = order.indexOf(this.filterId) + 1;
selectTab.prevId = tabIndex;
} */
});
rootScope.addEventListener('peer_typings', ({peerId, typings}) => {
const dialog = appMessagesManager.getDialogOnly(peerId);
if(!dialog) return;
if(typings.length) {
this.setTyping(dialog);
} else {
this.unsetTyping(dialog);
}
});
rootScope.addEventListener('state_cleared', () => {
//setTimeout(() =>
appStateManager.getState().then((state) => {
appUsersManager.clear();
appChatsManager.clear();
const filtersStorage = appMessagesManager.filtersStorage;
const filters = filtersStorage.filters;
for(const filterId in filters) { // delete filters
rootScope.dispatchEvent('updateDialogFilter', {
_: 'updateDialogFilter',
id: +filterId,
});
}
appMessagesManager.clear();
/* const clearPromises: Promise<any>[] = [];
for(const name in appStateManager.storagesResults) {
const results = appStateManager.storagesResults[name as keyof AppStateManager['storages']];
const storage = appStateManager.storages[name as keyof AppStateManager['storages']];
results.length = 0;
clearPromises.push(storage.clear());
} */
this.validateListForFilter();
this.onStateLoaded(state);
})//, 5000);
});
const foldersScrollable = new ScrollableX(this.folders.menuScrollContainer);
bottomPart.prepend(this.folders.menuScrollContainer);
const selectTab = horizontalMenu(this.folders.menu, this.folders.container, (id, tabContent) => {
/* if(id !== 0) {
id += 1;
} */
id = +tabContent.dataset.filterId || 0;
if(this.filterId === id) return;
this.sortedLists[id].clear();
this.setFilterId(id);
this.onTabChange();
}, () => {
for(const folderId in this.sortedLists) {
if(+folderId !== this.filterId) {
this.sortedLists[folderId].clear();
}
}
}, undefined, foldersScrollable);
//selectTab(0);
(this.folders.menu.firstElementChild as HTMLElement).click();
appMessagesManager.construct();
appStateManager.getState().then((state) => {
return this.onStateLoaded(state);
}).then(() => {
// return;
const isLoadedMain = appMessagesManager.dialogsStorage.isDialogsLoaded(0);
const isLoadedArchive = appMessagesManager.dialogsStorage.isDialogsLoaded(1);
const wasLoaded = isLoadedMain || isLoadedArchive;
const a: Promise<any> = isLoadedMain ? Promise.resolve() : appMessagesManager.getConversationsAll('', 0);
const b: Promise<any> = isLoadedArchive ? Promise.resolve() : appMessagesManager.getConversationsAll('', 1);
a.finally(() => {
b.then(() => {
this.accumulateArchivedUnread();
if(wasLoaded) {
(apiUpdatesManager.updatesState.syncLoading || Promise.resolve()).then(() => {
appMessagesManager.refreshConversations();
});
}
});
});
});
new ConnectionStatusComponent(this.chatsContainer);
this.chatsContainer.append(bottomPart);
setTimeout(() => {
lottieLoader.loadLottieWorkers();
}, 200);
this.onListLengthChange = debounce(this._onListLengthChange, 100, false, true);
}
public get chatList() {
return this.sortedList.list;
}
public setFilterId(filterId: number) {
this.filterId = filterId;
this.indexKey = appMessagesManager.dialogsStorage ? appMessagesManager.dialogsStorage.getDialogIndexKey(this.filterId) : 'index';
rootScope.filterId = filterId;
}
private async onStateLoaded(state: State) {
appNotificationsManager.getNotifyPeerTypeSettings();
const renderFiltersPromise = appMessagesManager.filtersStorage.getDialogFilters().then((filters) => {
for(const filter of filters) {
this.addFilter(filter);
}
});
if(state.filters && Object.keys(state.filters).length) {
await renderFiltersPromise;
if(this.showFiltersPromise) {
await this.showFiltersPromise;
}
}
if(appStateManager.storagesResults.dialogs.length) {
appDraftsManager.addMissedDialogs();
}
return this.onChatsScroll();
}
/* private getOffset(side: 'top' | 'bottom'): {index: number, pos: number} {
if(!this.scroll.loadedAll[side]) {
const element = (side === 'top' ? this.chatList.firstElementChild : this.chatList.lastElementChild) as HTMLElement;
if(element) {
const peerId = +element.dataset.peerId;
const dialog = appMessagesManager.getDialogByPeerId(peerId);
return {index: dialog[0].index, pos: dialog[1]};
}
}
return {index: 0, pos: -1};
} */
private getOffsetIndex(side: 'top' | 'bottom') {
return {index: this.scroll.loadedAll[side] ? 0 : this.offsets[side]};
}
private isDialogMustBeInViewport(dialog: Dialog) {
if(dialog.migratedTo !== undefined || !this.testDialogForFilter(dialog)) return false;
//return true;
const topOffset = this.getOffsetIndex('top');
const bottomOffset = this.getOffsetIndex('bottom');
if(!topOffset.index && !bottomOffset.index) {
return true;
}
const index = dialog[this.indexKey];
return (!topOffset.index || index <= topOffset.index) && (!bottomOffset.index || index >= bottomOffset.index);
}
private deleteDialog(peerId: number) {
this.sortedList.delete(peerId);
}
private updateDialog(dialog: Dialog) {
if(this.isDialogMustBeInViewport(dialog)) {
if(!this.sortedList.has(dialog.peerId)) {
this.sortedList.add(dialog.peerId);
return;
}
} else {
this.deleteDialog(dialog.peerId);
return;
}
const dom = this.getDialogDom(dialog.peerId);
if(dom) {
this.setLastMessage(dialog, undefined, dom, undefined, undefined, undefined, true);
this.sortedList.update(dialog.peerId);
}
}
public onTabChange = () => {
this.scroll = this.scrollables[this.filterId];
this.scroll.loadedAll.top = true;
this.scroll.loadedAll.bottom = false;
this.offsets.top = this.offsets.bottom = 0;
this.loadDialogsPromise = undefined;
this.sortedList = this.sortedLists[this.filterId];
this.onChatsScroll();
};
private setFilterUnreadCount(filterId: number, folder?: Dialog[]) {
const unreadSpan = filterId === 0 ? this.allUnreadCount : this.filtersRendered[filterId]?.unread;
if(!unreadSpan) {
return;
}
folder = folder || appMessagesManager.dialogsStorage.getFolder(filterId);
let mutedCount = 0;
let notMutedCount = 0;
folder.forEach(dialog => {
const isMuted = appNotificationsManager.isPeerLocalMuted(dialog.peerId, true);
if(isMuted && filterId === 0) {
return;
}
const value = +!!dialog.unread_count || +dialog.pFlags.unread_mark || 0; // * unread_mark can be undefined
if(isMuted) mutedCount += value;
else notMutedCount += value;
});
unreadSpan.classList.toggle('badge-gray', mutedCount && !notMutedCount);
const sum = mutedCount + notMutedCount;
unreadSpan.innerText = sum ? '' + sum : '';
}
private setFiltersUnreadCount() {
for(const filterId in this.filtersRendered) {
this.setFilterUnreadCount(+filterId);
}
this.setFilterUnreadCount(0);
}
/**
* Удалит неподходящие чаты из списка, но не добавит их(!)
*/
private validateListForFilter() {
const filter = appMessagesManager.filtersStorage.getFilter(this.filterId);
this.sortedList.getAll().forEach((element) => {
const dialog = appMessagesManager.getDialogOnly(element.id);
if(!this.testDialogForFilter(dialog, filter || null)) {
this.deleteDialog(element.id);
}
});
}
/**
* Удалит неподходящий чат из списка, но не добавит его(!)
*/
private validateDialogForFilter(dialog: Dialog, filter?: MyDialogFilter) {
if(!this.getDialogDom(dialog.peerId)) {
return;
}
if(!this.testDialogForFilter(dialog, filter)) {
this.deleteDialog(dialog.peerId);
}
}
public testDialogForFilter(dialog: Dialog, filter = appMessagesManager.filtersStorage.getFilter(this.filterId)) {
if(!dialog ||
(filter && !appMessagesManager.filtersStorage.testDialogForFilter(dialog, filter)) ||
(!filter && this.filterId !== dialog.folder_id)) {
return false;
}
return true;
}
public generateScrollable(list: HTMLUListElement, filterId: number) {
const scrollable = new Scrollable(null, 'CL', 500);
scrollable.container.addEventListener('scroll', this.onChatsRegularScroll);
scrollable.container.dataset.filterId = '' + filterId;
scrollable.onScrolledTop = this.onChatsScrollTop;
scrollable.onScrolledBottom = this.onChatsScroll;
scrollable.setVirtualContainer(list);
const sortedDialogList = new SortedDialogList(list, appMessagesManager.dialogsStorage ? appMessagesManager.dialogsStorage.getDialogIndexKey(filterId) : 'index');
this.scrollables[filterId] = scrollable;
this.sortedLists[filterId] = sortedDialogList;
// list.classList.add('hide');
// scrollable.container.style.backgroundColor = '#' + (Math.random() * (16 ** 6 - 1) | 0).toString(16);
return scrollable;
}
private addFilter(filter: Pick<DialogFilter, 'title' | 'id' | 'orderIndex'> & Partial<{titleEl: HTMLElement}>) {
if(this.filtersRendered[filter.id]) return;
const menuTab = document.createElement('div');
menuTab.classList.add('menu-horizontal-div-item');
const span = document.createElement('span');
const titleSpan = document.createElement('span');
titleSpan.classList.add('text-super');
if(filter.titleEl) titleSpan.append(filter.titleEl);
else titleSpan.innerHTML = RichTextProcessor.wrapEmojiText(filter.title);
const unreadSpan = document.createElement('div');
unreadSpan.classList.add('badge', 'badge-20', 'badge-primary');
const i = document.createElement('i');
span.append(titleSpan, unreadSpan, i);
menuTab.append(span);
ripple(menuTab);
const containerToAppend = this.folders.menu as HTMLElement;
positionElementByIndex(menuTab, containerToAppend, filter.orderIndex);
//containerToAppend.append(li);
const ul = this.createChatList();
const scrollable = this.generateScrollable(ul, filter.id);
scrollable.container.classList.add('tabs-tab', 'chatlist-parts');
/* const parts = document.createElement('div');
parts.classList.add('chatlist-parts'); */
const top = document.createElement('div');
top.classList.add('chatlist-top');
const bottom = document.createElement('div');
bottom.classList.add('chatlist-bottom');
top.append(ul);
scrollable.container.append(top, bottom);
/* parts.append(top, bottom);
scrollable.container.append(parts); */
const div = scrollable.container;
//this.folders.container.append(div);
positionElementByIndex(scrollable.container, this.folders.container, filter.orderIndex);
this.setListClickListener(ul, null, true);
this.filtersRendered[filter.id] = {
menu: menuTab,
container: div,
unread: unreadSpan,
title: titleSpan
};
if(!this.showFiltersPromise && Object.keys(this.filtersRendered).length > 1) {
this.showFiltersPromise = new Promise<void>((resolve) => {
window.setTimeout(() => {
this.showFiltersPromise = undefined;
if(Object.keys(this.filtersRendered).length > 1) {
this.folders.menuScrollContainer.classList.remove('hide');
this.setFiltersUnreadCount();
}
resolve();
}, 0);
});
}
}
private loadDialogs(side: SliceSides) {
/* if(testScroll) {
return;
} */
if(this.loadDialogsPromise/* || 1 === 1 */) return this.loadDialogsPromise;
const promise = new Promise<void>(async(resolve) => {
const {chatList, filterId} = this;
//return;
// let loadCount = 30/*this.chatsLoadCount */;
let loadCount = windowSize.windowH / 72 * 1.25 | 0;
let offsetIndex = 0;
const {index: currentOffsetIndex} = this.getOffsetIndex(side);
if(currentOffsetIndex) {
if(side === 'top') {
const storage = appMessagesManager.dialogsStorage.getFolder(filterId, true);
const index = storage.findIndex(dialog => dialog[this.indexKey] <= currentOffsetIndex);
const needIndex = Math.max(0, index - loadCount);
loadCount = index - needIndex;
offsetIndex = storage[needIndex][this.indexKey] + 1;
} else {
offsetIndex = currentOffsetIndex;
}
}
//let offset = storage[storage.length - 1]?.index || 0;
try {
//console.time('getDialogs time');
const getConversationsResult = appMessagesManager.getConversations('', offsetIndex, loadCount, filterId, true);
if(!getConversationsResult.cached && !chatList.childElementCount) {
const container = chatList.parentElement;
container.append(this.chatsPreloader);
}
const result = await getConversationsResult.promise;
if(this.loadDialogsPromise !== promise) {
return;
}
//console.timeEnd('getDialogs time');
// * loaded all
//if(!result.dialogs.length || chatList.childElementCount === result.count) {
// !result.dialogs.length не подходит, так как при супердревном диалоге getConversations его не выдаст.
//if(chatList.childElementCount === result.count) {
if(side === 'bottom') {
if(result.isEnd) {
this.scroll.loadedAll[side] = true;
}
} else if(result.isTopEnd) {
this.scroll.loadedAll[side] = true;
}
if(result.dialogs.length) {
const dialogs = side === 'top' ? result.dialogs.slice().reverse() : result.dialogs;
const loadPromises: Promise<any>[] = [];
const callbacks: (() => void)[] = [];
const cccc = (callback: () => void) => {
callbacks.push(callback);
};
dialogs.forEach((dialog) => {
const element = this.sortedList.add(dialog.peerId, true, cccc, false);
if(element.loadPromises) {
loadPromises.push(...element.loadPromises);
}
});
await Promise.all(loadPromises).finally();
callbacks.forEach(callback => callback());
} else {
this.onListLengthChange();
}
const offsetDialog = result.dialogs[side === 'top' ? 0 : result.dialogs.length - 1];
if(offsetDialog) {
this.offsets[side] = offsetDialog[this.indexKey];
}
this.log.debug('getDialogs ' + loadCount + ' dialogs by offset:', offsetIndex, result, chatList.childElementCount);
setTimeout(() => {
this.scroll.onScroll();
}, 0);
} catch(err) {
this.log.error(err);
}
if(this.chatsPreloader.parentElement) {
this.chatsPreloader.remove();
}
resolve();
}).finally(() => {
this.loadDialogsPromise = undefined;
});
return this.loadDialogsPromise = promise;
}
private generateEmptyPlaceholder(options: {
title: LangPackKey,
subtitle?: LangPackKey,
subtitleArgs?: FormatterArguments,
classNameType: string
}) {
const BASE_CLASS = 'empty-placeholder';
const container = document.createElement('div');
container.classList.add(BASE_CLASS, BASE_CLASS + '-' + options.classNameType);
const header = document.createElement('div');
header.classList.add(BASE_CLASS + '-header');
_i18n(header, options.title);
const subtitle = document.createElement('div');
subtitle.classList.add(BASE_CLASS + '-subtitle');
if(options.subtitle) {
_i18n(subtitle, options.subtitle, options.subtitleArgs);
}
container.append(header, subtitle);
return {container, header, subtitle};
}
private checkIfPlaceholderNeeded() {
if(this.filterId === 1) {
return;
}
const chatList = this.chatList;
const part = chatList.parentElement as HTMLElement;
let placeholderContainer = (Array.from(part.children) as HTMLElement[]).find(el => el.matches('.empty-placeholder'));
const needPlaceholder = this.scroll.loadedAll.bottom && !chatList.childElementCount/* || true */;
// chatList.style.display = 'none';
if(needPlaceholder && placeholderContainer) {
return;
} else if(!needPlaceholder) {
if(placeholderContainer) {
part.classList.remove('with-placeholder');
placeholderContainer.remove();
}
return;
}
let placeholder: ReturnType<AppDialogsManager['generateEmptyPlaceholder']>;
if(!this.filterId) {
placeholder = this.generateEmptyPlaceholder({
title: 'ChatList.Main.EmptyPlaceholder.Title',
classNameType: 'dialogs'
});
placeholderContainer = placeholder.container;
const img = document.createElement('img');
img.classList.add('empty-placeholder-dialogs-icon');
Promise.all([
appUsersManager.getContacts().then(users => {
let key: LangPackKey, args: FormatterArguments;
if(users.length/* && false */) {
key = 'ChatList.Main.EmptyPlaceholder.Subtitle';
args = [i18n('Contacts.Count', [users.length])];
} else {
key = 'ChatList.Main.EmptyPlaceholder.SubtitleNoContacts';
args = [];
}
const subtitleEl = new I18n.IntlElement({
key,
args,
element: placeholder.subtitle
});
}),
renderImageFromUrlPromise(img, 'assets/img/EmptyChats.svg'),
fastRafPromise()
]).then(() => {
placeholderContainer.classList.add('visible');
});
placeholderContainer.prepend(img);
} else {
placeholder = this.generateEmptyPlaceholder({
title: 'FilterNoChatsToDisplay',
subtitle: 'FilterNoChatsToDisplayInfo',
classNameType: 'folder'
});
placeholderContainer = placeholder.container;
placeholderContainer.prepend(wrapLocalSticker({
emoji: '📂',
width: 128,
height: 128
}).container)
const button = Button('btn-primary btn-color-primary btn-control tgico', {
text: 'FilterHeaderEdit',
icon: 'settings'
});
attachClickEvent(button, () => {
new AppEditFolderTab(appSidebarLeft).open(appMessagesManager.filtersStorage.getFilter(this.filterId));
});
placeholderContainer.append(button);
}
part.append(placeholderContainer);
part.classList.add('with-placeholder');
}
public _onListLengthChange = () => {
this.checkIfPlaceholderNeeded();
if(this.filterId > 0) return;
const chatList = this.chatList;
const count = chatList.childElementCount;
const parts = chatList.parentElement.parentElement;
const bottom = chatList.parentElement.nextElementSibling as HTMLElement;
const hasContacts = !!bottom.childElementCount;
if(count >= 10) {
if(hasContacts) {
parts.classList.remove('with-contacts');
bottom.innerHTML = '';
this.loadContacts = undefined;
this.processContact = undefined;
}
return;
} else if(hasContacts) return;
parts.classList.add('with-contacts');
const section = new SettingSection({
name: 'Contacts',
noDelimiter: true,
fakeGradientDelimiter: true
});
section.container.classList.add('hide');
appUsersManager.getContacts(undefined, undefined, 'online').then(contacts => {
const sortedUserList = new SortedUserList({avatarSize: 42, new: true});
this.loadContacts = () => {
const pageCount = windowSize.windowH / 60 | 0;
const arr = contacts.splice(0, pageCount).filter(this.verifyUserIdForContacts);
arr.forEach((peerId) => {
sortedUserList.add(peerId);
});
if(!contacts.length) {
this.loadContacts = undefined;
}
};
this.loadContacts();
this.processContact = (peerId) => {
if(peerId < 0) {
return;
}
const good = this.verifyUserIdForContacts(peerId);
const added = sortedUserList.has(peerId);
if(!added && good) sortedUserList.add(peerId);
else if(added && !good) sortedUserList.delete(peerId);
};
const list = sortedUserList.list;
list.classList.add('chatlist-new');
this.setListClickListener(list);
section.content.append(list);
section.container.classList.remove('hide');
});
bottom.append(section.container);
};
private verifyUserIdForContacts = (peerId: number) => {
const dialog = appMessagesManager.getDialogOnly(peerId);
return !dialog;
};
public onChatsRegularScroll = () => {
// return;
if(this.sliceTimeout) clearTimeout(this.sliceTimeout);
this.sliceTimeout = window.setTimeout(() => {
this.sliceTimeout = undefined;
if(!this.chatList.childElementCount || this.processContact) {
return;
}
/* const observer = new IntersectionObserver((entries) => {
const
});
Array.from(this.chatList.children).forEach(el => {
observer.observe(el);
}); */
fastRafConventional(() => {
const perf = performance.now();
const scrollTopWas = this.scroll.scrollTop;
const firstElementChild = this.chatList.firstElementChild;
const rectContainer = this.scroll.container.getBoundingClientRect();
const rectTarget = firstElementChild.getBoundingClientRect();
const children = Array.from(this.scroll.splitUp.children) as HTMLElement[];
// const padding = 8;
// const offsetTop = this.folders.container.offsetTop;
let offsetTop = this.scroll.splitUp.offsetTop;
if(offsetTop && scrollTopWas < offsetTop) offsetTop -= scrollTopWas;
// const offsetTop = scrollTopWas < padding ? padding - scrollTopWas : 0;
const firstY = rectContainer.y + offsetTop;
const lastY = rectContainer.y/* - 8 */; // 8px - .chatlist padding-bottom
const firstElement = findUpTag(document.elementFromPoint(Math.ceil(rectTarget.x), Math.ceil(firstY + 1)), firstElementChild.tagName) as HTMLElement;
const lastElement = findUpTag(document.elementFromPoint(Math.ceil(rectTarget.x), Math.floor(lastY + rectContainer.height - 1)), firstElementChild.tagName) as HTMLElement;
//alert('got element:' + rect.y);
if(!firstElement || !lastElement) {
return;
}
//alert('got element:' + !!firstElement);
const firstElementRect = firstElement.getBoundingClientRect();
const elementOverflow = firstElementRect.y - firstY;
const sliced: HTMLElement[] = [];
const firstIndex = children.indexOf(firstElement);
const lastIndex = children.indexOf(lastElement);
const saveLength = 10;
const sliceFromStart = IS_SAFARI ? [] : children.slice(0, Math.max(0, firstIndex - saveLength));
const sliceFromEnd = children.slice(lastIndex + saveLength);
/* if(sliceFromStart.length !== sliceFromEnd.length) {
console.log('not equal', sliceFromStart.length, sliceFromEnd.length);
}
if(sliceFromStart.length > sliceFromEnd.length) {
const diff = sliceFromStart.length - sliceFromEnd.length;
sliceFromStart.splice(0, diff);
} else if(sliceFromEnd.length > sliceFromStart.length) {
const diff = sliceFromEnd.length - sliceFromStart.length;
sliceFromEnd.splice(sliceFromEnd.length - diff, diff);
} */
if(sliceFromStart.length) {
this.scroll.loadedAll.top = false;
}
if(sliceFromEnd.length) {
this.scroll.loadedAll.bottom = false;
}
sliced.push(...sliceFromStart);
sliced.push(...sliceFromEnd);
sliced.forEach(el => {
const peerId = +el.dataset.peerId;
this.deleteDialog(peerId);
});
this.setOffsets();
//this.log('[slicer] elements', firstElement, lastElement, rect, sliced, sliceFromStart.length, sliceFromEnd.length);
//this.log('[slicer] reset scrollTop', this.scroll.scrollTop, firstElement.offsetTop, firstElementRect.y, rect.y, elementOverflow);
//alert('left length:' + children.length);
this.scroll.scrollTop = firstElement.offsetTop - elementOverflow;
this.log('slice time', performance.now() - perf);
/* const firstElementRect = firstElement.getBoundingClientRect();
const scrollTop = */
//this.scroll.scrollIntoView(firstElement, false);
});
}, 200);
};
private setOffsets() {
const chatList = this.chatList;
const firstDialog = this.getDialogFromElement(chatList.firstElementChild as HTMLElement);
const lastDialog = this.getDialogFromElement(chatList.lastElementChild as HTMLElement);
this.offsets.top = firstDialog[this.indexKey];
this.offsets.bottom = lastDialog[this.indexKey];
}
private getDialogFromElement(element: HTMLElement) {
return appMessagesManager.getDialogOnly(+element.dataset.peerId);
}
public onChatsScrollTop = () => {
this.onChatsScroll('top');
};
public onChatsScroll = (side: SliceSides = 'bottom') => {
if(this.scroll.loadedAll[side]) {
if(this.loadContacts) {
this.loadContacts();
}
return;
} else if(this.loadDialogsPromise) return this.loadDialogsPromise;
this.log('onChatsScroll', side);
return this.loadDialogs(side);
};
public setListClickListener(list: HTMLUListElement, onFound?: () => void, withContext = false, autonomous = false, openInner = false) {
let lastActiveListElement: HTMLElement;
const setPeerFunc = (openInner ? appImManager.setInnerPeer : appImManager.setPeer).bind(appImManager);
list.dataset.autonomous = '' + +autonomous;
list.addEventListener('mousedown', (e) => {
if(e.button !== 0) return;
//cancelEvent(e);
this.log('dialogs click list');
const target = e.target as HTMLElement;
const elem = findUpTag(target, 'LI');
if(!elem) {
return;
}
if(autonomous) {
const sameElement = lastActiveListElement === elem;
if(lastActiveListElement && !sameElement) {
lastActiveListElement.classList.remove('active');
}
if(elem) {
elem.classList.add('active');
lastActiveListElement = elem;
this.lastActiveElements.add(elem);
}
}
if(elem) {
if(onFound) onFound();
const peerId = +elem.dataset.peerId;
const lastMsgId = +elem.dataset.mid || undefined;
setPeerFunc(peerId, lastMsgId);
} else {
setPeerFunc(0);
}
}, {capture: true});
if(DEBUG) {
list.addEventListener('dblclick', (e) => {
const li = findUpTag(e.target, 'LI');
if(li) {
const peerId = +li.dataset.peerId;
this.log('debug dialog:', appMessagesManager.getDialogByPeerId(peerId));
}
});
}
if(withContext) {
attachContextMenuListener(list, this.contextMenu.onContextMenu);
}
}
public createChatList(options: {
// avatarSize?: number,
// handheldsSize?: number,
// size?: number,
new?: boolean
} = {}) {
const list = document.createElement('ul');
list.classList.add('chatlist'/* ,
'chatlist-avatar-' + (options.avatarSize || 54) *//* , 'chatlist-' + (options.size || 72) */);
if(options.new) {
list.classList.add('chatlist-new');
}
/* if(options.handheldsSize) {
list.classList.add('chatlist-handhelds-' + options.handheldsSize);
} */
return list;
}
public setLastMessage(
dialog: Dialog,
lastMessage?: any,
dom?: DialogDom,
highlightWord?: string,
loadPromises?: Promise<any>[],
isBatch = false,
setUnread = false
) {
///////console.log('setlastMessage:', lastMessage);
if(!dom) {
dom = this.getDialogDom(dialog.peerId);
if(!dom) {
//this.log.error('no dom for dialog:', dialog, lastMessage, dom, highlightWord);
return;
}
}
let draftMessage: MyDraftMessage;
if(!lastMessage) {
if(dialog.draft && dialog.draft._ === 'draftMessage') {
draftMessage = dialog.draft;
}
lastMessage = appMessagesManager.getMessageByPeer(dialog.peerId, dialog.top_message);
}
if(lastMessage._ === 'messageEmpty'/* || (lastMessage._ === 'messageService' && !lastMessage.rReply) */) {
dom.lastMessageSpan.innerHTML = '';
dom.lastTimeSpan.innerHTML = '';
delete dom.listEl.dataset.mid;
if(setUnread) {
this.setUnreadMessages(dialog, dom, isBatch);
}
return;
}
const peerId = dialog.peerId;
//let peerId = appMessagesManager.getMessagePeer(lastMessage);
//console.log('setting last message:', lastMessage);
/* if(!dom.lastMessageSpan.classList.contains('user-typing')) */ {
let mediaContainer: HTMLElement;
if(!lastMessage.deleted && !draftMessage) {
const media: MyDocument | MyPhoto = appMessagesManager.getMediaFromMessage(lastMessage);
if(media && (media._ === 'photo' || (['video', 'gif'] as MyDocument['type'][]).includes(media.type))) {
const size = appPhotosManager.choosePhotoSize(media, 20, 20);
if(size._ !== 'photoSizeEmpty') {
mediaContainer = document.createElement('div');
mediaContainer.classList.add('dialog-subtitle-media');
wrapPhoto({
photo: media,
message: lastMessage,
container: mediaContainer,
withoutPreloader: true,
size,
loadPromises
});
if((media as MyDocument).type === 'video') {
const playIcon = document.createElement('span');
playIcon.classList.add('tgico-play');
mediaContainer.append(playIcon);
}
}
}
}
const withoutMediaType = !!mediaContainer && !!lastMessage?.message;
let fragment: DocumentFragment;
if(highlightWord && lastMessage.message) {
fragment = appMessagesManager.wrapMessageForReply(lastMessage, undefined, undefined, false, highlightWord, withoutMediaType);
} else if(draftMessage) {
fragment = appMessagesManager.wrapMessageForReply(draftMessage);
} else if(!lastMessage.deleted) {
fragment = appMessagesManager.wrapMessageForReply(lastMessage, undefined, undefined, false, undefined, withoutMediaType);
} else { // rare case
fragment = document.createDocumentFragment();
}
if(mediaContainer) {
fragment.prepend(mediaContainer);
}
replaceContent(dom.lastMessageSpan, fragment);
/* if(lastMessage.from_id === auth.id) { // You: */
if(draftMessage) {
const bold = document.createElement('b');
bold.classList.add('danger');
bold.append(i18n('Draft'), ': ');
dom.lastMessageSpan.prepend(bold);
} else if(peerId < 0 && peerId !== lastMessage.fromId && !lastMessage.action) {
const sender = appPeersManager.getPeer(lastMessage.fromId);
if(sender && sender.id) {
const senderBold = document.createElement('b');
if(sender.id === rootScope.myId) {
senderBold.append(i18n('FromYou'));
} else {
//str = sender.first_name || sender.last_name || sender.username;
senderBold.append(new PeerTitle({
peerId: lastMessage.fromId,
onlyFirstName: true,
}).element);
}
senderBold.append(': ');
//console.log(sender, senderBold.innerText);
dom.lastMessageSpan.prepend(senderBold);
} //////// else console.log('no sender', lastMessage, peerId);
}
}
if(!lastMessage.deleted || draftMessage/* && lastMessage._ !== 'draftMessage' */) {
const date = draftMessage ? Math.max(draftMessage.date, lastMessage.date || 0) : lastMessage.date;
replaceContent(dom.lastTimeSpan, formatDateAccordingToTodayNew(new Date(date * 1000)));
} else dom.lastTimeSpan.textContent = '';
if(setUnread !== null) {
if(setUnread) {
this.setUnreadMessages(dialog, dom, isBatch);
} else { // means search
dom.listEl.dataset.mid = lastMessage.mid;
}
}
}
private setUnreadMessages(dialog: Dialog, dom = this.getDialogDom(dialog.peerId), isBatch = false) {
if(dialog.folder_id === 1) {
this.accumulateArchivedUnread();
}
if(!dom) {
//this.log.error('setUnreadMessages no dom!', dialog);
return;
}
if(!isBatch) {
const isMuted = appNotificationsManager.isPeerLocalMuted(dialog.peerId, true);
const wasMuted = dom.listEl.classList.contains('is-muted');
if(isMuted !== wasMuted) {
SetTransition(dom.listEl, 'is-muted', isMuted, 200);
}
}
let setStatusMessage: MyMessage;
if(dialog.draft?._ !== 'draftMessage') {
const lastMessage: MyMessage = appMessagesManager.getMessageByPeer(dialog.peerId, dialog.top_message);
if(!lastMessage.deleted && lastMessage.pFlags.out && lastMessage.peerId !== rootScope.myId) {
setStatusMessage = lastMessage;
}
}
setSendingStatus(dom.statusSpan, setStatusMessage, true);
const filter = appMessagesManager.filtersStorage.getFilter(this.filterId);
let isPinned: boolean;
if(filter) {
isPinned = filter.pinned_peers.indexOf(dialog.peerId) !== -1;
} else {
isPinned = !!dialog.pFlags.pinned;
}
const hasUnreadBadge = isPinned || !!dialog.unread_count || dialog.pFlags.unread_mark;
// dom.messageEl.classList.toggle('has-badge', hasBadge);
const isUnreadBadgeMounted = isInDOM(dom.unreadBadge);
if(hasUnreadBadge && !isUnreadBadgeMounted) {
dom.subtitleEl.append(dom.unreadBadge);
}
const hasMentionsBadge = dialog.unread_mentions_count > 1;
const isMentionBadgeMounted = dom.mentionsBadge && isInDOM(dom.mentionsBadge);
if(hasMentionsBadge) {
if(!dom.mentionsBadge) {
dom.mentionsBadge = document.createElement('div');
dom.mentionsBadge.className = 'dialog-subtitle-badge badge badge-24 mention mention-badge';
dom.mentionsBadge.innerText = '@';
dom.subtitleEl.insertBefore(dom.mentionsBadge, dom.lastMessageSpan.nextSibling);
}
}
const transitionDuration = isBatch ? 0 : 200;
SetTransition(dom.unreadBadge, 'is-visible', hasUnreadBadge, transitionDuration, hasUnreadBadge ? undefined : () => {
dom.unreadBadge.remove();
}, !isUnreadBadgeMounted ? 2 : 0);
if(dom.mentionsBadge) {
SetTransition(dom.mentionsBadge, 'is-visible', hasMentionsBadge, transitionDuration, hasMentionsBadge ? undefined : () => {
dom.mentionsBadge.remove();
delete dom.mentionsBadge;
}, !isMentionBadgeMounted ? 2 : 0);
}
if(!hasUnreadBadge) {
return;
}
if(isPinned) {
dom.unreadBadge.classList.add('tgico-chatspinned', 'tgico');
} else {
dom.unreadBadge.classList.remove('tgico-chatspinned', 'tgico');
}
let isUnread = true, isMention = false;
if(dialog.unread_mentions_count && dialog.unread_count === 1) {
dom.unreadBadge.innerText = '@';
isMention = true;
// dom.unreadBadge.classList.add('tgico-mention', 'tgico');
} else if(dialog.unread_count || dialog.pFlags.unread_mark) {
//dom.unreadMessagesSpan.innerText = '' + (dialog.unread_count ? formatNumber(dialog.unread_count, 1) : ' ');
dom.unreadBadge.innerText = '' + (dialog.unread_count || ' ');
} else {
dom.unreadBadge.innerText = '';
isUnread = false;
}
dom.unreadBadge.classList.toggle('unread', isUnread);
dom.unreadBadge.classList.toggle('mention', isMention);
}
private accumulateArchivedUnread() {
if(this.accumulateArchivedTimeout) return;
this.accumulateArchivedTimeout = window.setTimeout(() => {
this.accumulateArchivedTimeout = 0;
const dialogs = appMessagesManager.dialogsStorage.getFolder(1);
const sum = dialogs.reduce((acc, dialog) => acc + dialog.unread_count, 0);
rootScope.dispatchEvent('dialogs_archived_unread', {count: sum});
}, 0);
}
private getDialogDom(peerId: number) {
// return this.doms[peerId];
const element = this.sortedList.get(peerId);
return element?.dom;
}
private getDialog(dialog: Dialog | number): Dialog {
if(typeof(dialog) === 'number') {
const originalDialog = appMessagesManager.getDialogOnly(dialog);
if(!originalDialog) {
return {
peerId: dialog,
peer: appPeersManager.getOutputPeer(dialog),
pFlags: {}
} as any;
}
return originalDialog;
}
return dialog;
}
public addListDialog(options: Parameters<AppDialogsManager['addDialogNew']>[0] & {isBatch?: boolean}) {
const dialog = this.getDialog(options.dialog);
options.autonomous = false;
const ret = this.addDialogNew(options);
if(ret) {
const isMuted = appNotificationsManager.isPeerLocalMuted(dialog.peerId, true);
if(isMuted) {
ret.dom.listEl.classList.add('is-muted');
}
this.setLastMessage(dialog, undefined, ret.dom, undefined, options.loadPromises, options.isBatch, true);
}
return ret;
}
public addDialogNew(options: {
dialog: Dialog | number,
container?: Parameters<AppDialogsManager['addDialog']>[1],
drawStatus?: boolean,
rippleEnabled?: boolean,
onlyFirstName?: boolean,
meAsSaved?: boolean,
append?: boolean,
avatarSize?: number,
autonomous?: boolean,
lazyLoadQueue?: LazyLoadQueueIntersector,
loadPromises?: Promise<any>[]
}) {
return this.addDialog(options.dialog, options.container, options.drawStatus, options.rippleEnabled, options.onlyFirstName, options.meAsSaved, options.append, options.avatarSize, options.autonomous, options.lazyLoadQueue, options.loadPromises);
}
public addDialog(_dialog: Dialog | number,
container?: HTMLElement | Scrollable | DocumentFragment | false,
drawStatus = true,
rippleEnabled = true,
onlyFirstName = false,
meAsSaved = true,
append = true,
avatarSize = 54,
autonomous = !!container,
lazyLoadQueue?: LazyLoadQueueIntersector,
loadPromises?: Promise<any>[]) {
const dialog = this.getDialog(_dialog);
const peerId = dialog.peerId;
const avatarEl = new AvatarElement();
avatarEl.loadPromises = loadPromises;
avatarEl.lazyLoadQueue = lazyLoadQueue;
avatarEl.setAttribute('dialog', meAsSaved ? '1' : '0');
avatarEl.setAttribute('peer', '' + peerId);
avatarEl.classList.add('dialog-avatar', 'avatar-' + avatarSize);
if(drawStatus && peerId !== rootScope.myId) {
if(peerId > 0) {
const user = appUsersManager.getUser(peerId);
//console.log('found user', user);
if(user.status && user.status._ === 'userStatusOnline') {
avatarEl.classList.add('is-online');
}
}
}
const captionDiv = document.createElement('div');
captionDiv.classList.add('user-caption');
const titleSpanContainer = document.createElement('span');
titleSpanContainer.classList.add('user-title');
const peerTitle = new PeerTitle({
peerId,
dialog: meAsSaved,
onlyFirstName,
plainText: false
});
titleSpanContainer.append(peerTitle.element);
//p.classList.add('')
// в других случаях иконка верификации не нужна (а первый - это главные чатлисты)
//if(!container) {
// for muted icon
titleSpanContainer.classList.add('tgico'); // * эта строка будет актуальна только для !container, но ладно
const peer = appPeersManager.getPeer(peerId);
if(peer?.pFlags?.verified) {
titleSpanContainer.append(generateVerifiedIcon());
}
//}
const span = document.createElement('span');
span.classList.add('user-last-message');
span.setAttribute('dir', 'auto');
//captionDiv.append(titleSpan);
//captionDiv.append(span);
const li = document.createElement('li');
if(rippleEnabled) {
ripple(li);
}
li.append(avatarEl, captionDiv);
li.dataset.peerId = '' + peerId;
const statusSpan = document.createElement('span');
statusSpan.classList.add('message-status', 'sending-status'/* , 'transition', 'reveal' */);
const lastTimeSpan = document.createElement('span');
lastTimeSpan.classList.add('message-time');
const unreadBadge = document.createElement('div');
unreadBadge.className = 'dialog-subtitle-badge badge badge-24';
const titleP = document.createElement('p');
titleP.classList.add('dialog-title');
const rightSpan = document.createElement('span');
rightSpan.classList.add('dialog-title-details');
rightSpan.append(statusSpan, lastTimeSpan);
titleP.append(titleSpanContainer, rightSpan);
const subtitleEl = document.createElement('p');
subtitleEl.classList.add('dialog-subtitle');
subtitleEl.append(span);
captionDiv.append(titleP, subtitleEl);
const dom: DialogDom = {
avatarEl,
captionDiv,
titleSpan: peerTitle.element,
titleSpanContainer,
statusSpan,
lastTimeSpan,
unreadBadge,
lastMessageSpan: span,
containerEl: li,
listEl: li,
subtitleEl
};
/* let good = false;
for(const folderId in this.chatLists) {
if(this.chatLists[folderId] === container) {
good = true;
}
} */
if(container) {
const method = append ? 'append' : 'prepend';
container[method](li);
}
if(!autonomous && appImManager.chat?.peerId === peerId) {
li.classList.add('active');
this.lastActiveElements.add(li);
}
return {dom, dialog};
}
public setTyping(dialog: Dialog) {
const dom = this.getDialogDom(dialog.peerId);
if(!dom) {
return;
}
let typingElement = dom.lastMessageSpan.querySelector('.peer-typing-container') as HTMLElement;
if(typingElement) {
appImManager.getPeerTyping(dialog.peerId, typingElement);
} else {
typingElement = appImManager.getPeerTyping(dialog.peerId);
replaceContent(dom.lastMessageSpan, typingElement);
dom.lastMessageSpan.classList.add('user-typing');
}
}
public unsetTyping(dialog: Dialog) {
const dom = this.getDialogDom(dialog.peerId);
if(!dom) {
return;
}
dom.lastMessageSpan.classList.remove('user-typing');
this.setLastMessage(dialog, null, dom, undefined, undefined, undefined, null);
}
}
const appDialogsManager = new AppDialogsManager();
MOUNT_CLASS_TO.appDialogsManager = appDialogsManager;
export default appDialogsManager;