Telegram Web K with changes to work inside I2P https://web.telegram.i2p/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

735 lines
23 KiB

import { langPack, findUpClassName, $rootScope, escapeRegExp, whichChild } from "../utils";
5 years ago
import appImManager, { AppImManager } from "./appImManager";
5 years ago
import appPeersManager from './appPeersManager';
import appMessagesManager, { AppMessagesManager, Dialog } from "./appMessagesManager";
import appUsersManager, { User } from "./appUsersManager";
5 years ago
import { RichTextProcessor } from "../richtextprocessor";
import { ripple, putPreloader } from "../../components/misc";
//import Scrollable from "../../components/scrollable";
import Scrollable from "../../components/scrollable_new";
import appProfileManager from "./appProfileManager";
import { logger } from "../polyfill";
import appChatsManager from "./appChatsManager";
5 years ago
type DialogDom = {
avatarDiv: HTMLDivElement,
captionDiv: HTMLDivElement,
titleSpan: HTMLSpanElement,
statusSpan: HTMLSpanElement,
lastTimeSpan: HTMLSpanElement,
unreadMessagesSpan: HTMLSpanElement,
lastMessageSpan: HTMLSpanElement,
containerEl: HTMLDivElement,
listEl: HTMLLIElement
};
let testScroll = false;
5 years ago
export class AppDialogsManager {
public chatList = document.getElementById('dialogs') as HTMLUListElement;
public chatListArchived = document.getElementById('dialogs-archived') as HTMLUListElement;
public pinnedDelimiter: HTMLDivElement;
/* public chatsHidden: Scrollable["hiddenElements"];
public chatsVisible: Scrollable["visibleElements"];
public chatsArchivedHidden: Scrollable["hiddenElements"];
public chatsArchivedVisible: Scrollable["visibleElements"]; */
5 years ago
public doms: {[peerID: number]: DialogDom} = {};
public domsArchived: {[peerID: number]: DialogDom} = {};
public lastActiveListElement: HTMLElement = null;
5 years ago
5 years ago
private rippleCallback: (value?: boolean | PromiseLike<boolean>) => void = null;
private lastClickID = 0;
private lastGoodClickID = 0;
public chatsArchivedContainer = document.getElementById('chats-archived-container') as HTMLDivElement;
public chatsContainer = document.getElementById('chats-container') as HTMLDivElement;
private chatsArchivedOffsetIndex = 0;
private chatsOffsetIndex = 0;
private chatsPreloader: HTMLDivElement;
//private chatsLoadCount = 0;
//private loadDialogsPromise: Promise<any>;
private loadDialogsPromise: ReturnType<AppMessagesManager["getConversations"]>;
private loadedAll = false;
private loadedArchivedAll = false;
public scroll: Scrollable = null;
public scrollArchived: Scrollable = null;
private log = logger('DIALOGS');
5 years ago
constructor() {
this.chatsPreloader = putPreloader(null, true);
//this.chatsContainer.append(this.chatsPreloader);
this.pinnedDelimiter = document.createElement('div');
this.pinnedDelimiter.classList.add('pinned-delimiter');
this.pinnedDelimiter.appendChild(document.createElement('span'));
//this.chatsLoadCount = Math.round(document.body.scrollHeight / 70 * 1.5);
let splitOffset = 1110;
this.scroll = new Scrollable(this.chatsContainer, 'y', splitOffset, 'CL', this.chatList, 500);
this.scroll.setVirtualContainer(this.chatList);
this.scroll.onScrolledBottom = this.onChatsScroll.bind(this);
/* this.chatsHidden = this.scroll.hiddenElements;
this.chatsVisible = this.scroll.visibleElements; */
this.scrollArchived = new Scrollable(this.chatsArchivedContainer, 'y', splitOffset, 'CLA', this.chatListArchived, 500);
this.scrollArchived.setVirtualContainer(this.chatListArchived);
this.scrollArchived.onScrolledBottom = this.onChatsArchivedScroll.bind(this);
/* this.chatsArchivedHidden = this.scrollArchived.hiddenElements;
this.chatsArchivedVisible = this.scrollArchived.visibleElements; */
//this.scrollArchived.container.addEventListener('scroll', this.onChatsArchivedScroll.bind(this));
//let chatClosedDiv = document.getElementById('chat-closed');
this.setListClickListener(this.chatList);
this.setListClickListener(this.chatListArchived);
if(testScroll) {
let i = 0;
let add = () => {
let li = document.createElement('li');
li.dataset.id = '' + i;
li.id = '' + i;
li.innerHTML = `<div class="rp"><div class="user-avatar" style="background-color: rgb(166, 149, 231); font-size: 0px;"><img src="assets/img/pepe.jpg"></div><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 < 1000; ++i) {
add();
}
(window as any).addElement = add;
}
window.addEventListener('resize', () => {
//this.chatsLoadCount = Math.round(document.body.scrollHeight / 70 * 1.5);
setTimeout(() => {
this.onChatsArchivedScroll();
}, 0);
5 years ago
});
$rootScope.$on('user_update', (e: CustomEvent) => {
let userID = e.detail;
let user = appUsersManager.getUser(userID);
let dialog = appMessagesManager.getDialogByPeerID(user.id)[0];
//console.log('updating user:', user, dialog);
if(dialog && !appUsersManager.isBot(dialog.peerID) && dialog.peerID != $rootScope.myID) {
let online = user.status && user.status._ == 'userStatusOnline';
let dom = this.getDialogDom(dialog.peerID);
if(dom) {
if(online) {
dom.avatarDiv.classList.add('is-online');
} else {
dom.avatarDiv.classList.remove('is-online');
}
}
}
if(appImManager.peerID == user.id) {
appImManager.setPeerStatus();
}
5 years ago
});
$rootScope.$on('dialog_top', (e: CustomEvent) => {
let dialog: any = e.detail;
this.setLastMessage(dialog);
this.setUnreadMessages(dialog);
this.setDialogPosition(dialog);
this.setPinnedDelimiter();
});
$rootScope.$on('dialogs_multiupdate', (e: CustomEvent) => {
let dialogs = e.detail;
for(let id in dialogs) {
let dialog = dialogs[id];
/////console.log('updating dialog:', dialog);
if(!(dialog.peerID in this.doms)) {
this.addDialog(dialog);
}
this.setLastMessage(dialog);
this.setUnreadMessages(dialog);
this.setDialogPosition(dialog);
5 years ago
}
this.setPinnedDelimiter();
});
$rootScope.$on('dialog_drop', (e: CustomEvent) => {
let {peerID, dialog} = e.detail;
let dom = this.getDialogDom(peerID);
if(dom) {
dom.listEl.remove();
delete this.doms[peerID];
(dialog.folder_id == 1 ? this.scrollArchived : this.scroll).reorder();
}
5 years ago
});
$rootScope.$on('dialog_unread', (e: CustomEvent) => {
let info: {
peerID: number,
count: number
} = e.detail;
5 years ago
let dialog = appMessagesManager.getDialogByPeerID(info.peerID)[0];
if(dialog) {
this.setUnreadMessages(dialog);
if(dialog.peerID == appImManager.peerID) {
appImManager.updateUnreadByDialog(dialog);
}
}
});
this.loadDialogs().then(result => {
this.setPinnedDelimiter();
//appSidebarLeft.onChatsScroll();
this.loadDialogs(true);
});
}
public async loadDialogs(archived = false) {
if(testScroll) {
return;
}
if(this.loadDialogsPromise/* || 1 == 1 */) return this.loadDialogsPromise;
(archived ? this.chatsArchivedContainer : this.chatsContainer).append(this.chatsPreloader);
//let offset = appMessagesManager.generateDialogIndex();/* appMessagesManager.dialogsNum */;
let offset = archived ? this.chatsArchivedOffsetIndex : this.chatsOffsetIndex;
//let offset = 0;
let scroll = archived ? this.scrollArchived : this.scroll;
try {
console.time('getDialogs time');
let loadCount = 50/*this.chatsLoadCount */;
this.loadDialogsPromise = appMessagesManager.getConversations(offset, loadCount, +archived);
let result = await this.loadDialogsPromise;
console.timeEnd('getDialogs time');
if(result && result.dialogs && result.dialogs.length) {
let index = result.dialogs[result.dialogs.length - 1].index;
if(archived) this.chatsArchivedOffsetIndex = index;
else this.chatsOffsetIndex = index;
result.dialogs.forEach((dialog: any) => {
this.addDialog(dialog);
});
}
if(!result.dialogs.length || (archived ? this.scrollArchived.length == result.count : this.scroll.length == result.count)) { // loaded all
if(archived) this.loadedArchivedAll = true;
else this.loadedAll = true;
}
/* if(archived) {
let count = result.count;
this.archivedCount.innerText = '' + count;
} */
this.log('getDialogs ' + loadCount + ' dialogs by offset:', offset, result, this.scroll.length);
this.scroll.onScroll();
} catch(err) {
this.log.error(err);
}
this.chatsPreloader.remove();
this.loadDialogsPromise = undefined;
}
public onChatsScroll() {
if(this.loadedAll /* || this.scroll.hiddenElements.down.length > 0 */ || this.loadDialogsPromise/* || 1 == 1 */) return;
this.loadDialogs();
}
public onChatsArchivedScroll() {
if(this.loadedArchivedAll /* || this.scrollArchived.hiddenElements.down.length > 0 */ || this.loadDialogsPromise/* || 1 == 1 */) return;
this.loadDialogs(true);
5 years ago
}
public setListClickListener(list: HTMLUListElement, onFound?: () => void) {
list.addEventListener('click', (e: Event) => {
5 years ago
//return;
console.log('dialogs click list');
5 years ago
let target = e.target as HTMLElement;
let elem = target.classList.contains('rp') ? target : findUpClassName(target, 'rp');
5 years ago
if(!elem) {
return;
}
elem = elem.parentElement;
5 years ago
let samePeer = this.lastActiveListElement == elem;
if(this.lastActiveListElement && !samePeer) {
this.lastActiveListElement.classList.remove('active');
}
5 years ago
let startTime = Date.now();
let result: ReturnType<AppImManager['setPeer']>;
//console.log('appDialogsManager: lock lazyLoadQueue');
5 years ago
if(elem) {
/* if(chatClosedDiv) {
chatClosedDiv.style.display = 'none';
} */
if(onFound) onFound();
let peerID = +elem.getAttribute('data-peerID');
5 years ago
let lastMsgID = +elem.dataset.mid;
if(!samePeer) {
elem.classList.add('active');
this.lastActiveListElement = elem;
}
result = appImManager.setPeer(peerID, lastMsgID, false, true);
if(result instanceof Promise) {
this.lastGoodClickID = this.lastClickID;
appImManager.lazyLoadQueue.lock();
}
5 years ago
} else /* if(chatClosedDiv) */ {
5 years ago
result = appImManager.setPeer(0);
5 years ago
//chatClosedDiv.style.display = '';
}
5 years ago
/* if(!(result instanceof Promise)) { // if click on same dialog
this.rippleCallback();
this.rippleCallback = null;
} */
/* promise.then(() => {
appImManager.lazyLoadQueue.unlock();
}); */
/* promise.then(() => {
let length = appImManager.lazyLoadQueue.length();
console.log('pre ripple callback', length);
if(length) {
setTimeout(() => {
this.rippleCallback();
}, length * 25);
} else {
let elapsedTime = Date.now() - startTime;
this.rippleCallback(elapsedTime > 200);
}
}); */
5 years ago
});
}
public setDialogPosition(dialog: Dialog) {
let pos = appMessagesManager.getDialogByPeerID(dialog.peerID)[1];
let dom = this.getDialogDom(dialog.peerID);
let prevPos = whichChild(dom.listEl);
if(prevPos == pos) {
return;
} else if(prevPos < pos) { // was higher
pos += 1;
}
let chatList = dialog.folder_id == 1 ? this.chatListArchived : this.chatList;
if(chatList.childElementCount > pos) {
chatList.insertBefore(dom.listEl, chatList.children[pos]);
} else {
chatList.append(dom.listEl);
}
(dialog.folder_id == 1 ? this.scrollArchived : this.scroll).reorder();
this.log('setDialogPosition:', dialog, dom, pos);
}
public setPinnedDelimiter() {
let index = -1;
let dialogs = appMessagesManager.dialogsStorage.dialogs[0];
for(let dialog of dialogs) {
if(dialog.pFlags?.pinned) {
index++;
}
}
let currentIndex = (this.pinnedDelimiter.parentElement && whichChild(this.pinnedDelimiter.parentElement)) ?? -1;
if(index == currentIndex) return;
let children = this.chatList.children;
5 years ago
let modifying: HTMLElement[] = [];
if(currentIndex != -1 && children.length > currentIndex) {
let li = children[currentIndex] as HTMLElement;
modifying.push(li);
}
if(index != -1 && children.length > index) {
let li = children[index] as HTMLElement;
modifying.push(li);
li.append(this.pinnedDelimiter);
} else {
this.pinnedDelimiter.remove();
}
modifying.forEach(elem => {
this.scroll.updateElement(elem);
5 years ago
});
}
5 years ago
public setLastMessage(dialog: any, lastMessage?: any, dom?: DialogDom, highlightWord?: string) {
5 years ago
if(!lastMessage) {
lastMessage = appMessagesManager.getMessage(dialog.top_message);
}
5 years ago
///////console.log('setlastMessage:', lastMessage);
5 years ago
if(lastMessage._ == 'messageEmpty') return;
if(!dom) {
dom = this.getDialogDom(dialog.peerID);
}
let peer = dialog.peer;
let peerID = dialog.peerID;
//let peerID = appMessagesManager.getMessagePeer(lastMessage);
//console.log('setting last message:', lastMessage);
/* if(!dom.lastMessageSpan.classList.contains('user-typing')) */ {
/* let messageText = lastMessage.message;
let messageWrapped = '';
if(messageText) {
let entities = RichTextProcessor.parseEntities(messageText.replace(/\n/g, ' '), {noLinebreakers: true});
if(highlightWord) {
let regExp = new RegExp(escapeRegExp(highlightWord), 'gi');
let match: any;
if(!entities) entities = [];
let found = false;
while((match = regExp.exec(messageText)) !== null) {
entities.push({_: 'messageEntityHighlight', length: highlightWord.length, offset: match.index});
found = true;
}
if(found) {
entities.sort((a: any, b: any) => a.offset - b.offset);
}
}
messageWrapped = RichTextProcessor.wrapRichText(messageText, {
noLinebreakers: true,
entities: entities,
noTextFormat: true
});
} */
//dom.lastMessageSpan.innerHTML = lastMessageText + messageWrapped;
dom.lastMessageSpan.innerHTML = lastMessage.rReply;
5 years ago
/* if(lastMessage.from_id == auth.id) { // You: */
if(peer._ != 'peerUser' && peerID != -lastMessage.from_id) {
let sender = appUsersManager.getUser(lastMessage.from_id);
if(sender && sender.id) {
let senderBold = document.createElement('b');
let str = '';
if(sender.id == $rootScope.myID) {
5 years ago
str = 'You';
} else {
str = sender.first_name || sender.last_name || sender.username;
}
5 years ago
//senderBold.innerText = str + ': ';
senderBold.innerHTML = RichTextProcessor.wrapRichText(str, {noLinebreakers: true}) + ': ';
5 years ago
//console.log(sender, senderBold.innerText);
dom.lastMessageSpan.prepend(senderBold);
5 years ago
} //////// else console.log('no sender', lastMessage, peerID);
5 years ago
}
}
let timeStr = '';
let timestamp = lastMessage.date;
let now = Date.now() / 1000;
let time = new Date(lastMessage.date * 1000);
if((now - timestamp) < 86400) { // if < 1 day
timeStr = ('0' + time.getHours()).slice(-2) +
':' + ('0' + time.getMinutes()).slice(-2);
} else if((now - timestamp) < (86400 * 7)) { // week
let date = new Date(timestamp * 1000);
timeStr = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][date.getDay()];
} else {
let months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
timeStr = months[time.getMonth()] +
' ' + ('0' + time.getDate()).slice(-2);
}
dom.lastTimeSpan.innerHTML = timeStr;
dom.listEl.setAttribute('data-mid', lastMessage.mid);
if(this.doms[peerID] || this.domsArchived[peerID]) {
5 years ago
this.setUnreadMessages(dialog);
}
}
public setUnreadMessages(dialog: Dialog) {
5 years ago
let dom = this.getDialogDom(dialog.peerID);
let lastMessage = appMessagesManager.getMessage(dialog.top_message);
if(lastMessage._ != 'messageEmpty' &&
lastMessage.from_id == $rootScope.myID && lastMessage.peerID != $rootScope.myID &&
5 years ago
dialog.read_outbox_max_id) { // maybe comment, 06.20.2020
let outgoing = (lastMessage.pFlags && lastMessage.pFlags.unread)
/* && dialog.read_outbox_max_id != 0 */; // maybe uncomment, 31.01.2020
5 years ago
//console.log('outgoing', outgoing, lastMessage);
5 years ago
if(outgoing) {
dom.statusSpan.classList.remove('tgico-checks');
dom.statusSpan.classList.add('tgico-check');
} else {
dom.statusSpan.classList.remove('tgico-check');
dom.statusSpan.classList.add('tgico-checks');
}
} else dom.statusSpan.classList.remove('tgico-check', 'tgico-checks');
dom.unreadMessagesSpan.innerText = '';
dom.unreadMessagesSpan.classList.remove('tgico-pinnedchat');
5 years ago
if(dialog.unread_count) {
dom.unreadMessagesSpan.innerText = '' + dialog.unread_count;
//dom.unreadMessagesSpan.classList.remove('tgico-pinnedchat');
5 years ago
dom.unreadMessagesSpan.classList.add(new Date(dialog.notify_settings.mute_until * 1000) > new Date() ?
'unread-muted' : 'unread');
} else if(dialog.pFlags.pinned) {
dom.unreadMessagesSpan.classList.remove('unread', 'unread-muted');
dom.unreadMessagesSpan.classList.add('tgico-pinnedchat');
}
// set archived new count
if(dialog.folder_id == 1) {
let sum = Object.keys(this.domsArchived).map(p => +p).reduce((acc: number, peerID: number) => {
let dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
if(dialog) {
return acc + dialog.unread_count;
}
return acc;
}, 0);
$rootScope.$broadcast('dialogs_archived_unread', {count: sum});
}
5 years ago
}
public getDialogDom(peerID: number) {
return this.doms[peerID] || this.domsArchived[peerID];
5 years ago
}
public addDialog(_dialog: Dialog | number, container?: HTMLUListElement, drawStatus = true, rippleEnabled = true, onlyFirstName = false) {
let dialog: Dialog;
if(typeof(_dialog) === 'number') {
let originalDialog = appMessagesManager.getDialogByPeerID(_dialog)[0];
if(!originalDialog) {
originalDialog = {
peerID: _dialog,
pFlags: {}
} as any;
}
dialog = originalDialog;
} else {
dialog = _dialog;
}
5 years ago
let peerID: number = dialog.peerID;
if((this.doms[peerID] || this.domsArchived[peerID]) && !container) return;
5 years ago
let title = appPeersManager.getPeerTitle(peerID, false, onlyFirstName);
5 years ago
let avatarDiv = document.createElement('div');
avatarDiv.classList.add('user-avatar');
if(drawStatus && peerID != $rootScope.myID && dialog.peer) {
5 years ago
let peer = dialog.peer;
switch(peer._) {
case 'peerUser':
let user = appUsersManager.getUser(peerID);
//console.log('found user', user);
if(user.status && user.status._ == 'userStatusOnline') {
avatarDiv.classList.add('is-online');
}
break;
default:
break;
}
}
let captionDiv = document.createElement('div');
captionDiv.classList.add('user-caption');
let titleSpan = document.createElement('span');
titleSpan.classList.add('user-title');
if(peerID < 0) {
let chat = appChatsManager.getChat(-peerID);
if(chat && chat.pFlags && chat.pFlags.verified) {
titleSpan.classList.add('is-verified');
}
} else {
let user = appUsersManager.getUser(peerID);
if(user && user.pFlags && user.pFlags.verified) {
titleSpan.classList.add('is-verified');
}
}
if(peerID == $rootScope.myID) {
title = onlyFirstName ? 'Saved' : 'Saved Messages';
5 years ago
}
//console.log('trying to load photo for:', title);
appProfileManager.putPhoto(avatarDiv, dialog.peerID, true);
5 years ago
titleSpan.innerHTML = title;
5 years ago
//p.classList.add('')
let span = document.createElement('span');
span.classList.add('user-last-message');
//captionDiv.append(titleSpan);
//captionDiv.append(span);
let paddingDiv = document.createElement('div');
paddingDiv.classList.add('rp');
paddingDiv.append(avatarDiv, captionDiv);
5 years ago
if(rippleEnabled) {
ripple(paddingDiv, (id) => {
this.log('dialogs click element');
this.lastClickID = id;
return new Promise((resolve, reject) => {
this.rippleCallback = resolve;
//setTimeout(() => resolve(), 100);
//window.requestAnimationFrame(() => window.requestAnimationFrame(() => resolve()));
});
}, (id) => {
//console.log('appDialogsManager: ripple onEnd called!');
if(id == this.lastGoodClickID) {
appImManager.lazyLoadQueue.unlock();
}
5 years ago
});
}
5 years ago
let li = document.createElement('li');
li.append(paddingDiv);
li.setAttribute('data-peerID', '' + peerID);
5 years ago
let statusSpan = document.createElement('span');
statusSpan.classList.add('message-status');
let lastTimeSpan = document.createElement('span');
lastTimeSpan.classList.add('message-time');
let unreadMessagesSpan = document.createElement('span');
let titleP = document.createElement('p');
let rightSpan = document.createElement('span');
rightSpan.append(statusSpan, lastTimeSpan);
titleP.append(titleSpan, rightSpan);
let messageP = document.createElement('p');
messageP.append(span, unreadMessagesSpan);
captionDiv.append(titleP, messageP);
let dom: DialogDom = {
avatarDiv,
captionDiv,
titleSpan,
statusSpan,
lastTimeSpan,
unreadMessagesSpan,
lastMessageSpan: span,
containerEl: paddingDiv,
5 years ago
listEl: li
};
if(!container) {
if(dialog.folder_id && dialog.folder_id == 1) {
this.scrollArchived.append(li);
this.domsArchived[dialog.peerID] = dom;
} else {
this.scroll.append(li);
this.doms[dialog.peerID] = dom;
}
5 years ago
this.setLastMessage(dialog);
} else {
container.append(li);
}
return {dom, dialog};
}
public setTyping(dialog: Dialog, user: User) {
5 years ago
let dom = this.getDialogDom(dialog.peerID);
let str = '';
if(dialog.peerID < 0) {
let s = user.rFirstName || user.username;
if(!s) return;
str = s + ' ';
}
5 years ago
let senderBold = document.createElement('i');
str += 'typing...';
senderBold.innerHTML = str;
5 years ago
dom.lastMessageSpan.innerHTML = '';
dom.lastMessageSpan.append(senderBold);
dom.lastMessageSpan.classList.add('user-typing');
}
public unsetTyping(dialog: Dialog) {
5 years ago
let dom = this.getDialogDom(dialog.peerID);
dom.lastMessageSpan.classList.remove('user-typing');
this.setLastMessage(dialog, null, dom);
}
}
export default new AppDialogsManager();