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.

1992 lines
69 KiB

import apiManager from '../mtproto/apiManager';
4 years ago
import { $rootScope, isElementInViewport, numberWithCommas, findUpClassName, formatNumber, placeCaretAtEnd, calcImageInBox, findUpTag, langPack } from "../utils";
4 years ago
import appUsersManager from "./appUsersManager";
import appMessagesManager from "./appMessagesManager";
4 years ago
import appPeersManager from "./appPeersManager";
import appProfileManager from "./appProfileManager";
import appDialogsManager from "./appDialogsManager";
import { RichTextProcessor } from "../richtextprocessor";
import appPhotosManager from "./appPhotosManager";
import appSidebarRight from './appSidebarRight';
import { logger } from "../polyfill";
import lottieLoader from "../lottieLoader";
import appMediaViewer from "./appMediaViewer";
import appSidebarLeft from "./appSidebarLeft";
import appChatsManager from "./appChatsManager";
import appMessagesIDsManager from "./appMessagesIDsManager";
import apiUpdatesManager from './apiUpdatesManager';
4 years ago
import { wrapDocument, wrapPhoto, wrapVideo, wrapSticker, wrapReply } from '../../components/wrappers';
import ProgressivePreloader from '../../components/preloader';
import { openBtnMenu } from '../../components/misc';
4 years ago
import { ChatInput } from '../../components/chatInput';
import Scrollable from '../../components/scrollable';
4 years ago
import BubbleGroups from '../../components/bubbleGroups';
import LazyLoadQueue from '../../components/lazyLoadQueue';
4 years ago
console.log('appImManager included!');
let testScroll = false;
export class AppImManager {
public pageEl = document.querySelector('.page-chats') as HTMLDivElement;
public btnMute = this.pageEl.querySelector('.tool-mute') as HTMLButtonElement;
public btnMenuMute = this.pageEl.querySelector('.menu-mute') as HTMLButtonElement;
4 years ago
public avatarEl = document.getElementById('im-avatar') as HTMLDivElement;
public titleEl = document.getElementById('im-title') as HTMLDivElement;
public subtitleEl = document.getElementById('im-subtitle') as HTMLDivElement;
4 years ago
public bubblesContainer = document.getElementById('bubbles') as HTMLDivElement;
4 years ago
public chatInner = document.getElementById('bubbles-inner') as HTMLDivElement;
public searchBtn = this.pageEl.querySelector('.chat-search-button') as HTMLButtonElement;
public goDownBtn = this.pageEl.querySelector('#bubbles-go-down') as HTMLButtonElement;
private getHistoryTopPromise: Promise<boolean>;
private getHistoryBottomPromise: Promise<boolean>;
4 years ago
private chatInputC: ChatInput = null;
4 years ago
4 years ago
public myID = 0;
public peerID = 0;
public muted = false;
4 years ago
4 years ago
public bubbles: {[mid: number]: HTMLDivElement} = {};
public dateMessages: {[timestamp: number]: {
div: HTMLDivElement,
firstTimestamp: number,
bubble: HTMLDivElement,
timeout?: number
}} = {};
4 years ago
public unreaded: number[] = [];
public unreadOut: number[] = [];
public needUpdate: {replyMid: number, mid: number}[] = []; // if need wrapSingleMessage
4 years ago
public offline = false;
public updateStatusInterval = 0;
4 years ago
4 years ago
public pinnedMsgID = 0;
private pinnedMessageContainer = this.pageEl.querySelector('.pinned-message') as HTMLDivElement;
private pinnedMessageContent = this.pinnedMessageContainer.querySelector('.pinned-message-subtitle') as HTMLDivElement;
private firstTopMsgID = 0;
4 years ago
4 years ago
public lazyLoadQueue = new LazyLoadQueue();
4 years ago
public scroll: HTMLDivElement = null;
4 years ago
public scrollable: Scrollable = null;
public log: ReturnType<typeof logger>;
4 years ago
4 years ago
private preloader: ProgressivePreloader = null;
4 years ago
4 years ago
private typingTimeouts: {[peerID: number]: number} = {};
private typingUsers: {[userID: number]: number} = {} // to peerID
4 years ago
private topbar: HTMLDivElement = null;
private chatInput: HTMLDivElement = null;
private scrolledAll: boolean;
private scrolledAllDown: boolean;
4 years ago
public contextMenu = document.getElementById('bubble-contextmenu') as HTMLDivElement;
private contextMenuPin = this.contextMenu.querySelector('.menu-pin') as HTMLDivElement;
private contextMenuEdit = this.contextMenu.querySelector('.menu-edit') as HTMLDivElement;
private contextMenuMsgID: number;
4 years ago
private popupDeleteMessage: {
popupEl?: HTMLDivElement,
deleteBothBtn?: HTMLButtonElement,
deleteMeBtn?: HTMLButtonElement,
cancelBtn?: HTMLButtonElement
} = {};
4 years ago
private setPeerPromise: Promise<boolean> = null;
4 years ago
public bubbleGroups = new BubbleGroups();
private scrolledDown = true;
4 years ago
4 years ago
constructor() {
4 years ago
/* if(!lottieLoader.loaded) {
lottieLoader.loadLottie();
} */
4 years ago
this.log = logger('IM');
4 years ago
this.chatInputC = new ChatInput();
4 years ago
4 years ago
this.preloader = new ProgressivePreloader(null, false);
4 years ago
this.popupDeleteMessage.popupEl = this.pageEl.querySelector('.popup-delete-message') as HTMLDivElement;
this.popupDeleteMessage.deleteBothBtn = this.popupDeleteMessage.popupEl.querySelector('.popup-delete-both') as HTMLButtonElement;
this.popupDeleteMessage.deleteMeBtn = this.popupDeleteMessage.popupEl.querySelector('.popup-delete-me') as HTMLButtonElement;
this.popupDeleteMessage.cancelBtn = this.popupDeleteMessage.popupEl.querySelector('.popup-close') as HTMLButtonElement;
4 years ago
apiManager.getUserID().then((id) => {
this.myID = $rootScope.myID = id;
4 years ago
});
4 years ago
this.topbar = document.getElementById('topbar') as HTMLDivElement;
this.chatInput = document.getElementById('chat-input') as HTMLDivElement;
4 years ago
4 years ago
$rootScope.$on('user_auth', (e: CustomEvent) => {
let userAuth = e.detail;
this.myID = $rootScope.myID = userAuth ? userAuth.id : 0;
4 years ago
});
4 years ago
// will call when message is sent (only 1)
4 years ago
$rootScope.$on('history_append', (e: CustomEvent) => {
let details = e.detail;
4 years ago
this.renderNewMessagesByIDs([details.messageID]);
4 years ago
});
4 years ago
// will call when sent for update pos
$rootScope.$on('history_update', (e: CustomEvent) => {
let details = e.detail;
4 years ago
if(details.mid && details.peerID == this.peerID) {
let mid = details.mid;
4 years ago
let bubble = this.bubbles[mid];
if(!bubble) return;
4 years ago
let message = appMessagesManager.getMessage(mid);
//this.log('history_update', this.bubbles[mid], mid, message);
4 years ago
this.renderMessage(message, false, false, bubble);
}
});
4 years ago
4 years ago
$rootScope.$on('history_multiappend', (e: CustomEvent) => {
let msgIDsByPeer = e.detail;
if(!(this.peerID in msgIDsByPeer)) return;
4 years ago
4 years ago
let msgIDs = msgIDsByPeer[this.peerID];
4 years ago
this.renderNewMessagesByIDs(msgIDs);
});
4 years ago
$rootScope.$on('history_delete', (e: CustomEvent) => {
let detail: {
peerID: string,
msgs: {[x: number]: boolean}
} = e.detail;
4 years ago
this.deleteMessagesByIDs(Object.keys(detail.msgs).map(s => +s));
4 years ago
});
4 years ago
// Calls when message successfully sent and we have an ID
4 years ago
$rootScope.$on('message_sent', (e: CustomEvent) => {
let {tempID, mid} = e.detail;
4 years ago
4 years ago
////this.log('message_sent', e.detail);
4 years ago
4 years ago
let bubble = this.bubbles[tempID];
if(bubble) {
this.bubbles[mid] = bubble;
4 years ago
4 years ago
/////this.log('message_sent', bubble);
4 years ago
bubble.classList.remove('is-sending');
bubble.classList.add('is-sent');
4 years ago
this.bubbleGroups.removeBubble(bubble, tempID);
4 years ago
delete this.bubbles[tempID];
} else {
this.log.warn('message_sent there is no bubble', e.detail);
4 years ago
}
4 years ago
4 years ago
let length = this.unreadOut.length;
for(let i = 0; i < length; i++) {
if(this.unreadOut[i] == tempID) {
this.unreadOut[i] = mid;
}
}
});
4 years ago
$rootScope.$on('message_edit', (e: CustomEvent) => {
let {peerID, mid, id, justMedia} = e.detail;
4 years ago
if(peerID != this.peerID) return;
4 years ago
let bubble = this.bubbles[mid];
if(!bubble) return;
4 years ago
let message = appMessagesManager.getMessage(mid);
this.renderMessage(message, true, false, bubble, false);
});
4 years ago
4 years ago
$rootScope.$on('messages_downloaded', (e: CustomEvent) => {
let mids: number[] = e.detail;
4 years ago
mids.forEach(mid => {
if(this.pinnedMsgID == mid) {
let message = appMessagesManager.getMessage(mid);
4 years ago
/////this.log('setting pinned message', message);
this.pinnedMessageContainer.dataset.mid = '' + mid;
this.pinnedMessageContainer.style.display = '';
this.pinnedMessageContent.innerHTML = RichTextProcessor.wrapEmojiText(message.message);
}
4 years ago
4 years ago
this.needUpdate.forEachReverse((obj, idx) => {
if(obj.replyMid == mid) {
let {mid, replyMid} = this.needUpdate.splice(idx, 1)[0];
4 years ago
//this.log('messages_downloaded', mid, replyMid, i, this.needUpdate, this.needUpdate.length, mids, this.bubbles[mid]);
let bubble = this.bubbles[mid];
if(!bubble) return;
4 years ago
let message = appMessagesManager.getMessage(mid);
4 years ago
let repliedMessage = appMessagesManager.getMessage(replyMid);
if(repliedMessage.deleted) { // чтобы не пыталось бесконечно загрузить удалённое сообщение
delete message.reply_to_mid; // WARNING!
}
4 years ago
this.renderMessage(message, true, false, bubble, false);
//this.renderMessage(message, true, true, bubble, false);
}
4 years ago
});
});
4 years ago
});
4 years ago
4 years ago
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
let update = e.detail;
4 years ago
this.handleUpdate(update);
});
4 years ago
4 years ago
window.addEventListener('blur', () => {
lottieLoader.checkAnimations(true);
4 years ago
4 years ago
this.offline = true;
this.updateStatus();
clearInterval(this.updateStatusInterval);
window.addEventListener('focus', () => {
lottieLoader.checkAnimations(false);
4 years ago
4 years ago
this.offline = false;
this.updateStatus();
this.updateStatusInterval = window.setInterval(() => this.updateStatus(), 50e3);
}, {once: true});
});
4 years ago
4 years ago
(this.pageEl.querySelector('.person') as HTMLDivElement).addEventListener('click', (e) => {
appSidebarRight.toggleSidebar(true);
});
4 years ago
4 years ago
this.chatInner.addEventListener('click', (e) => {
let target = e.target as HTMLElement;
let bubble: HTMLDivElement = null;
try {
bubble = findUpClassName(e.target, 'bubble');
} catch(err) {}
4 years ago
if(!bubble) return;
4 years ago
if(['IMG', 'VIDEO', 'SVG', 'DIV', 'image'].indexOf(target.tagName) === -1) target = findUpTag(target, 'DIV');
4 years ago
if(target.tagName == 'DIV') {
if(target.classList.contains('forward')) {
let savedFrom = bubble.dataset.savedFrom;
let splitted = savedFrom.split('_');
let peerID = +splitted[0];
let msgID = +splitted[1];
4 years ago
////this.log('savedFrom', peerID, msgID);
this.setPeer(peerID, msgID, true);
return;
} else if(target.classList.contains('user-avatar') || target.classList.contains('name')) {
let peerID = +target.dataset.peerID;
if(!isNaN(peerID)) {
this.setPeer(peerID);
}
4 years ago
return;
}
4 years ago
let isReplyClick = false;
4 years ago
try {
4 years ago
isReplyClick = !!findUpClassName(e.target, 'reply');
} catch(err) {}
4 years ago
if(isReplyClick && bubble.classList.contains('is-reply')/* || bubble.classList.contains('forwarded') */) {
let originalMessageID = +bubble.getAttribute('data-original-mid');
this.setPeer(this.peerID, originalMessageID);
}
} else if(bubble.classList.contains('round')) {
} else if(target.tagName == 'IMG' && target.parentElement.classList.contains('user-avatar')) {
let peerID = +target.parentElement.dataset.peerID;
4 years ago
if(!isNaN(peerID)) {
this.setPeer(peerID);
}
} else if(((target.tagName == 'IMG' || target.tagName == 'image') && !target.classList.contains('emoji')) || target.tagName == 'VIDEO') {
let messageID = 0;
for(let mid in this.bubbles) {
if(this.bubbles[mid] == bubble) {
messageID = +mid;
break;
}
}
let message = appMessagesManager.getMessage(messageID);
if(!message) {
this.log.warn('no message by messageID:', messageID);
return;
}
4 years ago
let ids = Object.keys(this.bubbles).map(k => +k).filter(id => {
if(!this.scrollable.visibleElements.find(e => e.element == this.bubbles[id])) return false;
let message = appMessagesManager.getMessage(id);
4 years ago
return message.media && (message.media.photo || (message.media.document && (message.media.document.type == 'video' || message.media.document.type == 'gif')) || (message.media.webpage && (message.media.webpage.document || message.media.webpage.photo)));
}).sort();
let idx = ids.findIndex(i => i == messageID);
let targets = ids.map(id => ({
//element: (this.bubbles[id].querySelector('img, video') || this.bubbles[id].querySelector('image')) as HTMLElement,
element: this.bubbles[id].querySelector('img, video, .bubble__media-container') as HTMLElement,
mid: id
}));
4 years ago
4 years ago
/////this.log('ids', ids, idx, this.bubbles[prev], this.bubbles[next]);
4 years ago
appMediaViewer.openMedia(message, targets[idx].element, true,
this.scroll.parentElement, targets.slice(0, idx), targets.slice(idx + 1));
4 years ago
//appMediaViewer.openMedia(message, target as HTMLImageElement);
4 years ago
}
4 years ago
4 years ago
//console.log('chatInner click', e);
});
4 years ago
4 years ago
this.searchBtn.addEventListener('click', (e) => {
if(this.peerID) {
appSidebarLeft.beginSearch(this.peerID);
}
});
4 years ago
4 years ago
this.pinnedMessageContainer.addEventListener('click', (e) => {
e.preventDefault();
e.cancelBubble = true;
4 years ago
4 years ago
let mid = +this.pinnedMessageContainer.getAttribute('data-mid');
this.setPeer(this.peerID, mid);
});
4 years ago
this.btnMenuMute.addEventListener('click', () => this.mutePeer());
this.btnMute.addEventListener('click', () => this.mutePeer());
4 years ago
let onKeyDown = (e: KeyboardEvent) => {
let target = e.target as HTMLElement;
4 years ago
//if(target.tagName == 'INPUT') return;
4 years ago
//this.log('onkeydown', e);
4 years ago
if(this.chatInputC.attachMediaPopUp.container.classList.contains('active')) {
if(target.tagName != 'INPUT') {
this.chatInputC.attachMediaPopUp.captionInput.focus();
}
4 years ago
if(e.key == 'Enter') {
this.chatInputC.attachMediaPopUp.sendBtn.click();
} else if(e.key == 'Escape') {
this.chatInputC.attachMediaPopUp.container.classList.remove('active');
}
4 years ago
return;
}
4 years ago
4 years ago
if(e.key == 'Meta' || e.key == 'Control') {
return;
} else if(e.key == 'c' && (e.ctrlKey || e.metaKey) && target.tagName != 'INPUT') {
return;
}
4 years ago
if(e.target != this.chatInputC.messageInput && target.tagName != 'INPUT') {
this.chatInputC.messageInput.focus();
placeCaretAtEnd(this.chatInputC.messageInput);
}
};
4 years ago
document.body.addEventListener('keydown', onKeyDown);
4 years ago
this.chatInner.addEventListener('contextmenu', e => {
let bubble: HTMLDivElement = null;
4 years ago
try {
bubble = findUpClassName(e.target, 'bubble__container');
} catch(e) {}
4 years ago
if(bubble) {
bubble = bubble.parentElement as HTMLDivElement; // bc container
e.preventDefault();
e.cancelBubble = true;
let msgID = 0;
for(let id in this.bubbles) {
if(this.bubbles[id] === bubble) {
msgID = +id;
break;
}
}
4 years ago
if(!msgID) return;
4 years ago
if(this.myID == this.peerID || (this.peerID < 0 && !appPeersManager.isChannel(this.peerID) && !appPeersManager.isMegagroup(this.peerID))) {
this.contextMenuPin.style.display = '';
} else this.contextMenuPin.style.display = 'none';
4 years ago
this.contextMenuMsgID = msgID;
4 years ago
let side = bubble.classList.contains('is-in') ? 'left' : 'right';
4 years ago
this.contextMenuEdit.style.display = side == 'right' ? '' : 'none';
4 years ago
this.contextMenu.classList.remove('bottom-left', 'bottom-right');
this.contextMenu.classList.add(side == 'left' ? 'bottom-right' : 'bottom-left');
4 years ago
let {clientX, clientY} = e;
4 years ago
this.contextMenu.style.left = (side == 'right' ? clientX - this.contextMenu.scrollWidth : clientX) + 'px';
if((clientY + this.contextMenu.scrollHeight) > window.innerHeight) {
this.contextMenu.style.top = (window.innerHeight - this.contextMenu.scrollHeight) + 'px';
} else {
this.contextMenu.style.top = clientY + 'px';
}
4 years ago
//this.contextMenu.classList.add('active');
openBtnMenu(this.contextMenu);
4 years ago
4 years ago
/////this.log('contextmenu', e, bubble, msgID, side);
}
});
4 years ago
this.contextMenu.querySelector('.menu-copy').addEventListener('click', () => {
let message = appMessagesManager.getMessage(this.contextMenuMsgID);
4 years ago
let str = message ? message.message : '';
4 years ago
var textArea = document.createElement("textarea");
textArea.value = str;
textArea.style.position = "fixed"; //avoid scrolling to bottom
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
4 years ago
try {
document.execCommand('copy');
} catch (err) {
console.error('Oops, unable to copy', err);
}
4 years ago
document.body.removeChild(textArea);
});
4 years ago
this.contextMenu.querySelector('.menu-delete').addEventListener('click', () => {
if(this.peerID == this.myID) {
this.popupDeleteMessage.deleteBothBtn.style.display = 'none';
this.popupDeleteMessage.deleteMeBtn.innerText = 'DELETE';
} else {
this.popupDeleteMessage.deleteBothBtn.style.display = '';
this.popupDeleteMessage.deleteMeBtn.innerText = 'DELETE JUST FOR ME';
4 years ago
if(this.peerID > 0) {
let title = appPeersManager.getPeerTitle(this.peerID);
this.popupDeleteMessage.deleteBothBtn.innerHTML = 'DELETE FOR ME AND ' + title;
} else {
this.popupDeleteMessage.deleteBothBtn.innerText = 'DELETE FOR ALL';
}
}
4 years ago
this.popupDeleteMessage.popupEl.classList.add('active');
});
this.contextMenu.querySelector('.menu-reply').addEventListener('click', () => {
let message = appMessagesManager.getMessage(this.contextMenuMsgID);
4 years ago
this.chatInputC.setTopInfo(appPeersManager.getPeerTitle(message.fromID, true), message.message, undefined, message.media);
this.chatInputC.replyToMsgID = this.contextMenuMsgID;
this.chatInputC.editMsgID = 0;
});
4 years ago
this.contextMenuEdit.addEventListener('click', () => {
let message = appMessagesManager.getMessage(this.contextMenuMsgID);
4 years ago
this.chatInputC.setTopInfo('Editing', message.message, message.message, message.media);
this.chatInputC.replyToMsgID = 0;
this.chatInputC.editMsgID = this.contextMenuMsgID;
});
4 years ago
this.contextMenuPin.addEventListener('click', () => {
apiManager.invokeApi('messages.updatePinnedMessage', {
flags: 0,
peer: appPeersManager.getInputPeerByID(this.peerID),
id: this.contextMenuMsgID
}).then(updates => {
4 years ago
/////this.log('pinned updates:', updates);
apiUpdatesManager.processUpdateMessage(updates);
});
});
4 years ago
this.popupDeleteMessage.deleteBothBtn.addEventListener('click', () => {
this.deleteMessages(true);
this.popupDeleteMessage.cancelBtn.click();
});
4 years ago
this.popupDeleteMessage.deleteMeBtn.addEventListener('click', () => {
this.deleteMessages(false);
this.popupDeleteMessage.cancelBtn.click();
});
this.goDownBtn.addEventListener('click', () => {
let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0];
4 years ago
if(dialog) {
this.setPeer(this.peerID, dialog.top_message);
} else {
this.log('will scroll down 3');
this.scroll.scrollTop = this.scroll.scrollHeight;
}
});
4 years ago
4 years ago
this.updateStatusInterval = window.setInterval(() => this.updateStatus(), 50e3);
this.updateStatus();
setInterval(() => this.setPeerStatus(), 60e3);
4 years ago
4 years ago
this.setScroll();
apiUpdatesManager.attach();
4 years ago
}
4 years ago
public deleteMessages(revoke = false) {
let flags = revoke ? 1 : 0;
let ids = [this.contextMenuMsgID];
4 years ago
apiManager.invokeApi('messages.deleteMessages', {
flags: flags,
revoke: revoke,
id: ids
}).then((affectedMessages: any) => {
4 years ago
/////this.log('deleted messages:', affectedMessages);
4 years ago
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updatePts',
pts: affectedMessages.pts,
pts_count: affectedMessages.pts_count
}
});
4 years ago
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updateDeleteMessages',
messages: ids
}
});
});
}
4 years ago
4 years ago
public updateStatus() {
if(!this.myID) return Promise.resolve();
4 years ago
4 years ago
appUsersManager.setUserStatus(this.myID, this.offline);
4 years ago
return apiManager.invokeApi('account.updateStatus', {offline: this.offline});
4 years ago
}
4 years ago
public loadMoreHistory(top: boolean) {
this.log('loadMoreHistory', top);
4 years ago
if(!this.peerID || testScroll || this.setPeerPromise || (top && this.getHistoryTopPromise) || (!top && this.getHistoryBottomPromise)) return;
let history = Object.keys(this.bubbles).map(id => +id).sort();
if(!history.length) return;
/* let history = appMessagesManager.historiesStorage[this.peerID].history;
let length = history.length; */
// filter negative ids
let lastBadIndex = -1;
for(let i = 0; i < history.length; ++i) {
if(history[i] <= 0) lastBadIndex = i;
else break;
}
if(lastBadIndex != -1) {
history = history.slice(lastBadIndex + 1);
}
if(top && !this.scrolledAll) {
this.scrollable.lock('both');
this.log('Will load more (up) history by id:', history[0], 'maxID:', history[history.length - 1], history);
4 years ago
/* false && */this.getHistory(history[0], true);
}
if(this.scrolledAllDown) return;
let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0];
/* if(!dialog) {
this.log.warn('no dialog for load history');
return;
} */
// if scroll down after search
if(!top && (!dialog || history.indexOf(dialog.top_message) === -1)) {
this.scrollable.lock('both');
this.log('Will load more (down) history by maxID:', history[history.length - 1], history);
4 years ago
/* false && */this.getHistory(history[history.length - 1], false, true);
4 years ago
}
}
4 years ago
public onScroll() {
let readed: number[] = [];
4 years ago
4 years ago
this.unreaded.forEachReverse((msgID, idx) => {
4 years ago
let bubble = this.bubbles[msgID];
4 years ago
4 years ago
if(isElementInViewport(bubble)) {
readed.push(msgID);
4 years ago
this.unreaded.splice(idx, 1);
4 years ago
}
4 years ago
});
4 years ago
lottieLoader.checkAnimations(false, 'chat');
4 years ago
4 years ago
if(readed.length) {
let max = Math.max(...readed);
4 years ago
let min = Math.min(...readed);
4 years ago
if(this.peerID < 0) {
max = appMessagesIDsManager.getMessageIDInfo(max)[0];
min = appMessagesIDsManager.getMessageIDInfo(min)[0];
}
4 years ago
4 years ago
//appMessagesManager.readMessages(readed);
appMessagesManager.readHistory(this.peerID, max, min).catch((err: any) => {
this.log.error('readHistory err:', err);
appMessagesManager.readHistory(this.peerID, max, min);
});
4 years ago
}
4 years ago
4 years ago
if(this.scroll.scrollHeight - (this.scroll.scrollTop + this.scroll.offsetHeight) == 0/* <= 5 */) {
this.scroll.parentElement.classList.add('scrolled-down');
this.scrolledDown = true;
4 years ago
} else if(this.scroll.parentElement.classList.contains('scrolled-down')) {
this.scroll.parentElement.classList.remove('scrolled-down');
this.scrolledDown = false;
4 years ago
}
}
4 years ago
4 years ago
public setScroll() {
this.scrollable = new Scrollable(this.bubblesContainer, 'y', 750, 'IM', this.chatInner/* 1500 */, 300);
4 years ago
this.scroll = this.scrollable.container;
this.bubblesContainer.append(this.goDownBtn);
4 years ago
4 years ago
//this.scrollable.setVirtualContainer(this.chatInner);
4 years ago
this.scrollable.onScrolledTop = () => this.loadMoreHistory(true);
this.scrollable.onScrolledBottom = () => this.loadMoreHistory(false);
this.scroll.addEventListener('scroll', this.onScroll.bind(this));
this.scroll.parentElement.classList.add('scrolled-down');
4 years ago
}
4 years ago
4 years ago
public setPeerStatus(needClear = false) {
4 years ago
if(!this.myID) return;
4 years ago
4 years ago
if(this.peerID < 0) { // not human
let chat = appPeersManager.getPeer(this.peerID);
let isChannel = appPeersManager.isChannel(this.peerID) && !appPeersManager.isMegagroup(this.peerID);
4 years ago
4 years ago
this.subtitleEl.classList.remove('online');
appSidebarRight.profileElements.subtitle.classList.remove('online');
4 years ago
///////this.log('setPeerStatus', chat);
4 years ago
if(needClear) {
this.subtitleEl.innerText = appSidebarRight.profileElements.subtitle.innerText = '';
}
Promise.all([
appPeersManager.isMegagroup(this.peerID) ? apiManager.invokeApi('messages.getOnlines', {
peer: appPeersManager.getInputPeerByID(this.peerID)
}) as Promise<any> : Promise.resolve(),
// will redirect if wrong
appProfileManager.getChatFull(chat.id)
]).then(results => {
let [chatOnlines, chatInfo] = results;
4 years ago
let onlines = chatOnlines ? chatOnlines.onlines : 1;
4 years ago
4 years ago
///////////this.log('chatInfo res:', chatInfo);
4 years ago
if(chatInfo.pinned_msg_id) { // request pinned message
this.pinnedMsgID = chatInfo.pinned_msg_id;
appMessagesManager.wrapSingleMessage(chatInfo.pinned_msg_id);
4 years ago
}
4 years ago
let participants_count = chatInfo.participants_count || chatInfo.participants.participants.length;
4 years ago
let subtitle = numberWithCommas(participants_count) + ' ' + (isChannel ? 'subscribers' : 'members');
4 years ago
if(onlines > 1) {
subtitle += ', ' + numberWithCommas(onlines) + ' online';
}
4 years ago
4 years ago
this.subtitleEl.innerText = appSidebarRight.profileElements.subtitle.innerText = subtitle;
});
} else if(!appUsersManager.isBot(this.peerID)) { // user
let user = appUsersManager.getUser(this.peerID);
4 years ago
4 years ago
if(this.myID == this.peerID) {
this.subtitleEl.innerText = appSidebarRight.profileElements.subtitle.innerText = '';
} else if(user && user.status) {
4 years ago
let subtitle = '';
switch(user.status._) {
4 years ago
case 'userStatusRecently': {
subtitle += 'last seen recently';
break;
4 years ago
}
4 years ago
case 'userStatusOffline': {
subtitle = 'last seen ';
4 years ago
4 years ago
let date = user.status.was_online;
let now = Date.now() / 1000;
if((now - date) < 60) {
subtitle += ' just now';
} else if((now - date) < 3600) {
subtitle += ((now - date) / 60 | 0) + ' minutes ago';
} else if(now - date < 86400) {
subtitle += ((now - date) / 3600 | 0) + ' hours ago';
} else {
let d = new Date(date * 1000);
subtitle += ('0' + d.getDate()).slice(-2) + '.' + ('0' + (d.getMonth() + 1)).slice(-2) + ' at ' +
('0' + d.getHours()).slice(-2) + ':' + ('0' + d.getMinutes()).slice(-2);
}
break;
}
case 'userStatusOnline': {
this.subtitleEl.classList.add('online');
appSidebarRight.profileElements.subtitle.classList.add('online');
subtitle = 'online';
break;
}
4 years ago
}
4 years ago
4 years ago
appSidebarRight.profileElements.subtitle.innerText = subtitle;
4 years ago
4 years ago
if(this.typingUsers[this.peerID] == this.peerID) {
this.subtitleEl.innerText = 'typing...';
this.subtitleEl.classList.add('online');
4 years ago
} else {
this.subtitleEl.innerText = subtitle;
if(subtitle != 'online') {
this.subtitleEl.classList.remove('online');
appSidebarRight.profileElements.subtitle.classList.remove('online');
}
}
4 years ago
}
}
}
4 years ago
4 years ago
public cleanup() {
4 years ago
////console.time('appImManager cleanup');
4 years ago
this.peerID = $rootScope.selectedPeerID = 0;
this.scrolledAll = false;
this.scrolledAllDown = false;
this.muted = false;
4 years ago
4 years ago
/* for(let i in this.bubbles) {
4 years ago
let bubble = this.bubbles[i];
bubble.remove();
4 years ago
} */
4 years ago
this.bubbles = {};
this.dateMessages = {};
4 years ago
this.bubbleGroups.cleanup();
4 years ago
this.unreaded = [];
this.unreadOut = [];
this.needUpdate.length = 0;
4 years ago
this.lazyLoadQueue.clear();
4 years ago
// clear input
this.chatInputC.messageInput.innerHTML = '';
this.chatInputC.replyElements.cancelBtn.click();
4 years ago
// clear messages
4 years ago
this.chatInner.innerHTML = '';
4 years ago
lottieLoader.checkAnimations(false, 'chat', true);
this.getHistoryTopPromise = this.getHistoryBottomPromise = undefined;
4 years ago
4 years ago
//this.scrollable.setVirtualContainer(this.chatInner);
this.scrollable.setVirtualContainer(null);
4 years ago
////console.timeEnd('appImManager cleanup');
4 years ago
}
4 years ago
4 years ago
public setPeer(peerID: number, lastMsgID = 0, forwarding = false, fromClick = false) {
//console.time('appImManager setPeer');
//console.time('appImManager setPeer pre promise');
////console.time('appImManager: pre render start');
if(peerID == 0) {
appSidebarRight.toggleSidebar(false);
this.topbar.style.display = this.chatInput.style.display = this.goDownBtn.style.display = 'none';
this.cleanup();
4 years ago
return false;
}
4 years ago
4 years ago
let samePeer = this.peerID == peerID;
4 years ago
if(this.setPeerPromise && samePeer) return this.setPeerPromise;
4 years ago
if(lastMsgID) {
appMessagesManager.readHistory(peerID, lastMsgID); // lol
}
4 years ago
if(samePeer) {
if(!testScroll && !lastMsgID) {
4 years ago
return true;
}
4 years ago
4 years ago
if(this.bubbles[lastMsgID]) {
let dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
4 years ago
if(dialog && lastMsgID == dialog.top_message) {
this.log('will scroll down', this.scroll.scrollTop, this.scroll.scrollHeight);
this.scroll.scrollTop = this.scroll.scrollHeight;
} else {
4 years ago
//this.bubbles[lastMsgID].scrollIntoView();
this.scrollable.scrollIntoView(this.bubbles[lastMsgID]);
}
4 years ago
4 years ago
return true;
4 years ago
}
}
4 years ago
4 years ago
// clear
this.cleanup();
4 years ago
4 years ago
// set new
this.peerID = $rootScope.selectedPeerID = peerID;
4 years ago
4 years ago
// no dialog
/* if(!appMessagesManager.getDialogByPeerID(this.peerID).length) {
4 years ago
this.log.error('No dialog by peerID:', this.peerID);
return Promise.reject();
} */
4 years ago
let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0] || null;
4 years ago
//////this.log('setPeer peerID:', this.peerID, dialog, lastMsgID);
appProfileManager.putPhoto(this.avatarEl, this.peerID);
appProfileManager.putPhoto(appSidebarRight.profileElements.avatar, this.peerID);
this.firstTopMsgID = dialog ? dialog.top_message : 0;
4 years ago
this.chatInner.style.visibility = 'hidden';
this.chatInput.style.display = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID) ? 'none' : '';
this.topbar.style.display = '';
if(appPeersManager.isAnyGroup(peerID)) this.chatInner.classList.add('is-chat');
else this.chatInner.classList.remove('is-chat');
4 years ago
window.requestAnimationFrame(() => {
//this.chatInner.style.visibility = 'hidden';
4 years ago
let title = '';
if(this.peerID == this.myID) title = 'Saved Messages';
else title = appPeersManager.getPeerTitle(this.peerID);
//this.titleEl.innerHTML = appSidebarRight.profileElements.name.innerHTML = dom.titleSpan.innerHTML;
this.titleEl.innerHTML = appSidebarRight.profileElements.name.innerHTML = title;
this.pinnedMessageContainer.style.display = 'none';
this.goDownBtn.style.display = '';
//this.topbar.style.display = this.goDownBtn.style.display = '';
//this.chatInput.style.display = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID) ? 'none' : '';
4 years ago
//appSidebarRight.toggleSidebar(true);
//if(appPeersManager.isAnyGroup(peerID)) this.chatInner.classList.add('is-chat');
//else this.chatInner.classList.remove('is-chat');
4 years ago
if(!fromClick) {
if(!samePeer && appDialogsManager.lastActiveListElement) {
appDialogsManager.lastActiveListElement.classList.remove('active');
}
4 years ago
4 years ago
let dom = appDialogsManager.getDialogDom(this.peerID);
if(dom) {
appDialogsManager.lastActiveListElement = dom.listEl;
dom.listEl.classList.add('active');
}
}
4 years ago
this.setPeerStatus(true);
});
// add last message, bc in getHistory will load < max_id
let additionMsgID = 0;
if(lastMsgID && !forwarding) additionMsgID = lastMsgID;
else if(dialog && dialog.top_message) additionMsgID = dialog.top_message;
/* this.setPeerPromise = null;
this.preloader.detach();
return true; */
4 years ago
4 years ago
//////appSidebarRight.toggleSidebar(true);
//console.timeEnd('appImManager setPeer pre promise');
this.preloader.attach(this.bubblesContainer);
return this.setPeerPromise = Promise.all([
4 years ago
this.getHistory(forwarding ? lastMsgID + 1 : lastMsgID, true, false, additionMsgID).then(() => {
4 years ago
////this.log('setPeer removing preloader');
4 years ago
if(lastMsgID) {
if(!dialog || lastMsgID != dialog.top_message) {
let bubble = this.bubbles[lastMsgID];
if(bubble) this.bubbles[lastMsgID].scrollIntoView();
else this.log.warn('no bubble by lastMsgID:', lastMsgID);
} else {
this.log('will scroll down 2');
this.scroll.scrollTop = this.scroll.scrollHeight;
4 years ago
}
}
4 years ago
/* this.onScroll();
this.scrollable.onScroll();*/
4 years ago
this.preloader.detach();
4 years ago
this.chatInner.style.visibility = '';
//console.timeEnd('appImManager setPeer');
4 years ago
//setTimeout(() => {
4 years ago
//appSidebarRight.fillProfileElements();
4 years ago
//appSidebarRight.loadSidebarMedia(true);
//}, 500);
4 years ago
return true;
})/* .catch(err => {
this.log.error(err);
}) */,
4 years ago
4 years ago
appSidebarRight.fillProfileElements()/* ,
appSidebarRight.loadSidebarMedia(true) */
]).catch(err => {
this.log.error('setPeer promises error:', err);
this.preloader.detach();
return false;
4 years ago
}).then(res => {
if(this.peerID == peerID) {
this.setPeerPromise = null;
}
return !!res;
4 years ago
});
}
4 years ago
public setTyping(action: any): Promise<boolean> {
if(!this.peerID) return Promise.resolve(false);
4 years ago
if(typeof(action) == 'string') {
action = {_: action};
}
4 years ago
let input = appPeersManager.getInputPeerByID(this.peerID);
return apiManager.invokeApi('messages.setTyping', {
peer: input,
action: action
}) as Promise<boolean>;
}
4 years ago
4 years ago
public updateUnreadByDialog(dialog: any) {
let maxID = this.peerID == this.myID ? dialog.read_inbox_max_id : dialog.read_outbox_max_id;
4 years ago
4 years ago
///////this.log('updateUnreadByDialog', maxID, dialog, this.unreadOut);
4 years ago
4 years ago
this.unreadOut.forEachReverse((msgID, idx) => {
if(msgID > 0 && msgID <= maxID) {
4 years ago
let bubble = this.bubbles[msgID];
bubble.classList.remove('is-sent');
bubble.classList.add('is-read');
4 years ago
this.unreadOut.splice(idx, 1);
4 years ago
}
4 years ago
});
4 years ago
}
4 years ago
4 years ago
public deleteMessagesByIDs(msgIDs: number[]) {
msgIDs.forEach(id => {
if(this.firstTopMsgID == id) {
let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0];
4 years ago
if(dialog) {
4 years ago
///////this.log('setting firstTopMsgID after delete:', id, dialog.top_message, dialog);
this.firstTopMsgID = dialog.top_message;
}
}
4 years ago
4 years ago
if(!(id in this.bubbles)) return;
let bubble = this.bubbles[id];
delete this.bubbles[id];
4 years ago
this.scrollable.removeElement(bubble);
//bubble.remove();
4 years ago
});
4 years ago
4 years ago
lottieLoader.checkAnimations();
}
4 years ago
public renderNewMessagesByIDs(msgIDs: number[]) {
if(!this.bubbles[this.firstTopMsgID] && Object.keys(this.bubbles).length) { // seems search active
4 years ago
//////this.log('seems search is active, skipping render:', msgIDs);
4 years ago
return;
}
4 years ago
let scrolledDown = this.scrolledDown;
4 years ago
msgIDs.forEach((msgID: number) => {
let message = appMessagesManager.getMessage(msgID);
4 years ago
4 years ago
/////////this.log('got new message to append:', message);
4 years ago
4 years ago
//this.unreaded.push(msgID);
this.renderMessage(message);
});
if(scrolledDown) this.scrollable.scrollTop = this.scrollable.scrollHeight;
4 years ago
}
4 years ago
// reverse means top
public renderMessage(message: any, reverse = false, multipleRender = false, bubble: HTMLDivElement = null, updatePosition = true) {
4 years ago
/////this.log('message to render:', message);
if(message.deleted) return;
4 years ago
4 years ago
let peerID = this.peerID;
let our = message.fromID == this.myID;
4 years ago
4 years ago
let messageDiv = document.createElement('div');
messageDiv.classList.add('message');
4 years ago
4 years ago
//messageDiv.innerText = message.message;
4 years ago
let bubbleContainer: HTMLDivElement;
4 years ago
// bubble
if(!bubble) {
4 years ago
bubbleContainer = document.createElement('div');
bubbleContainer.classList.add('bubble__container');
4 years ago
bubble = document.createElement('div');
bubble.classList.add('bubble');
4 years ago
bubble.appendChild(bubbleContainer);
this.bubbles[+message.mid] = bubble;
} else {
bubble.className = 'bubble';
4 years ago
bubbleContainer = bubble.firstElementChild as HTMLDivElement;
bubbleContainer.innerHTML = '';
//bubble.innerHTML = '';
}
4 years ago
4 years ago
bubble.dataset.mid = message.mid;
4 years ago
if(message._ == 'messageService') {
bubble.className = 'bubble service';
let action = message.action;
let title = appPeersManager.getPeerTitle(message.fromID);
let name = document.createElement('div');
name.classList.add('name');
name.dataset.peerID = message.fromID;
name.innerHTML = title;
let _ = action._;
if(_ == "messageActionPhoneCall") {
_ += '.' + action.type;
}
// @ts-ignore
let str = (name.innerText ? name.outerHTML + ' ' : '') + langPack[_];
bubbleContainer.innerHTML = `<div class="service-msg">${str}</div>`;
4 years ago
/* if(!updatePosition) {
if(!multipleRender) {
this.scrollPosition.restore(); // лагает из-за этого
}
} else if(reverse) {
this.scrollable.prepend(bubble);
} else {
this.scrollable.append(bubble);
} */
4 years ago
4 years ago
return bubble;
4 years ago
}
4 years ago
// time section
4 years ago
4 years ago
let date = new Date(message.date * 1000);
4 years ago
let time = ('0' + date.getHours()).slice(-2) + ':' + ('0' + date.getMinutes()).slice(-2);
if(message.views) {
bubble.classList.add('channel-post');
time = formatNumber(message.views, 1) + ' <i class="tgico-channelviews"></i> ' + time;
}
4 years ago
if(message.edit_date) {
bubble.classList.add('is-edited');
time = '<i class="edited">edited</i> ' + time;
}
4 years ago
4 years ago
let timeSpan = document.createElement('span');
timeSpan.classList.add('time');
4 years ago
4 years ago
let timeInner = document.createElement('div');
timeInner.classList.add('inner', 'tgico');
timeInner.innerHTML = time;
4 years ago
4 years ago
let richText = RichTextProcessor.wrapRichText(message.message, {
entities: message.totalEntities
});
4 years ago
4 years ago
if(message.totalEntities) {
let emojiEntities = message.totalEntities.filter((e: any) => e._ == 'messageEntityEmoji');
let strLength = message.message.length;
let emojiStrLength = emojiEntities.reduce((acc: number, curr: any) => acc + curr.length, 0);
4 years ago
4 years ago
if(emojiStrLength == strLength && emojiEntities.length <= 3) {
let attachmentDiv = document.createElement('div');
attachmentDiv.classList.add('attachment');
4 years ago
4 years ago
attachmentDiv.innerHTML = richText;
4 years ago
bubble.classList.add('is-message-empty', 'emoji-' + emojiEntities.length + 'x', 'emoji-big');
4 years ago
4 years ago
bubbleContainer.append(attachmentDiv);
4 years ago
} else {
messageDiv.innerHTML = richText;
}
4 years ago
4 years ago
/* if(strLength == emojiStrLength) {
messageDiv.classList.add('emoji-only');
messageDiv.classList.add('message-empty');
} */
} else {
messageDiv.innerHTML = richText;
}
4 years ago
4 years ago
timeSpan.appendChild(timeInner);
messageDiv.append(timeSpan);
4 years ago
bubbleContainer.prepend(messageDiv);
//bubble.prepend(timeSpan, messageDiv); // that's bad
4 years ago
4 years ago
if(our) {
if(message.pFlags.unread || message.mid < 0) this.unreadOut.push(message.mid); // message.mid < 0 added 11.02.2020
let status = '';
if(message.mid < 0) status = 'is-sending';
else status = message.pFlags.unread ? 'is-sent' : 'is-read';
4 years ago
bubble.classList.add(status);
} else {
//this.log('not our message', message, message.pFlags.unread);
4 years ago
if(message.pFlags.unread) {
this.unreaded.push(message.mid);
}
4 years ago
}
4 years ago
4 years ago
// media
if(message.media) {
let attachmentDiv = document.createElement('div');
attachmentDiv.classList.add('attachment');
4 years ago
4 years ago
if(!message.message) {
bubble.classList.add('is-message-empty');
4 years ago
}
4 years ago
4 years ago
let processingWebPage = false;
switch(message.media._) {
case 'messageMediaPending': {
let pending = message.media;
let preloader = pending.preloader as ProgressivePreloader;
4 years ago
switch(pending.type) {
case 'photo': {
if(pending.size < 5e6) {
let img = new Image();
img.src = URL.createObjectURL(pending.file);
4 years ago
let {w, h} = calcImageInBox(pending.w, pending.h, 380, 380);
4 years ago
attachmentDiv.style.width = w + 'px';
attachmentDiv.style.height = h + 'px';
4 years ago
attachmentDiv.append(img);
preloader.attach(attachmentDiv, false);
bubble.classList.add('hide-name', 'photo');
4 years ago
break;
}
}
4 years ago
case 'audio':
case 'document': {
4 years ago
let docDiv = wrapDocument(pending, false, true);
4 years ago
let icoDiv = docDiv.querySelector('.document-ico');
preloader.attach(icoDiv, false);
4 years ago
bubble.classList.remove('is-message-empty');
messageDiv.append(docDiv);
processingWebPage = true;
break;
}
4 years ago
}
4 years ago
break;
}
4 years ago
4 years ago
case 'messageMediaPhoto': {
let photo = message.media.photo;
4 years ago
////////this.log('messageMediaPhoto', photo);
4 years ago
bubble.classList.add('hide-name', 'photo');
4 years ago
wrapPhoto(photo.id, message, attachmentDiv, undefined, undefined, true, our, this.lazyLoadQueue, () => {
return this.peerID == peerID;
});
4 years ago
break;
}
4 years ago
4 years ago
case 'messageMediaWebPage': {
processingWebPage = true;
4 years ago
4 years ago
let webpage = message.media.webpage;
4 years ago
////////this.log('messageMediaWebPage', webpage);
4 years ago
if(webpage._ == 'webPageEmpty') {
break;
}
4 years ago
4 years ago
bubble.classList.add('webpage');
4 years ago
4 years ago
let box = document.createElement('div');
box.classList.add('box', 'web');
4 years ago
4 years ago
let quote = document.createElement('div');
quote.classList.add('quote');
4 years ago
4 years ago
let nameEl = document.createElement('a');
nameEl.classList.add('name');
4 years ago
4 years ago
let titleDiv = document.createElement('div');
titleDiv.classList.add('title');
4 years ago
4 years ago
let textDiv = document.createElement('div');
textDiv.classList.add('text');
4 years ago
4 years ago
let preview: HTMLDivElement = null;
if(webpage.photo || webpage.document) {
preview = document.createElement('div');
preview.classList.add('preview');
}
4 years ago
4 years ago
let doc: any = null;
if(webpage.document) {
doc = webpage.document;
4 years ago
4 years ago
if(doc.type == 'gif' || doc.type == 'video') {
//if(doc.size <= 20e6) {
4 years ago
bubble.classList.add('video');
wrapVideo({
doc,
container: preview,
message,
justLoader: true,
preloader: null,
round: false,
boxWidth: 380,
boxHeight: 300,
lazyLoadQueue: this.lazyLoadQueue,
middleware: () => {
return this.peerID == peerID;
}
});
//}
4 years ago
} else {
doc = null;
}
}
4 years ago
4 years ago
if(webpage.photo && !doc) {
bubble.classList.add('photo');
wrapPhoto(webpage.photo.id, message, preview, 380, 300, false, null, this.lazyLoadQueue, () => {
return this.peerID == peerID;
});
4 years ago
}
4 years ago
4 years ago
if(preview) {
quote.append(preview);
}
4 years ago
4 years ago
nameEl.setAttribute('target', '_blank');
nameEl.href = webpage.url || '#';
nameEl.innerHTML = webpage.site_name ? RichTextProcessor.wrapEmojiText(webpage.site_name) : '';
4 years ago
4 years ago
if(webpage.description) {
textDiv.innerHTML = RichTextProcessor.wrapRichText(webpage.description);
}
4 years ago
if(webpage.title) {
titleDiv.innerHTML = RichTextProcessor.wrapRichText(webpage.title);
}
4 years ago
4 years ago
quote.append(nameEl, titleDiv, textDiv);
box.append(quote);
4 years ago
//bubble.prepend(box);
4 years ago
bubbleContainer.prepend(timeSpan, box);
4 years ago
4 years ago
//this.log('night running', bubble.scrollHeight);
4 years ago
4 years ago
break;
}
4 years ago
4 years ago
case 'messageMediaDocument': {
let doc = message.media.document;
4 years ago
this.log('messageMediaDocument', doc, bubble);
4 years ago
if(doc.sticker/* && doc.size <= 1e6 */) {
4 years ago
bubble.classList.add('sticker');
4 years ago
4 years ago
if(doc.animated) {
bubble.classList.add('sticker-animated');
}
4 years ago
appPhotosManager.setAttachmentSize(doc, attachmentDiv, undefined, undefined, true);
4 years ago
//let preloader = new ProgressivePreloader(attachmentDiv, false);
4 years ago
bubbleContainer.style.height = attachmentDiv.style.height;
bubbleContainer.style.width = attachmentDiv.style.width;
4 years ago
//appPhotosManager.setAttachmentSize(doc, bubble);
wrapSticker(doc, attachmentDiv, () => {
4 years ago
if(this.peerID != peerID) {
this.log.warn('peer changed, canceling sticker attach');
return false;
}
4 years ago
4 years ago
return true;
}, this.lazyLoadQueue, 'chat', false, !!message.pending || !multipleRender);
4 years ago
break;
} else if((doc.type == 'video' || doc.type == 'gif') && doc.size <= 20e6) {
4 years ago
this.log('never get free 2', doc);
if(doc.type == 'round') {
bubble.classList.add('round');
}
4 years ago
bubble.classList.add('hide-name', 'video');
//wrapVideo.call(this, doc, attachmentDiv, message, true, null, false, doc.type == 'round', 380, 380, doc.type != 'round', our);
wrapVideo({
doc,
container: attachmentDiv,
message,
justLoader: true,
preloader: null,
round: doc.type == 'round',
boxWidth: 380,
boxHeight: 380,
withTail: doc.type != 'round',
isOut: our,
lazyLoadQueue: this.lazyLoadQueue,
middleware: () => {
return this.peerID == peerID;
}
});
4 years ago
break;
} else if(doc.mime_type == 'audio/ogg') {
let docDiv = wrapDocument(doc);
bubble.classList.remove('is-message-empty');
4 years ago
bubble.classList.add('bubble-audio');
messageDiv.append(docDiv);
processingWebPage = true;
4 years ago
break;
} else {
let docDiv = wrapDocument(doc);
4 years ago
bubble.classList.remove('is-message-empty');
4 years ago
messageDiv.append(docDiv);
processingWebPage = true;
4 years ago
4 years ago
break;
}
}
4 years ago
4 years ago
default:
bubble.classList.remove('is-message-empty');
4 years ago
messageDiv.innerHTML = 'unrecognized media type: ' + message.media._;
messageDiv.append(timeSpan);
this.log.warn('unrecognized media type:', message.media._, message);
break;
4 years ago
}
4 years ago
4 years ago
if(!processingWebPage) {
4 years ago
bubbleContainer.append(attachmentDiv);
4 years ago
}
}
4 years ago
if((this.peerID < 0 && !our) || message.fwd_from || message.reply_to_mid) { // chat
4 years ago
let title = appPeersManager.getPeerTitle(message.fwdFromID || message.fromID);
4 years ago
let isHidden = message.fwd_from && !message.fwd_from.from_id && !message.fwd_from.channel_id;
if(isHidden) {
4 years ago
///////this.log('message to render hidden', message);
title = message.fwd_from.from_name;
bubble.classList.add('hidden-profile');
}
4 years ago
//this.log(title);
4 years ago
if(message.fwdFromID || message.fwd_from) {
4 years ago
bubble.classList.add('forwarded');
4 years ago
if(message.savedFrom) {
let fwd = document.createElement('div');
fwd.classList.add('forward'/* , 'tgico-forward' */);
4 years ago
fwd.innerHTML = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
<defs><path d="M13.55 3.24L13.64 3.25L13.73 3.27L13.81 3.29L13.9 3.32L13.98 3.35L14.06 3.39L14.14 3.43L14.22 3.48L14.29 3.53L14.36 3.59L14.43 3.64L22.23 10.85L22.36 10.99L22.48 11.15L22.57 11.31L22.64 11.48L22.69 11.66L22.72 11.85L22.73 12.04L22.71 12.22L22.67 12.41L22.61 12.59L22.53 12.76L22.42 12.93L22.29 13.09L22.23 13.15L14.43 20.36L14.28 20.48L14.12 20.58L13.95 20.66L13.77 20.72L13.58 20.76L13.4 20.77L13.22 20.76L13.03 20.73L12.85 20.68L12.68 20.61L12.52 20.52L12.36 20.4L12.22 20.27L12.16 20.2L12.1 20.13L12.05 20.05L12.01 19.98L11.96 19.9L11.93 19.82L11.89 19.73L11.87 19.65L11.84 19.56L11.83 19.47L11.81 19.39L11.81 19.3L11.8 19.2L11.8 16.42L11 16.49L10.23 16.58L9.51 16.71L8.82 16.88L8.18 17.09L7.57 17.33L7.01 17.6L6.48 17.91L5.99 18.26L5.55 18.64L5.14 19.05L4.77 19.51L4.43 19.99L4.29 20.23L4.21 20.35L4.11 20.47L4 20.57L3.88 20.65L3.75 20.72L3.62 20.78L3.48 20.82L3.33 20.84L3.19 20.84L3.04 20.83L2.9 20.79L2.75 20.74L2.62 20.68L2.53 20.62L2.45 20.56L2.38 20.5L2.31 20.43L2.25 20.36L2.2 20.28L2.15 20.19L2.11 20.11L2.07 20.02L2.04 19.92L2.02 19.83L2.01 19.73L2 19.63L2.04 17.99L2.19 16.46L2.46 15.05L2.85 13.75L3.35 12.58L3.97 11.53L4.7 10.6L5.55 9.8L6.51 9.12L7.59 8.56L8.77 8.13L10.07 7.83L11.48 7.65L11.8 7.63L11.8 4.8L11.91 4.56L12.02 4.35L12.14 4.16L12.25 3.98L12.37 3.82L12.48 3.68L12.61 3.56L12.73 3.46L12.85 3.38L12.98 3.31L13.11 3.27L13.24 3.24L13.37 3.23L13.46 3.23L13.55 3.24Z" id="b13RmHDQtl"></path></defs><use xlink:href="#b13RmHDQtl" opacity="1" fill="#fff" fill-opacity="1"></use></svg>`;
4 years ago
bubbleContainer.append(fwd);
bubble.dataset.savedFrom = message.savedFrom;
}
4 years ago
4 years ago
if(!bubble.classList.contains('sticker')) {
let nameDiv = document.createElement('div');
nameDiv.classList.add('name');
nameDiv.innerHTML = 'Forwarded from ' + title;
nameDiv.dataset.peerID = message.fwdFromID;
//nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID, false);
4 years ago
bubbleContainer.append(nameDiv);
4 years ago
}
} else {
if(message.reply_to_mid) {
let originalMessage = appMessagesManager.getMessage(message.reply_to_mid);
4 years ago
let originalPeerTitle = appPeersManager.getPeerTitle(originalMessage.fromID, true) || '';
4 years ago
4 years ago
/////////this.log('message to render reply', originalMessage, originalPeerTitle, bubble, message);
4 years ago
// need to download separately
if(originalMessage._ == 'messageEmpty') {
4 years ago
//////////this.log('message to render reply empty, need download', message, message.reply_to_mid);
appMessagesManager.wrapSingleMessage(message.reply_to_mid);
this.needUpdate.push({replyMid: message.reply_to_mid, mid: message.mid});
4 years ago
originalPeerTitle = 'Loading...';
}
4 years ago
if(originalMessage.mid) {
bubble.setAttribute('data-original-mid', originalMessage.mid);
} else {
bubble.setAttribute('data-original-mid', message.reply_to_mid);
}
4 years ago
4 years ago
bubbleContainer.append(wrapReply(originalPeerTitle, originalMessage.message || '', originalMessage.media));
bubble.classList.add('is-reply');
4 years ago
}
4 years ago
if(!bubble.classList.contains('sticker') && (peerID < 0 && peerID != message.fromID)) {
4 years ago
let nameDiv = document.createElement('div');
nameDiv.classList.add('name');
nameDiv.innerHTML = title;
nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID, false);
nameDiv.dataset.peerID = message.fromID;
4 years ago
bubbleContainer.append(nameDiv);
} else /* if(!message.reply_to_mid) */ {
bubble.classList.add('hide-name');
4 years ago
}
}
4 years ago
if(!our && this.peerID < 0 && (!appPeersManager.isChannel(this.peerID) || appPeersManager.isMegagroup(this.peerID))) {
let avatarDiv = document.createElement('div');
avatarDiv.classList.add('user-avatar');
4 years ago
4 years ago
/////////this.log('exec loadDialogPhoto', message);
if(message.fromID) { // if no - user hidden
appProfileManager.putPhoto(avatarDiv, message.fromID);
} else if(!title && message.fwd_from && message.fwd_from.from_name) {
title = message.fwd_from.from_name;
4 years ago
appProfileManager.putPhoto(avatarDiv, 0, false, title);
}
4 years ago
avatarDiv.dataset.peerID = message.fromID;
4 years ago
4 years ago
bubbleContainer.append(avatarDiv);
4 years ago
}
} else {
bubble.classList.add('hide-name');
4 years ago
}
4 years ago
4 years ago
bubble.classList.add(our ? 'is-out' : 'is-in');
if(updatePosition) {
this.bubbleGroups.addBubble(bubble, message, reverse);
4 years ago
//window.requestAnimationFrame(() => {
/* if(reverse) {
this.scrollable.prependByBatch(bubble);
} else {
this.scrollable.appendByBatch(bubble);
} */
if(!multipleRender) {
if(reverse) {
this.scrollable.prepend(bubble);
} else {
this.scrollable.append(bubble);
4 years ago
}
}
4 years ago
//});
4 years ago
let justDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
let dateTimestamp = justDate.getTime();
let needUpdatePos = false;
if(!(dateTimestamp in this.dateMessages)) {
let str = '';
4 years ago
let today = new Date();
today.setHours(0);
today.setMinutes(0);
today.setSeconds(0);
4 years ago
if(today < date) {
str = 'Today';
} else {
4 years ago
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
str = justDate.getFullYear() == new Date().getFullYear() ?
4 years ago
months[justDate.getMonth()] + ' ' + justDate.getDate() :
justDate.toISOString().split('T')[0].split('-').reverse().join('.');
}
4 years ago
let div = document.createElement('div');
4 years ago
div.className = 'bubble service';
div.innerHTML = `<div class="service-msg">${str}</div>`;
4 years ago
////////this.log('need to render date message', dateTimestamp, str);
this.dateMessages[dateTimestamp] = {
div,
bubble,
firstTimestamp: date.getTime()
};
4 years ago
needUpdatePos = true;
} else {
let dateMessage = this.dateMessages[dateTimestamp];
if(dateMessage.firstTimestamp > date.getTime()) {
dateMessage.bubble = bubble;
needUpdatePos = true;
}
}
let dateMessage = this.dateMessages[dateTimestamp];
if(needUpdatePos && !dateMessage.timeout) {
4 years ago
//dateMessage.timeout = setTimeout(() => {
delete dateMessage.timeout;
4 years ago
//this.scrollable.insertBefore(dateMessage.div, dateMessage.bubble);
//}, 0);
}
4 years ago
} else {
this.bubbleGroups.updateGroupByMessageID(message.mid);
}
4 years ago
/* if(bubble.classList.contains('webpage')) {
this.log('night running', bubble, bubble.scrollHeight);
} */
4 years ago
return bubble;
}
public performHistoryResult(history: number[], reverse: boolean, isBackLimit: boolean, additionMsgID: number, resetPromises = false) {
// commented bot getProfile in getHistory!
if(!history/* .filter((id: number) => id > 0) */.length) {
if(!isBackLimit) {
this.scrolledAll = true;
} else {
this.scrolledAllDown = true;
}
}
//history = history.slice(); // need
4 years ago
if(additionMsgID) {
history.unshift(additionMsgID);
}
//let method = reverse ? result.history.forEach : result.history.forEachReverse;
let method = reverse ? Array.prototype.forEach : Array.prototype.forEachReverse;
method = method.bind(history);
//console.time('appImManager render history');
this.log('getHistory method', method);
let firstLoad = !!this.setPeerPromise && false;
4 years ago
let peerID = this.peerID;
return new Promise<boolean>((resolve, reject) => {
let resolved = false;
/* let bubbles: HTMLDivElement[] = [];
method((msgID) => {
let message = appMessagesManager.getMessage(msgID);
let bubble = this.renderMessage(message, reverse, true);
bubbles.push(bubble);
}); */
let leftHeightToScroll = this.scrollable.innerHeight;
4 years ago
//console.timeEnd('appImManager: pre render start');
//this.log('start performHistoryResult, scrollTop:', this.scrollable.scrollTop, this.scrollable.scrollHeight, this.scrollable.innerHeight);
4 years ago
let renderedFirstScreen = false;
let r = () => {
//let bubble = bubbles.shift();
//if(!bubble && !resolved) return resolve();
//if(!history.length) return resolve(true);
/* let msgID = result.history.shift();
if(!msgID && !resolved) return resolve();
let message = appMessagesManager.getMessage(msgID); */
if(this.peerID != peerID) {
return reject('peer changed');
}
//let startTime = Date.now();
//let elapsedTime = 0;
//do {
let msgID = history.shift();
if(!msgID) {
if(resetPromises) {
(reverse ? this.getHistoryTopPromise = undefined : this.getHistoryBottomPromise = undefined);
}
this.scrollable.unlock('both');
if(!resolved) {
resolve(true);
}
return;
}
let message = appMessagesManager.getMessage(msgID);
let bubble = this.renderMessage(message, reverse, true);
if(bubble) {
if(reverse) {
this.scrollable.prepend(bubble);
//this.log('performHistoryResult scrollTop', this.scrollable.scrollTop, bubble.scrollHeight);
4 years ago
/* if(innerHeight >= 0) {
let height = bubble.scrollHeight;
innerHeight -= height;
this.scrollable.scrollTop += height;
} */
if(!renderedFirstScreen) {
if(!this.scrollable.scrollTop) {
let height = bubble.scrollHeight;
//let height = Math.ceil(bubble.getBoundingClientRect().height);
this.scrollable.scrollTop += height;
//innerHeight -= height;
}
/* if(leftHeightToScroll >= 0) {
let height = bubble.scrollHeight;
leftHeightToScroll -= height;
this.scrollable.scrollTop += height;
} */ else {
4 years ago
renderedFirstScreen = true;
resolve();
resolved = true;
}
}
} else {
this.scrollable.append(bubble);
}
}
//} while(cached && !this.scrollable.scrollTop);
//} while((elapsedTime = Date.now() - startTime) < 3);
/* let bubble = this.renderMessage(message, reverse, true);
if(!bubble) return r();
if(reverse) {
this.scrollable.prepend(bubble);
if(!this.scrollable.scrollTop) {
let height = bubble.scrollHeight;
this.scrollable.scrollTop += height;
//innerHeight -= height;
} else if(!resolved) {
resolve();
resolved = true;
}
} else {
this.scrollable.append(bubble);
} */
firstLoad ? window.requestAnimationFrame(r) : r();
4 years ago
};
firstLoad ? window.requestAnimationFrame(r) : r();
4 years ago
//r();
/* method((msgID) => {
let message = appMessagesManager.getMessage(msgID);
window.requestAnimationFrame(() => {
this.renderMessage(message, reverse, true);
});
}); */
})/* .then(() => {
if(!isBackLimit) {
this.scrollPosition.restore(() => {
this.scrollable.unlock('both');
});
} else {
this.scrollable.unlock('both');
}
//console.timeEnd('appImManager render history');
return true;
}) */;
4 years ago
}
4 years ago
4 years ago
// reverse means scroll up
4 years ago
public getHistory(maxID = 0, reverse = false, isBackLimit = false, additionMsgID = 0) {
4 years ago
let peerID = this.peerID;
4 years ago
//console.time('appImManager call getHistory');
4 years ago
let dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
if(!maxID && dialog && dialog.top_message) {
maxID = dialog.top_message/* + 1 */;
4 years ago
}
4 years ago
let loadCount = Object.keys(this.bubbles).length > 0 ? 20 : this.scrollable.innerHeight / 38/* * 1.25 */ | 0;
4 years ago
4 years ago
/* if(testScroll) {
4 years ago
loadCount = 5;
4 years ago
if(Object.keys(this.bubbles).length > 0)
return Promise.resolve(true);
4 years ago
} */
4 years ago
4 years ago
////console.time('render history total');
4 years ago
4 years ago
let backLimit = 0;
if(isBackLimit) {
backLimit = loadCount;
loadCount = 0;
maxID += 1;
}
4 years ago
let result = appMessagesManager.getHistory(this.peerID, maxID, loadCount, backLimit);
/* if(!(result instanceof Promise)) {
let _result = result;
$rootScope.$broadcast('history_request'); // for ripple
result = new Promise((resolve, reject) => setTimeout(() => resolve(_result), 150));
} */
if(result instanceof Promise) {
let promise = result.then((result) => {
this.log('getHistory result by maxID:', maxID, reverse, isBackLimit, result);
//console.timeEnd('appImManager call getHistory');
if(this.peerID != peerID) {
this.log.warn('peer changed');
////console.timeEnd('render history total');
return Promise.reject();
}
4 years ago
////console.timeEnd('render history total');
return this.performHistoryResult(result.history || [], reverse, isBackLimit, additionMsgID, true);
}, (err) => {
this.log.error('getHistory error:', err);
(reverse ? this.getHistoryTopPromise = undefined : this.getHistoryBottomPromise = undefined);
this.scrollable.unlock('both');
4 years ago
return false;
});
return (reverse ? this.getHistoryTopPromise = promise : this.getHistoryBottomPromise = promise);
} else {
let promise = this.performHistoryResult(result.history || [], reverse, isBackLimit, additionMsgID, true);
//return (reverse ? this.getHistoryTopPromise = promise : this.getHistoryBottomPromise = promise);
return promise;
//return this.performHistoryResult(result.history || [], reverse, isBackLimit, additionMsgID, true);
4 years ago
}
4 years ago
}
4 years ago
public setMutedState(muted = false) {
appSidebarRight.profileElements.notificationsCheckbox.checked = !muted;
appSidebarRight.profileElements.notificationsStatus.innerText = muted ? 'Disabled' : 'Enabled';
4 years ago
let peerID = this.peerID;
4 years ago
this.muted = muted;
if(peerID < 0) { // not human
let isChannel = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID);
if(isChannel) {
this.btnMute.classList.remove('tgico-mute', 'tgico-unmute');
this.btnMute.classList.add(muted ? 'tgico-unmute' : 'tgico-mute');
this.btnMute.style.display = '';
} else {
this.btnMute.style.display = 'none';
}
} else {
this.btnMute.style.display = 'none';
}
4 years ago
this.btnMenuMute.classList.remove('tgico-mute', 'tgico-unmute');
this.btnMenuMute.classList.add(muted ? 'tgico-unmute' : 'tgico-mute');
let rp = this.btnMenuMute.firstElementChild;
this.btnMenuMute.innerText = muted ? 'Unmute' : 'Mute';
this.btnMenuMute.appendChild(rp);
}
4 years ago
public mutePeer() {
let inputPeer = appPeersManager.getInputPeerByID(this.peerID);
let inputNotifyPeer = {
_: 'inputNotifyPeer',
peer: inputPeer
};
4 years ago
let settings = {
_: 'inputPeerNotifySettings',
flags: 0,
mute_until: 0
};
4 years ago
if(!this.muted) {
settings.flags |= 1 << 2;
settings.mute_until = 2147483646;
} else {
settings.flags |= 2;
}
4 years ago
apiManager.invokeApi('account.updateNotifySettings', {
peer: inputNotifyPeer,
settings: settings
}).then(res => {
this.handleUpdate({_: 'updateNotifySettings', peer: inputNotifyPeer, notify_settings: settings});
});
4 years ago
/* return apiManager.invokeApi('account.getNotifySettings', {
peer: inputNotifyPeer
}).then((settings: any) => {
settings.flags |= 2 << 1;
settings.mute_until = 2000000000; // 2147483646
4 years ago
return apiManager.invokeApi('account.updateNotifySettings', {
peer: inputNotifyPeer,
settings: Object.assign(settings, {
_: 'inputPeerNotifySettings'
})
}).then(res => {
this.log('mute result:', res);
});
}); */
}
4 years ago
public handleUpdate(update: any) {
switch(update._) {
case 'updateUserTyping':
case 'updateChatUserTyping':
4 years ago
if(this.myID == update.user_id) {
return;
}
var peerID = update._ == 'updateUserTyping' ? update.user_id : -update.chat_id;
this.typingUsers[update.user_id] = peerID;
if(!appUsersManager.hasUser(update.user_id)) {
if(update.chat_id && appChatsManager.hasChat(update.chat_id) && !appChatsManager.isChannel(update.chat_id)) {
appProfileManager.getChatFull(update.chat_id);
}
4 years ago
//return;
}
appUsersManager.forceUserOnline(update.user_id);
let dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
let currentPeer = this.peerID == peerID;
if(this.typingTimeouts[peerID]) clearTimeout(this.typingTimeouts[peerID]);
else if(dialog) {
appDialogsManager.setTyping(dialog, appUsersManager.getUser(update.user_id));
if(currentPeer) { // user
this.setPeerStatus();
}
4 years ago
}
this.typingTimeouts[peerID] = setTimeout(() => {
this.typingTimeouts[peerID] = 0;
delete this.typingUsers[update.user_id];
if(dialog) {
appDialogsManager.unsetTyping(dialog);
}
4 years ago
// лень просчитывать случаи
this.setPeerStatus();
}, 6000);
break;
case 'updateNotifySettings': {
let {peer, notify_settings} = update;
4 years ago
// peer was NotifyPeer
peer = peer.peer;
4 years ago
let peerID = appPeersManager.getPeerID(peer);
4 years ago
let dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
if(dialog) {
dialog.notify_settings = notify_settings;
}
4 years ago
if(peerID == this.peerID) {
let muted = notify_settings.mute_until ? new Date(notify_settings.mute_until * 1000) > new Date() : false;
this.setMutedState(muted);
}
4 years ago
4 years ago
/////this.log('updateNotifySettings', peerID, notify_settings);
break;
}
4 years ago
case 'updateChatPinnedMessage':
case 'updateUserPinnedMessage': {
let {id} = update;
4 years ago
4 years ago
/////this.log('updateUserPinnedMessage', update);
4 years ago
this.pinnedMsgID = id;
// hz nado li tut appMessagesIDsManager.getFullMessageID(update.max_id, channelID);
let peerID = update.user_id || -update.chat_id || -update.channel_id;
if(peerID == this.peerID) {
appMessagesManager.wrapSingleMessage(id);
}
4 years ago
break;
}
}
}
4 years ago
}
const appImManager = new AppImManager();
export default appImManager;