send message status & added global search contacts & set any peer

This commit is contained in:
Eduard Kuzmenko 2020-02-11 22:35:57 +07:00
parent 04123d0ba6
commit 55833f2489
25 changed files with 907 additions and 832 deletions

View File

@ -1,6 +1,6 @@
import { AppImManager } from "../lib/appManagers/appImManager";
import { AppMessagesManager } from "../lib/appManagers/appMessagesManager";
import { LazyLoadQueue, horizontalMenu, MTDocument, wrapSticker } from "./misc";
import { horizontalMenu } from "./misc";
import lottieLoader from "../lib/lottieLoader";
import Scrollable from "./scrollable";
import { findUpTag, whichChild } from "../lib/utils";
@ -8,6 +8,8 @@ import { RichTextProcessor } from "../lib/richtextprocessor";
import appStickersManager, { MTStickerSet } from "../lib/appManagers/appStickersManager";
import apiManager from '../lib/mtproto/apiManager';
import CryptoWorker from '../lib/crypto/cryptoworker';
import LazyLoadQueue from "./lazyLoadQueue";
import { MTDocument, wrapSticker } from "./wrappers";
export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown';

View File

@ -0,0 +1,44 @@
import { isElementInViewport } from "../lib/utils";
export default class LazyLoadQueue {
private lazyLoadMedia: Array<{div: HTMLDivElement, load: () => Promise<void>}> = [];
public check(id?: number) {
/* let length = this.lazyLoadMedia.length;
for(let i = length - 1; i >= 0; --i) {
let {div, load} = this.lazyLoadMedia[i];
if(isElementInViewport(div)) {
console.log('will load div:', div);
load();
this.lazyLoadMedia.splice(i, 1);
}
} */
if(id !== undefined) {
let {div, load} = this.lazyLoadMedia[id];
if(isElementInViewport(div)) {
//console.log('will load div by id:', div, div.getBoundingClientRect());
load();
this.lazyLoadMedia.splice(id, 1);
}
return;
}
this.lazyLoadMedia = this.lazyLoadMedia.filter(({div, load}) => {
if(isElementInViewport(div)) {
//console.log('will load div:', div, div.getBoundingClientRect());
load();
return false;
}
return true;
});
}
public push(el: {div: HTMLDivElement, load: () => Promise<void>}) {
let id = this.lazyLoadMedia.push(el) - 1;
this.check(id);
}
}

View File

@ -1,47 +1,5 @@
import { MTProto } from "../lib/mtproto/mtproto";
import { formatBytes, whichChild, isElementInViewport, isInDOM, findUpTag } from "../lib/utils";
import appPhotosManager from '../lib/appManagers/appPhotosManager';
import CryptoWorker from '../lib/crypto/cryptoworker';
import LottieLoader from '../lib/lottieLoader';
import appStickersManager from "../lib/appManagers/appStickersManager";
import appDocsManager from "../lib/appManagers/appDocsManager";
import {AppImManager} from "../lib/appManagers/appImManager";
import {AppMediaViewer} from '../lib/appManagers/appMediaViewer';
import { RichTextProcessor } from "../lib/richtextprocessor";
import lottieLoader from "../lib/lottieLoader";
export type MTDocument = {
_: 'document',
pFlags: any,
flags: number,
id: string,
access_hash: string,
file_reference: Uint8Array | number[],
date: number,
mime_type: string,
size: number,
thumbs: MTPhotoSize[],
dc_id: number,
attributes: any[],
type?: string,
h?: number,
w?: number,
file_name?: string,
file?: File
};
export type MTPhotoSize = {
_: string,
w?: number,
h?: number,
size?: number,
type?: string, // i, m, x, y, w by asc
location?: any,
bytes?: Uint8Array, // if type == 'i'
preloaded?: boolean // custom added
};
import apiManager from "../lib/mtproto/apiManager";
import { whichChild, isElementInViewport, isInDOM, findUpTag } from "../lib/utils";
let onRippleClick = function(this: HTMLElement, e: MouseEvent) {
var $circle = this.firstElementChild as HTMLSpanElement;//this.querySelector('.c-ripple__circle') as HTMLSpanElement;
@ -103,580 +61,6 @@ export function putPreloader(elem: Element) {
elem.innerHTML += html;
}
export class ProgressivePreloader {
public preloader: HTMLDivElement = null;
private circle: SVGCircleElement = null;
private progress = 0;
constructor(elem?: Element, private cancelable = true) {
this.preloader = document.createElement('div');
this.preloader.classList.add('preloader-container');
this.preloader.innerHTML = `
<div class="you-spin-me-round">
<svg xmlns="http://www.w3.org/2000/svg" class="preloader-circular" viewBox="25 25 50 50">
<circle class="preloader-path-new" cx="50" cy="50" r="23" fill="none" stroke-miterlimit="10"/>
</svg>
</div>`;
if(cancelable) {
this.preloader.innerHTML += `
<svg xmlns="http://www.w3.org/2000/svg" class="preloader-close" viewBox="0 0 20 20">
<line x1="0" y1="20" x2="20" y2="0" stroke-width="2" stroke-linecap="round"></line>
<line x1="0" y1="0" x2="20" y2="20" stroke-width="2" stroke-linecap="round"></line>
</svg>`;
} else {
this.preloader.classList.add('preloader-swing');
}
this.circle = this.preloader.firstElementChild.firstElementChild.firstElementChild as SVGCircleElement;
if(elem) {
this.attach(elem);
}
}
public attach(elem: Element, reset = true) {
if(this.cancelable && reset) {
this.setProgress(0);
}
elem.append(this.preloader);
/* let isIn = isInDOM(this.preloader);
if(isIn && this.progress != this.defaultProgress) {
this.setProgress(this.defaultProgress);
}
elem.append(this.preloader);
if(!isIn && this.progress != this.defaultProgress) {
this.setProgress(this.defaultProgress);
} */
}
public detach() {
if(this.preloader.parentElement) {
this.preloader.parentElement.removeChild(this.preloader);
}
}
public setProgress(percents: number) {
this.progress = percents;
if(!isInDOM(this.circle)) {
return;
}
if(percents == 0) {
this.circle.style.strokeDasharray = '';
return;
}
let totalLength = this.circle.getTotalLength();
console.log('setProgress', (percents / 100 * totalLength));
this.circle.style.strokeDasharray = '' + Math.max(5, percents / 100 * totalLength) + ', 200';
}
}
export class LazyLoadQueue {
private lazyLoadMedia: Array<{div: HTMLDivElement, load: () => Promise<void>}> = [];
public check(id?: number) {
/* let length = this.lazyLoadMedia.length;
for(let i = length - 1; i >= 0; --i) {
let {div, load} = this.lazyLoadMedia[i];
if(isElementInViewport(div)) {
console.log('will load div:', div);
load();
this.lazyLoadMedia.splice(i, 1);
}
} */
if(id !== undefined) {
let {div, load} = this.lazyLoadMedia[id];
if(isElementInViewport(div)) {
//console.log('will load div by id:', div, div.getBoundingClientRect());
load();
this.lazyLoadMedia.splice(id, 1);
}
return;
}
this.lazyLoadMedia = this.lazyLoadMedia.filter(({div, load}) => {
if(isElementInViewport(div)) {
//console.log('will load div:', div, div.getBoundingClientRect());
load();
return false;
}
return true;
});
}
public push(el: {div: HTMLDivElement, load: () => Promise<void>}) {
let id = this.lazyLoadMedia.push(el) - 1;
this.check(id);
}
}
export function wrapVideo(this: any, doc: MTDocument, container: HTMLDivElement, message: any, justLoader = true, preloader?: ProgressivePreloader, controls = true) {
if(!container.firstElementChild || container.firstElementChild.tagName != 'IMG') {
let size = appPhotosManager.setAttachmentSize(doc, container);
}
let peerID = this.peerID ? this.peerID : this.currentMessageID;
//container.classList.add('video');
let img = container.firstElementChild as HTMLImageElement || new Image();
img.setAttribute('message-id', '' + message.id);
if(!container.contains(img)) {
container.append(img);
}
//return Promise.resolve();
if(!preloader) {
preloader = new ProgressivePreloader(container, false);
}
let loadVideo = () => {
let promise = appDocsManager.downloadDoc(doc);
/* promise.notify = (details: {done: number, total: number}) => {
console.log('doc download', promise, details);
preloader.setProgress(details.done);
}; */
return promise.then(blob => {
if((this.peerID ? this.peerID : this.currentMessageID) != peerID) {
this.log.warn('peer changed');
return;
}
console.log('loaded doc:', doc, blob, container);
let video = document.createElement('video');
video.loop = controls;
video.autoplay = controls;
if(!justLoader) {
video.controls = controls;
} else {
video.volume = 0;
}
video.setAttribute('message-id', '' + message.id);
let source = document.createElement('source');
//source.src = doc.url;
source.src = URL.createObjectURL(blob);
source.type = doc.mime_type;
if(img && container.contains(img)) {
container.removeChild(img);
}
video.append(source);
container.append(video);
//container.style.width = '';
//container.style.height = '';
preloader.detach();
});
};
if(doc.type == 'gif' || true) { // extra fix
return this.peerID ? this.loadMediaQueuePush(loadVideo) : loadVideo();
} else { // if video
let load = () => appPhotosManager.preloadPhoto(doc).then((blob) => {
if((this.peerID ? this.peerID : this.currentMessageID) != peerID) {
this.log.warn('peer changed');
return;
}
img.src = URL.createObjectURL(blob);
/* image.style.height = doc.h + 'px';
image.style.width = doc.w + 'px'; */
/* if(justLoader) { // extra fix
justLoader = false;
controls = false;
} */
if(!justLoader) {
return loadVideo();
} else {
container.style.width = '';
container.style.height = '';
preloader.detach();
}
});
return this.peerID ? this.loadMediaQueuePush(load) : load();
}
}
export function wrapDocument(doc: MTDocument, withTime = false): HTMLDivElement {
let docDiv = document.createElement('div');
docDiv.classList.add('document');
let iconDiv = document.createElement('div');
iconDiv.classList.add('tgico-document');
let extSplitted = doc.file_name ? doc.file_name.split('.') : '';
let ext = '';
ext = extSplitted.length > 1 && Array.isArray(extSplitted) ? extSplitted.pop().toLowerCase() : 'file';
let ext2 = ext;
if(doc.type == 'photo') {
docDiv.classList.add('photo');
ext2 = `<img src="${URL.createObjectURL(doc.file)}">`;
}
let fileName = doc.file_name || 'Unknown.file';
let size = formatBytes(doc.size);
if(withTime) {
let months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
let date = new Date(doc.date * 1000);
size += ' · ' + months[date.getMonth()] + ' ' + date.getDate() + ', ' + date.getFullYear()
+ ' at ' + date.getHours() + ':' + ('0' + date.getMinutes()).slice(-2);
}
docDiv.innerHTML = `
<div class="document-ico ext-${ext}">${ext2}</div>
<div class="document-name">${fileName}</div>
<div class="document-size">${size}</div>
`;
return docDiv;
}
export function scrollable(el: HTMLDivElement, x = false, y = true) {
let container = document.createElement('div');
container.classList.add('scrollable');
if(x) container.classList.add('scrollable-x');
if(y) container.classList.add('scrollable-y');
let type = x ? 'width' : 'height';
let side = x ? 'left' : 'top';
let scrollType = x ? 'scrollWidth' : 'scrollHeight';
let scrollSide = x ? 'scrollLeft' : 'scrollTop';
container.addEventListener('mouseover', () => {
resize();
/* container.classList.add('active');
container.addEventListener('mouseout', () => {
container.classList.remove('active');
}, {once: true}); */
});
let thumb = document.createElement('div');
thumb.className = 'scrollbar-thumb';
// @ts-ignore
thumb.style[type] = '30px';
let resize = () => {
// @ts-ignore
scrollSize = container[scrollType];
let rect = container.getBoundingClientRect();
// @ts-ignore
size = rect[type];
if(!size || size == scrollSize) {
thumbSize = 0;
// @ts-ignore
thumb.style[type] = thumbSize + 'px';
return;
}
//if(!height) return;
let divider = scrollSize / size / 0.5;
thumbSize = size / divider;
if(thumbSize < 20) thumbSize = 20;
// @ts-ignore
thumb.style[type] = thumbSize + 'px';
// @ts-ignore
//console.log('onresize', thumb.style[type], thumbHeight, height);
};
let scrollSize = -1;
let size = 0;
let thumbSize = 0;
window.addEventListener('resize', resize);
//container.addEventListener('DOMNodeInserted', resize);
let hiddenElements: {
up: Element[],
down: Element[]
} = {
up: [],
down: []
};
let paddings = {up: 0, down: 0};
let paddingTopDiv = document.createElement('div');
paddingTopDiv.classList.add('scroll-padding');
let paddingBottomDiv = document.createElement('div');
paddingBottomDiv.classList.add('scroll-padding');
let onScroll = (e: Event) => {
// @ts-ignore
//let st = container[scrollSide];
// @ts-ignore
if(container[scrollType] != scrollSize || thumbSize == 0) {
resize();
}
//let splitUp = container.querySelector('ul');
let splitUp = container.children[1];
let children = Array.from(splitUp.children) as HTMLElement[];
let firstVisible = -1, lastVisible = -1;
let length = children.length;
for(let i = 0; i < length; ++i) {
let child = children[i];
if(isElementInViewport(child)) {
if(firstVisible < 0) firstVisible = i;
lastVisible = i;
}
}
if(firstVisible > 0) {
let sliced = children.slice(0, firstVisible);
for(let child of sliced) {
paddings.up += child.scrollHeight;
hiddenElements.up.push(child);
child.parentElement.removeChild(child);
}
//console.log('sliced up', sliced.length);
//sliced.forEach(child => child.style.display = 'none');
paddingTopDiv.style.height = paddings.up + 'px';
//console.log('onscroll need to add padding: ', paddings.up);
} else if(hiddenElements.up.length) {
while(isElementInViewport(paddingTopDiv) && paddings.up) {
let child = hiddenElements.up.pop();
splitUp.prepend(child);
paddings.up -= child.scrollHeight;
paddingTopDiv.style.height = paddings.up + 'px';
}
}
if(lastVisible < (length - 1)) {
let sliced = children.slice(lastVisible + 1).reverse();
for(let child of sliced) {
paddings.down += child.scrollHeight;
hiddenElements.down.unshift(child);
child.parentElement.removeChild(child);
}
//console.log('onscroll sliced down', sliced.length);
//sliced.forEach(child => child.style.display = 'none');
paddingBottomDiv.style.height = paddings.down + 'px';
//console.log('onscroll need to add padding: ', paddings.up);
} else if(hiddenElements.down.length) {
while(isElementInViewport(paddingBottomDiv) && paddings.down) {
let child = hiddenElements.down.shift();
splitUp.append(child);
paddings.down -= child.scrollHeight;
paddingBottomDiv.style.height = paddings.down + 'px';
}
}
//console.log('onscroll', container, firstVisible, lastVisible, hiddenElements);
// @ts-ignore
let value = container[scrollSide] / (scrollSize - size) * 100;
let maxValue = 100 - (thumbSize / size * 100);
//console.log('onscroll', container.scrollHeight, thumbHeight, height, value, maxValue);
// @ts-ignore
thumb.style[side] = (value >= maxValue ? maxValue : value) + '%';
//lastScrollPos = st;
};
let lastScrollPos = 0;
container.addEventListener('scroll', onScroll);
container.append(paddingTopDiv);
Array.from(el.children).forEach(c => container.append(c));
container.append(paddingBottomDiv);
el.append(container);//container.append(el);
container.parentElement.append(thumb);
resize();
return {container, hiddenElements, onScroll};
}
export function wrapPhoto(this: AppImManager, photo: any, message: any, container: HTMLDivElement) {
//container.classList.add('photo');
let peerID = this.peerID;
let size = appPhotosManager.setAttachmentSize(photo.id, container);
let image = container.firstElementChild as HTMLImageElement || new Image();
//let size = appPhotosManager.setAttachmentSize(photo.id, image);
image.setAttribute('message-id', message.mid);
if(!container.contains(image)) {
container.append(image);
}
let preloader = new ProgressivePreloader(container, false);
let load = () => appPhotosManager.preloadPhoto(photo.id, size).then((blob) => {
if(this.peerID != peerID) {
this.log.warn('peer changed');
return;
}
image.src = URL.createObjectURL(blob);
preloader.detach();
//image.style.width = '';
//image.style.height = '';
//container.style.width = '';
//container.style.height = '';
});
console.log('wrapPhoto', load, container, image);
return this.loadMediaQueue ? this.loadMediaQueuePush(load) : load();
}
export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: () => boolean, lazyLoadQueue?: LazyLoadQueue, group?: string, canvas?: boolean, play = false) {
let stickerType = doc.mime_type == "application/x-tgsticker" ? 2 : (doc.mime_type == "image/webp" ? 1 : 0);
if(!stickerType) {
console.error('wrong doc for wrapSticker!', doc, div);
}
//console.log('wrap sticker', doc);
if(doc.thumbs && !div.firstElementChild) {
let thumb = doc.thumbs[0];
if(thumb.bytes) {
MTProto.apiFileManager.saveSmallFile(thumb.location, thumb.bytes);
appPhotosManager.setAttachmentPreview(thumb.bytes, div, true);
}
}
let load = () => MTProto.apiFileManager.downloadSmallFile({
_: 'inputDocumentFileLocation',
access_hash: doc.access_hash,
file_reference: doc.file_reference,
thumb_size: ''/* document.thumbs[0].type */,
id: doc.id,
stickerType: stickerType
}, {mimeType: doc.mime_type, dcID: doc.dc_id}).then(blob => {
//console.log('loaded sticker:', blob, div);
if(middleware && !middleware()) return;
if(div.firstElementChild) {
div.firstElementChild.remove();
}
if(stickerType == 2) {
const reader = new FileReader();
reader.addEventListener('loadend', async(e) => {
// @ts-ignore
const text = e.srcElement.result;
let json = await CryptoWorker.gzipUncompress<string>(text, true);
let animation = await LottieLoader.loadAnimation({
container: div,
loop: false,
autoplay: false,
animationData: JSON.parse(json),
renderer: canvas ? 'canvas' : 'svg'
}, group);
if(!canvas) {
div.addEventListener('mouseover', (e) => {
let animation = LottieLoader.getAnimation(div, group);
if(animation) {
//console.log('sticker hover', animation, div);
// @ts-ignore
animation.loop = true;
// @ts-ignore
if(animation.currentFrame == animation.totalFrames - 1) {
animation.goToAndPlay(0, true);
} else {
animation.play();
}
div.addEventListener('mouseout', () => {
// @ts-ignore
animation.loop = false;
}, {once: true});
}
});
} /* else {
let canvas = div.firstElementChild as HTMLCanvasElement;
if(!canvas.width && !canvas.height) {
console.log('Need lottie resize');
// @ts-ignore
animation.resize();
}
} */
if(play) {
animation.play();
}
});
reader.readAsArrayBuffer(blob);
} else if(stickerType == 1) {
let img = new Image();
img.src = URL.createObjectURL(blob);
/* div.style.height = doc.h + 'px';
div.style.width = doc.w + 'px'; */
div.append(img);
}
div.setAttribute('file-id', doc.id);
appStickersManager.saveSticker(doc);
});
return lazyLoadQueue ? (lazyLoadQueue.push({div, load}), Promise.resolve()) : load();
}
export function horizontalMenu(tabs: HTMLUListElement, content: HTMLDivElement, onClick?: (id: number, tabContent: HTMLDivElement) => void, onTransitionEnd?: () => void) {
let hideTimeout: number = 0;
let prevTabContent: HTMLDivElement = null;
@ -761,10 +145,10 @@ export function horizontalMenu(tabs: HTMLUListElement, content: HTMLDivElement,
}
export function getNearestDc() {
return MTProto.apiManager.invokeApi('help.getNearestDc').then((nearestDcResult: any) => {
return apiManager.invokeApi('help.getNearestDc').then((nearestDcResult: any) => {
if(nearestDcResult.nearest_dc != nearestDcResult.this_dc) {
//MTProto.apiManager.baseDcID = nearestDcResult.nearest_dc;
MTProto.apiManager.getNetworker(nearestDcResult.nearest_dc);
apiManager.getNetworker(nearestDcResult.nearest_dc);
}
return nearestDcResult;

View File

@ -1,5 +1,5 @@
//import { appImManager, appMessagesManager, appDialogsManager, apiUpdatesManager, appUsersManager } from "../lib/services";
import { LazyLoadQueue, openBtnMenu } from "./misc";
import { openBtnMenu } from "./misc";
import Scrollable from './scrollable';
import {stackBlurImage} from '../lib/StackBlur';
@ -49,13 +49,6 @@ export default () => import('../lib/services').then(services => {
}
});
// @ts-ignore
document.addEventListener('history_multiappend', (e: CustomEvent) => {
//let msgIDsByPeer = e.detail;
appDialogsManager.sortDom();
});
// @ts-ignore
document.addEventListener('dialog_top', (e: CustomEvent) => {
let dialog: any = e.detail;
@ -64,16 +57,6 @@ export default () => import('../lib/services').then(services => {
appDialogsManager.sortDom();
});
// @ts-ignore
document.addEventListener('history_delete', (e: CustomEvent) => {
let detail: {
peerID: string,
msgs: {[x: number]: boolean}
} = e.detail;
appImManager.deleteMessagesByIDs(Object.keys(detail.msgs).map(s => +s));
});
// @ts-ignore
document.addEventListener('dialogs_multiupdate', (e: CustomEvent) => {
let dialogs = e.detail;

View File

@ -1,4 +1,3 @@
import { MTProto } from "../lib/mtproto/mtproto";
import { putPreloader, getNearestDc, formatPhoneNumber } from "./misc";
import Scrollable from './scrollable';
import {RichTextProcessor} from '../lib/richtextprocessor';
@ -6,6 +5,7 @@ import * as Config from '../lib/config';
import { findUpTag } from "../lib/utils";
import pageAuthCode from "./pageAuthCode";
import apiManager from "../lib/mtproto/apiManager";
let installed = false;
@ -193,8 +193,8 @@ export default () => {
this.removeAttribute('readonly'); // fix autocomplete
});*/
/* MTProto.authorizer.auth(2);
MTProto.networkerFactory.startAll(); */
/* authorizer.auth(2);
networkerFactory.startAll(); */
btnNext.addEventListener('click', function(this: HTMLElement, e) {
this.setAttribute('disabled', 'true');
@ -204,7 +204,7 @@ export default () => {
//this.innerHTML = 'PLEASE WAIT...';
let phone_number = telEl.value;
MTProto.apiManager.invokeApi('auth.sendCode', {
apiManager.invokeApi('auth.sendCode', {
/* flags: 0, */
phone_number: phone_number,
api_id: Config.App.id,

View File

@ -0,0 +1,76 @@
import { isInDOM } from "../lib/utils";
export default class ProgressivePreloader {
public preloader: HTMLDivElement = null;
private circle: SVGCircleElement = null;
private progress = 0;
constructor(elem?: Element, private cancelable = true) {
this.preloader = document.createElement('div');
this.preloader.classList.add('preloader-container');
this.preloader.innerHTML = `
<div class="you-spin-me-round">
<svg xmlns="http://www.w3.org/2000/svg" class="preloader-circular" viewBox="25 25 50 50">
<circle class="preloader-path-new" cx="50" cy="50" r="23" fill="none" stroke-miterlimit="10"/>
</svg>
</div>`;
if(cancelable) {
this.preloader.innerHTML += `
<svg xmlns="http://www.w3.org/2000/svg" class="preloader-close" viewBox="0 0 20 20">
<line x1="0" y1="20" x2="20" y2="0" stroke-width="2" stroke-linecap="round"></line>
<line x1="0" y1="0" x2="20" y2="20" stroke-width="2" stroke-linecap="round"></line>
</svg>`;
} else {
this.preloader.classList.add('preloader-swing');
}
this.circle = this.preloader.firstElementChild.firstElementChild.firstElementChild as SVGCircleElement;
if(elem) {
this.attach(elem);
}
}
public attach(elem: Element, reset = true) {
if(this.cancelable && reset) {
this.setProgress(0);
}
elem.append(this.preloader);
/* let isIn = isInDOM(this.preloader);
if(isIn && this.progress != this.defaultProgress) {
this.setProgress(this.defaultProgress);
}
elem.append(this.preloader);
if(!isIn && this.progress != this.defaultProgress) {
this.setProgress(this.defaultProgress);
} */
}
public detach() {
if(this.preloader.parentElement) {
this.preloader.parentElement.removeChild(this.preloader);
}
}
public setProgress(percents: number) {
this.progress = percents;
if(!isInDOM(this.circle)) {
return;
}
if(percents == 0) {
this.circle.style.strokeDasharray = '';
return;
}
let totalLength = this.circle.getTotalLength();
console.log('setProgress', (percents / 100 * totalLength));
this.circle.style.strokeDasharray = '' + Math.max(5, percents / 100 * totalLength) + ', 200';
}
}

323
src/components/wrappers.ts Normal file
View File

@ -0,0 +1,323 @@
import appPhotosManager from '../lib/appManagers/appPhotosManager';
import CryptoWorker from '../lib/crypto/cryptoworker';
import LottieLoader from '../lib/lottieLoader';
import appStickersManager from "../lib/appManagers/appStickersManager";
import appDocsManager from "../lib/appManagers/appDocsManager";
import {AppImManager} from "../lib/appManagers/appImManager";
import { formatBytes } from "../lib/utils";
import ProgressivePreloader from './preloader';
import LazyLoadQueue from './lazyLoadQueue';
import apiFileManager from '../lib/mtproto/apiFileManager';
export type MTDocument = {
_: 'document',
pFlags: any,
flags: number,
id: string,
access_hash: string,
file_reference: Uint8Array | number[],
date: number,
mime_type: string,
size: number,
thumbs: MTPhotoSize[],
dc_id: number,
attributes: any[],
type?: string,
h?: number,
w?: number,
file_name?: string,
file?: File
};
export type MTPhotoSize = {
_: string,
w?: number,
h?: number,
size?: number,
type?: string, // i, m, x, y, w by asc
location?: any,
bytes?: Uint8Array, // if type == 'i'
preloaded?: boolean // custom added
};
export function wrapVideo(this: any, doc: MTDocument, container: HTMLDivElement, message: any, justLoader = true, preloader?: ProgressivePreloader, controls = true) {
if(!container.firstElementChild || container.firstElementChild.tagName != 'IMG') {
let size = appPhotosManager.setAttachmentSize(doc, container);
}
let peerID = this.peerID ? this.peerID : this.currentMessageID;
//container.classList.add('video');
let img = container.firstElementChild as HTMLImageElement || new Image();
img.setAttribute('message-id', '' + message.id);
if(!container.contains(img)) {
container.append(img);
}
//return Promise.resolve();
if(!preloader) {
preloader = new ProgressivePreloader(container, false);
}
let loadVideo = () => {
let promise = appDocsManager.downloadDoc(doc);
/* promise.notify = (details: {done: number, total: number}) => {
console.log('doc download', promise, details);
preloader.setProgress(details.done);
}; */
return promise.then(blob => {
if((this.peerID ? this.peerID : this.currentMessageID) != peerID) {
this.log.warn('peer changed');
return;
}
console.log('loaded doc:', doc, blob, container);
let video = document.createElement('video');
video.loop = controls;
video.autoplay = controls;
if(!justLoader) {
video.controls = controls;
} else {
video.volume = 0;
}
video.setAttribute('message-id', '' + message.id);
let source = document.createElement('source');
//source.src = doc.url;
source.src = URL.createObjectURL(blob);
source.type = doc.mime_type;
if(img && container.contains(img)) {
container.removeChild(img);
}
video.append(source);
container.append(video);
//container.style.width = '';
//container.style.height = '';
preloader.detach();
});
};
if(doc.type == 'gif' || true) { // extra fix
return this.peerID ? this.loadMediaQueuePush(loadVideo) : loadVideo();
} else { // if video
let load = () => appPhotosManager.preloadPhoto(doc).then((blob) => {
if((this.peerID ? this.peerID : this.currentMessageID) != peerID) {
this.log.warn('peer changed');
return;
}
img.src = URL.createObjectURL(blob);
/* image.style.height = doc.h + 'px';
image.style.width = doc.w + 'px'; */
/* if(justLoader) { // extra fix
justLoader = false;
controls = false;
} */
if(!justLoader) {
return loadVideo();
} else {
container.style.width = '';
container.style.height = '';
preloader.detach();
}
});
return this.peerID ? this.loadMediaQueuePush(load) : load();
}
}
export function wrapDocument(doc: MTDocument, withTime = false): HTMLDivElement {
let docDiv = document.createElement('div');
docDiv.classList.add('document');
let iconDiv = document.createElement('div');
iconDiv.classList.add('tgico-document');
let extSplitted = doc.file_name ? doc.file_name.split('.') : '';
let ext = '';
ext = extSplitted.length > 1 && Array.isArray(extSplitted) ? extSplitted.pop().toLowerCase() : 'file';
let ext2 = ext;
if(doc.type == 'photo') {
docDiv.classList.add('photo');
ext2 = `<img src="${URL.createObjectURL(doc.file)}">`;
}
let fileName = doc.file_name || 'Unknown.file';
let size = formatBytes(doc.size);
if(withTime) {
let months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
let date = new Date(doc.date * 1000);
size += ' · ' + months[date.getMonth()] + ' ' + date.getDate() + ', ' + date.getFullYear()
+ ' at ' + date.getHours() + ':' + ('0' + date.getMinutes()).slice(-2);
}
docDiv.innerHTML = `
<div class="document-ico ext-${ext}">${ext2}</div>
<div class="document-name">${fileName}</div>
<div class="document-size">${size}</div>
`;
return docDiv;
}
export function wrapPhoto(this: AppImManager, photo: any, message: any, container: HTMLDivElement) {
//container.classList.add('photo');
let peerID = this.peerID;
let size = appPhotosManager.setAttachmentSize(photo.id, container);
let image = container.firstElementChild as HTMLImageElement || new Image();
//let size = appPhotosManager.setAttachmentSize(photo.id, image);
image.setAttribute('message-id', message.mid);
if(!container.contains(image)) {
container.append(image);
}
let preloader = new ProgressivePreloader(container, false);
let load = () => appPhotosManager.preloadPhoto(photo.id, size).then((blob) => {
if(this.peerID != peerID) {
this.log.warn('peer changed');
return;
}
image.src = URL.createObjectURL(blob);
preloader.detach();
//image.style.width = '';
//image.style.height = '';
//container.style.width = '';
//container.style.height = '';
});
console.log('wrapPhoto', load, container, image);
return this.loadMediaQueue ? this.loadMediaQueuePush(load) : load();
}
export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: () => boolean, lazyLoadQueue?: LazyLoadQueue, group?: string, canvas?: boolean, play = false) {
let stickerType = doc.mime_type == "application/x-tgsticker" ? 2 : (doc.mime_type == "image/webp" ? 1 : 0);
if(!stickerType) {
console.error('wrong doc for wrapSticker!', doc, div);
}
//console.log('wrap sticker', doc);
if(doc.thumbs && !div.firstElementChild) {
let thumb = doc.thumbs[0];
if(thumb.bytes) {
apiFileManager.saveSmallFile(thumb.location, thumb.bytes);
appPhotosManager.setAttachmentPreview(thumb.bytes, div, true);
}
}
let load = () => apiFileManager.downloadSmallFile({
_: 'inputDocumentFileLocation',
access_hash: doc.access_hash,
file_reference: doc.file_reference,
thumb_size: ''/* document.thumbs[0].type */,
id: doc.id,
stickerType: stickerType
}, {mimeType: doc.mime_type, dcID: doc.dc_id}).then(blob => {
//console.log('loaded sticker:', blob, div);
if(middleware && !middleware()) return;
if(div.firstElementChild) {
div.firstElementChild.remove();
}
if(stickerType == 2) {
const reader = new FileReader();
reader.addEventListener('loadend', async(e) => {
// @ts-ignore
const text = e.srcElement.result;
let json = await CryptoWorker.gzipUncompress<string>(text, true);
let animation = await LottieLoader.loadAnimation({
container: div,
loop: false,
autoplay: false,
animationData: JSON.parse(json),
renderer: canvas ? 'canvas' : 'svg'
}, group);
if(!canvas) {
div.addEventListener('mouseover', (e) => {
let animation = LottieLoader.getAnimation(div, group);
if(animation) {
//console.log('sticker hover', animation, div);
// @ts-ignore
animation.loop = true;
// @ts-ignore
if(animation.currentFrame == animation.totalFrames - 1) {
animation.goToAndPlay(0, true);
} else {
animation.play();
}
div.addEventListener('mouseout', () => {
// @ts-ignore
animation.loop = false;
}, {once: true});
}
});
} /* else {
let canvas = div.firstElementChild as HTMLCanvasElement;
if(!canvas.width && !canvas.height) {
console.log('Need lottie resize');
// @ts-ignore
animation.resize();
}
} */
if(play) {
animation.play();
}
});
reader.readAsArrayBuffer(blob);
} else if(stickerType == 1) {
let img = new Image();
img.src = URL.createObjectURL(blob);
/* div.style.height = doc.h + 'px';
div.style.width = doc.w + 'px'; */
div.append(img);
}
div.setAttribute('file-id', doc.id);
appStickersManager.saveSticker(doc);
});
return lazyLoadQueue ? (lazyLoadQueue.push({div, load}), Promise.resolve()) : load();
}

View File

@ -23,7 +23,7 @@ export class AppChatsManager {
}
public saveApiChats(apiChats: any[]) {
apiChats.forEach(this.saveApiChat.bind(this));
apiChats.forEach(chat => this.saveApiChat(chat));
}
public saveApiChat(apiChat: any) {
@ -254,27 +254,6 @@ export class AppChatsManager {
}
return participants;
}
/* public openChat(chatID: number, accessHash: string) {
var scope = $rootScope.$new()
scope.chatID = chatID
if(this.isChannel(chatID)) {
var modalInstance = $modal.open({
templateUrl: templateUrl('channel_modal'),
controller: 'ChannelModalController',
scope: scope,
windowClass: 'chat_modal_window channel_modal_window mobile_modal'
})
} else {
var modalInstance = $modal.open({
templateUrl: templateUrl('chat_modal'),
controller: 'ChatModalController',
scope: scope,
windowClass: 'chat_modal_window mobile_modal'
})
}
} */
}
export default new AppChatsManager();

View File

@ -22,11 +22,14 @@ type DialogDom = {
export class AppDialogsManager {
public chatList = document.getElementById('dialogs') as HTMLUListElement;
public chatListArchived = document.getElementById('dialogs-archived') as HTMLUListElement;
public pinnedDelimiter: HTMLDivElement;
public chatsHidden: any;
public myID = 0;
public doms: {[x: number]: any} = {};
public doms: {[peerID: number]: DialogDom} = {};
public domsArchived: {[peerID: number]: DialogDom} = {};
public lastActiveListElement: HTMLElement = null;
constructor() {
this.pinnedDelimiter = document.createElement('div');
@ -56,6 +59,10 @@ export class AppDialogsManager {
return;
}
if(this.lastActiveListElement) {
this.lastActiveListElement.classList.remove('active');
}
if(elem) {
/* if(chatClosedDiv) {
chatClosedDiv.style.display = 'none';
@ -66,6 +73,8 @@ export class AppDialogsManager {
let peerID = +elem.getAttribute('data-peerID');
let lastMsgID = +elem.getAttribute('data-mid');
appImManager.setPeer(peerID, lastMsgID);
elem.classList.add('active');
this.lastActiveListElement = elem;
} else /* if(chatClosedDiv) */ {
appImManager.setPeer(0);
//chatClosedDiv.style.display = '';
@ -140,7 +149,7 @@ export class AppDialogsManager {
return true;
}
public sortDom() {
public sortDom(archived = false) {
//return;
let dialogs = appMessagesManager.dialogsStorage.dialogs;
@ -153,11 +162,17 @@ export class AppDialogsManager {
let dialog = dialogs[i];
if(!dialog.pFlags.pinned) break;
pinnedDialogs.push(dialog);
}
let dom = this.getDialogDom(dialog.peerID);
if(pinnedDialogs.length) {
let dom = this.getDialogDom(pinnedDialogs[pinnedDialogs.length - 1].peerID);
if(dom) {
dom.listEl.append(this.pinnedDelimiter);
}
} else {
if(this.pinnedDelimiter.parentElement) {
this.pinnedDelimiter.parentElement.removeChild(this.pinnedDelimiter);
}
}
let sorted = dialogs
@ -372,13 +387,14 @@ export class AppDialogsManager {
}
public getDialogDom(peerID: number) {
return this.doms[peerID] as DialogDom;
return this.doms[peerID] || this.domsArchived[peerID];
}
public addDialog(dialog: {
peerID: number,
pFlags: any,
peer: any
peer: any,
folder_id?: number
}, container?: HTMLUListElement, drawStatus = true) {
let peerID: number = dialog.peerID;
@ -481,7 +497,14 @@ export class AppDialogsManager {
};
if(!container) {
if(dialog.folder_id) {
this.chatListArchived.append(li);
this.domsArchived[dialog.peerID] = dom;
} else {
this.chatList.append(li);
this.doms[dialog.peerID] = dom;
}
//this.appendTo.push(li);
if(dialog.pFlags.pinned) {
@ -490,7 +513,6 @@ export class AppDialogsManager {
dom.listEl.append(this.pinnedDelimiter);
}
this.doms[dialog.peerID] = dom;
this.setLastMessage(dialog);
} else {
container.append(li);

View File

@ -4,7 +4,7 @@ import appUsersManager from "./appUsersManager";
import appMessagesManager from "./appMessagesManager";
import appPeersManager from "./appPeersManager";
import appProfileManager from "./appProfileManager";
import { ProgressivePreloader, wrapDocument, wrapSticker, wrapVideo, wrapPhoto, openBtnMenu, LazyLoadQueue } from "../../components/misc";
//import { ProgressivePreloader, wrapDocument, wrapSticker, wrapVideo, wrapPhoto, openBtnMenu, LazyLoadQueue } from "../../components/misc";
import appDialogsManager from "./appDialogsManager";
import { RichTextProcessor } from "../richtextprocessor";
import appPhotosManager from "./appPhotosManager";
@ -19,6 +19,10 @@ import appChatsManager from "./appChatsManager";
import appMessagesIDsManager from "./appMessagesIDsManager";
import apiUpdatesManager from './apiUpdatesManager';
import initEmoticonsDropdown, { EMOTICONSSTICKERGROUP } from '../../components/emoticonsDropdown';
import LazyLoadQueue from '../../components/lazyLoadQueue';
import { wrapDocument, wrapPhoto, wrapVideo, wrapSticker } from '../../components/wrappers';
import ProgressivePreloader from '../../components/preloader';
import { openBtnMenu } from '../../components/misc';
console.log('appImManager included!');
@ -213,7 +217,7 @@ class ChatInput {
event.preventDefault();
});
/* this.messageInput.addEventListener('paste', (e) => {
this.messageInput.addEventListener('paste', (e) => {
e.preventDefault();
// @ts-ignore
let text = (e.originalEvent || e).clipboardData.getData('text/plain');
@ -229,7 +233,7 @@ class ChatInput {
// @ts-ignore
//console.log('paste text', text, );
window.document.execCommand('insertHTML', false, text);
}); */
});
let attachFile = (file: File) => {
console.log('selected file:', file, typeof(file));
@ -240,6 +244,8 @@ class ChatInput {
this.attachMediaPopUp.captionInput.value = '';
this.attachMediaPopUp.mediaContainer.innerHTML = '';
this.attachMediaPopUp.mediaContainer.style.width = '';
this.attachMediaPopUp.mediaContainer.style.height = '';
switch(willAttach) {
case 'media': {
@ -248,11 +254,14 @@ class ChatInput {
img.onload = () => {
willAttachWidth = img.naturalWidth;
willAttachHeight = img.naturalHeight;
let {w, h} = calcImageInBox(willAttachWidth, willAttachHeight, 378, 256);
this.attachMediaPopUp.mediaContainer.style.width = w + 'px';
this.attachMediaPopUp.mediaContainer.style.height = h + 'px';
this.attachMediaPopUp.mediaContainer.append(img);
};
this.attachMediaPopUp.titleEl.innerText = 'Send Photo';
this.attachMediaPopUp.mediaContainer.append(img);
this.attachMediaPopUp.container.classList.add('active');
break;
@ -308,7 +317,7 @@ class ChatInput {
// @ts-ignore
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
//console.log(items); // will give you the mime types
//console.log('item', event.clipboardData.getData());
for(let i = 0; i < items.length; ++i) {
if(items[i].kind == 'file') {
event.cancelBubble = true;
@ -526,15 +535,42 @@ export class AppImManager {
let msgIDs = msgIDsByPeer[this.peerID];
this.renderMessagesByIDs(msgIDs);
appDialogsManager.sortDom();
});
$rootScope.$on('history_delete', (e: CustomEvent) => {
let detail: {
peerID: string,
msgs: {[x: number]: boolean}
} = e.detail;
this.deleteMessagesByIDs(Object.keys(detail.msgs).map(s => +s));
});
// Calls when message successfully sent and we have an ID
$rootScope.$on('message_sent', (e: CustomEvent) => {
let {tempID, mid} = e.detail;
this.log('message_sent', e.detail);
let bubble = this.bubbles[tempID];
if(bubble) {
this.bubbles[mid] = bubble;
this.log('message_sent', bubble);
let media = bubble.querySelector('img, video');
if(media) {
media.setAttribute('message-id', mid);
}
bubble.classList.remove('is-sending');
bubble.classList.add('is-sent');
delete this.bubbles[tempID];
} else {
this.log.warn('message_sent there is no bubble', e.detail);
}
let length = this.unreadOut.length;
@ -964,10 +1000,12 @@ export class AppImManager {
let length = history.length; */
// filter negative ids
let lastBadIndex = 0;
for(let i = 0; i < history.length; ++i) {
if(history[i] <= 0) history.splice(i, 1);
if(history[i] <= 0) lastBadIndex = i;
else break;
}
history = history.slice(lastBadIndex + 1);
this.getHistoryTimeout = 0;
@ -978,6 +1016,11 @@ export class AppImManager {
let bubble = this.bubbles[msgID];
if(!bubble) {
this.log.error('no bubble by msgID:', msgID);
continue;
}
if(isElementInViewport(bubble)) {
willLoad = true;
@ -994,6 +1037,9 @@ export class AppImManager {
}
let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0];
if(!dialog) {
return;
}
// if scroll down after search
if(!willLoad && history.indexOf(/* this.lastDialog */dialog.top_message) === -1) {
@ -1162,7 +1208,7 @@ export class AppImManager {
}
if(this.bubbles[lastMsgID]) {
if(lastMsgID == this.lastDialog.top_message) {
if(this.lastDialog && lastMsgID == this.lastDialog.top_message) {
this.scroll.scrollTop = this.scroll.scrollHeight;
} else {
this.bubbles[lastMsgID].scrollIntoView();
@ -1179,45 +1225,47 @@ export class AppImManager {
this.peerID = $rootScope.selectedPeerID = peerID;
// no dialog
if(!appMessagesManager.getDialogByPeerID(this.peerID).length) {
/* if(!appMessagesManager.getDialogByPeerID(this.peerID).length) {
this.log.error('No dialog by peerID:', this.peerID);
return Promise.reject();
}
} */
this.pinnedMessageContainer.style.display = 'none';
this.preloader.attach(this.chatInner);
if(this.lastDialog) {
let lastDom = appDialogsManager.getDialogDom(this.lastDialog.peerID);
lastDom.listEl.classList.remove('active');
}
let dialog = this.lastDialog = appMessagesManager.getDialogByPeerID(this.peerID)[0];
let dialog = this.lastDialog = appMessagesManager.getDialogByPeerID(this.peerID)[0] || null;
this.log('setPeer peerID:', this.peerID, dialog);
appDialogsManager.loadDialogPhoto(this.avatarEl, dialog.peerID);
appDialogsManager.loadDialogPhoto(appSidebarRight.profileElements.avatar, dialog.peerID);
appDialogsManager.loadDialogPhoto(this.avatarEl, this.peerID);
appDialogsManager.loadDialogPhoto(appSidebarRight.profileElements.avatar, this.peerID);
this.firstTopMsgID = dialog.top_message || 0;
this.firstTopMsgID = dialog ? dialog.top_message : 0;
let dom = appDialogsManager.getDialogDom(this.peerID);
/* let dom = appDialogsManager.getDialogDom(this.peerID);
if(!dom) {
this.log.warn('No rendered dialog by peerID:', this.peerID);
appDialogsManager.addDialog(dialog);
dom = appDialogsManager.getDialogDom(this.peerID);
}
// warning need check
dom.listEl.classList.add('active');
dom.listEl.classList.add('active'); */
this.setPeerStatus();
this.titleEl.innerHTML = appSidebarRight.profileElements.name.innerHTML = dom.titleSpan.innerHTML;
//this.titleEl.innerHTML = appSidebarRight.profileElements.name.innerHTML = dom.titleSpan.innerHTML;
this.titleEl.innerHTML = appSidebarRight.profileElements.name.innerHTML = appPeersManager.getPeerTitle(this.peerID);
this.topbar.style.display = '';
appSidebarRight.toggleSidebar(true);
this.chatInput.style.display = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID) ? 'none' : '';
if(appPeersManager.isAnyGroup(peerID)) {
this.chatInner.classList.add('is-chat');
} else {
this.chatInner.classList.remove('is-chat');
}
return Promise.all([
this.getHistory(lastMsgID).then(() => {
this.log('setPeer removing preloader');
@ -1230,7 +1278,7 @@ export class AppImManager {
} else {
this.scroll.scrollTop = this.scroll.scrollHeight;
}
} else if(dialog.top_message) { // add last message, bc in getHistory will load < max_id
} else if(dialog && dialog.top_message) { // add last message, bc in getHistory will load < max_id
this.renderMessage(appMessagesManager.getMessage(dialog.top_message));
}
@ -1240,10 +1288,10 @@ export class AppImManager {
this.preloader.detach();
setTimeout(() => {
//setTimeout(() => {
//appSidebarRight.fillProfileElements();
appSidebarRight.loadSidebarMedia();
}, 0);
//}, 500);
return true;
})/* .catch(err => {
@ -1271,15 +1319,17 @@ export class AppImManager {
}
public updateUnreadByDialog(dialog: any) {
let maxID = dialog.read_outbox_max_id;
let maxID = this.peerID == this.myID ? dialog.read_inbox_max_id : dialog.read_outbox_max_id;
this.log('updateUnreadByDialog', maxID, dialog, this.unreadOut);
let length = this.unreadOut.length;
for(let i = length - 1; i >= 0; --i) {
let msgID = this.unreadOut[i];
if(msgID <= maxID) {
if(msgID > 0 && msgID <= maxID) {
let bubble = this.bubbles[msgID];
bubble.classList.remove('sent');
bubble.classList.add('read');
bubble.classList.remove('is-sent');
bubble.classList.add('is-read');
this.unreadOut.splice(i, 1);
}
}
@ -1391,8 +1441,10 @@ export class AppImManager {
//bubble.prepend(timeSpan, messageDiv); // that's bad
if(our) {
if(message.pFlags.unread) this.unreadOut.push(message.mid);
let status = message.pFlags.unread ? 'sent' : 'read';
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';
bubble.classList.add(status);
} else {
//this.log('not our message', message, message.pFlags.unread);
@ -1416,7 +1468,7 @@ export class AppImManager {
switch(pending.type) {
case 'photo': {
if(pending.size < 1e6) {
if(pending.size < 5e6) {
let img = new Image();
img.src = URL.createObjectURL(pending.file);
@ -1487,8 +1539,6 @@ export class AppImManager {
let textDiv = document.createElement('div');
textDiv.classList.add('text');
let loadedVideo = false;
let preview: HTMLDivElement = null;
if(webpage.photo || webpage.document) {
preview = document.createElement('div');
@ -1631,7 +1681,7 @@ export class AppImManager {
let nameDiv = document.createElement('div');
nameDiv.classList.add('name');
nameDiv.innerHTML = 'Forwarded from ' + title;
nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID, false);
//nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID, false);
bubble.append(nameDiv);
}
} else {
@ -1768,9 +1818,9 @@ export class AppImManager {
this.chatInner.append(containerDiv);
}
if(bubble.classList.contains('webpage')) {
/* if(bubble.classList.contains('webpage')) {
this.log('night running', bubble, bubble.scrollHeight);
}
} */
//return //this.scrollPosition.restore();
@ -1825,7 +1875,7 @@ export class AppImManager {
public getHistory(maxID = 0, reverse = false, isBackLimit = false) {
let peerID = this.peerID;
if(!maxID && this.lastDialog.top_message) {
if(!maxID && this.lastDialog && this.lastDialog.top_message) {
maxID = this.lastDialog.top_message/* + 1 */;
}

View File

@ -1,13 +1,15 @@
import { MTDocument, ProgressivePreloader, wrapVideo } from "../../components/misc";
//import { MTDocument, ProgressivePreloader, wrapVideo } from "../../components/misc";
import appPeersManager from "./appPeersManager";
import appDialogsManager from "./appDialogsManager";
import appPhotosManager from "./appPhotosManager";
import appSidebarRight from "./appSidebarRight";
import { $rootScope } from "../utils";
import appMessagesManager from "./appMessagesManager";
import { CancellablePromise } from "../mtproto/apiFileManager";
//import { CancellablePromise } from "../mtproto/apiFileManager";
import { RichTextProcessor } from "../richtextprocessor";
import { logger } from "../polyfill";
import ProgressivePreloader from "../../components/preloader";
import { wrapVideo } from "../../components/wrappers";
export class AppMediaViewer {
private overlaysDiv = document.querySelector('.overlays') as HTMLDivElement;

View File

@ -4,7 +4,7 @@ import appChatsManager from "./appChatsManager";
import appUsersManager from "./appUsersManager";
import { RichTextProcessor } from "../richtextprocessor";
import { nextRandomInt, bigint } from "../bin_utils";
import { MTProto, telegramMeWebService } from "../mtproto/mtproto";
import { telegramMeWebService } from "../mtproto/mtproto";
import apiUpdatesManager from "./apiUpdatesManager";
import appPhotosManager from "./appPhotosManager";
@ -12,9 +12,12 @@ import AppStorage from '../storage';
import AppPeersManager from "./appPeersManager";
import ServerTimeManager from "../mtproto/serverTimeManager";
import apiFileManager, { CancellablePromise } from "../mtproto/apiFileManager";
import { MTDocument, ProgressivePreloader } from "../../components/misc";
import appDocsManager from "./appDocsManager";
import appImManager from "./appImManager";
import { MTDocument } from "../../components/wrappers";
import ProgressivePreloader from "../../components/preloader";
import serverTimeManager from "../mtproto/serverTimeManager";
import apiManager from "../mtproto/apiManager";
type HistoryStorage = {
count: number | null,
@ -221,7 +224,7 @@ export class AppMessagesManager {
to_id: AppPeersManager.getOutputPeer(peerID),
flags: flags,
pFlags: pFlags,
date: tsNow(true) + MTProto.serverTimeManager.serverTimeOffset,
date: tsNow(true) + serverTimeManager.serverTimeOffset,
message: text,
random_id: randomIDS,
reply_to_msg_id: replyToMsgID,
@ -274,7 +277,7 @@ export class AppMessagesManager {
var apiPromise: any;
if(options.viaBotID) {
apiPromise = MTProto.apiManager.invokeApi('messages.sendInlineBotResult', {
apiPromise = apiManager.invokeApi('messages.sendInlineBotResult', {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
random_id: randomID,
@ -287,7 +290,7 @@ export class AppMessagesManager {
flags |= 8;
}
apiPromise = MTProto.apiManager.invokeApi('messages.sendMessage', {
apiPromise = apiManager.invokeApi('messages.sendMessage', {
flags: flags,
no_webpage: noWebPage,
peer: AppPeersManager.getInputPeerByID(peerID),
@ -523,7 +526,7 @@ export class AppMessagesManager {
let invoke = (flags: number, inputMedia: any) => {
appImManager.setTyping('sendMessageCancelAction');
return MTProto.apiManager.invokeApi('messages.sendMedia', {
return apiManager.invokeApi('messages.sendMedia', {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
media: inputMedia,
@ -767,7 +770,7 @@ export class AppMessagesManager {
var flags = 0;
if(this.dialogsOffsetDate) {
offsetDate = this.dialogsOffsetDate + MTProto.serverTimeManager.serverTimeOffset;
offsetDate = this.dialogsOffsetDate + serverTimeManager.serverTimeOffset;
offsetIndex = this.dialogsOffsetDate * 0x10000;
flags |= 1;
}
@ -776,7 +779,7 @@ export class AppMessagesManager {
/* let id = 296814355;
hash = (((hash * 0x4F25) & 0x7FFFFFFF) + id) & 0x7FFFFFFF; */
return MTProto.apiManager.invokeApi('messages.getDialogs', {
return apiManager.invokeApi('messages.getDialogs', {
flags: flags,
offset_date: offsetDate,
offset_id: appMessagesIDsManager.getMessageLocalID(offsetID),
@ -851,7 +854,7 @@ export class AppMessagesManager {
public generateDialogIndex(date?: any) {
if(date === undefined) {
date = tsNow(true) + MTProto.serverTimeManager.serverTimeOffset;
date = tsNow(true) + serverTimeManager.serverTimeOffset;
}
return (date * 0x10000) + ((++this.dialogsNum) & 0xFFFF);
}
@ -925,7 +928,7 @@ export class AppMessagesManager {
console.log('will reloadConversation', peerID);
return MTProto.apiManager.invokeApi('messages.getPeerDialogs', {
return apiManager.invokeApi('messages.getPeerDialogs', {
peers: peers
}).then(this.applyConversations.bind(this));
}
@ -972,7 +975,7 @@ export class AppMessagesManager {
apiMessage.reply_to_mid = appMessagesIDsManager.getFullMessageID(apiMessage.reply_to_msg_id, channelID);
}
apiMessage.date -= MTProto.serverTimeManager.serverTimeOffset;
apiMessage.date -= serverTimeManager.serverTimeOffset;
apiMessage.peerID = peerID;
apiMessage.fromID = apiMessage.pFlags.post ? peerID : apiMessage.from_id;
@ -993,7 +996,7 @@ export class AppMessagesManager {
apiMessage.fwdPostID = fwdHeader.channel_post;
}
fwdHeader.date -= MTProto.serverTimeManager.serverTimeOffset;
fwdHeader.date -= serverTimeManager.serverTimeOffset;
}
if(apiMessage.via_bot_id > 0) {
@ -1579,7 +1582,7 @@ export class AppMessagesManager {
notification.silent = message.pFlags.silent || false
if(notificationPhoto.location && !notificationPhoto.location.empty) {
MTProto.apiFileManager.downloadSmallFile(notificationPhoto.location/* , notificationPhoto.size */)
apiFileManager.downloadSmallFile(notificationPhoto.location/* , notificationPhoto.size */)
.then((blob) => {
if(message.pFlags.unread) {
notification.image = blob
@ -1798,7 +1801,7 @@ export class AppMessagesManager {
var apiPromise
if(peerID || !query) {
apiPromise = MTProto.apiManager.invokeApi('messages.search', {
apiPromise = apiManager.invokeApi('messages.search', {
flags: 0,
peer: AppPeersManager.getInputPeerByID(peerID),
q: query || '',
@ -1826,7 +1829,7 @@ export class AppMessagesManager {
offsetPeerID = this.getMessagePeer(offsetMessage);
}
apiPromise = MTProto.apiManager.invokeApi('messages.searchGlobal', {
apiPromise = apiManager.invokeApi('messages.searchGlobal', {
q: query,
offset_rate: offsetRate,
offset_peer: AppPeersManager.getInputPeerByID(offsetPeerID),
@ -1950,12 +1953,12 @@ export class AppMessagesManager {
var apiPromise: any;
if(isChannel) {
apiPromise = MTProto.apiManager.invokeApi('channels.readHistory', {
apiPromise = apiManager.invokeApi('channels.readHistory', {
channel: appChatsManager.getChannelInput(-peerID),
max_id: maxID
});
} else {
apiPromise = MTProto.apiManager.invokeApi('messages.readHistory', {
apiPromise = apiManager.invokeApi('messages.readHistory', {
peer: AppPeersManager.getInputPeerByID(peerID),
max_id: maxID
}).then((affectedMessages: any) => {
@ -2041,7 +2044,7 @@ export class AppMessagesManager {
let msgIDs = splitted.msgIDs[channelID];
if(channelID > 0) {
MTProto.apiManager.invokeApi('channels.readMessageContents', {
apiManager.invokeApi('channels.readMessageContents', {
channel: appChatsManager.getChannelInput(channelID),
id: msgIDs
}).then(() => {
@ -2055,7 +2058,7 @@ export class AppMessagesManager {
});
});
} else {
MTProto.apiManager.invokeApi('messages.readMessageContents', {
apiManager.invokeApi('messages.readMessageContents', {
id: msgIDs
}).then((affectedMessages: any) => {
apiUpdatesManager.processUpdateMessage({
@ -2096,7 +2099,7 @@ export class AppMessagesManager {
var msgs: any = {}
msgs[tempID] = true;
$rootScope.$broadcast('history_delete', {peerID: peerID, msgs: msgs});
//$rootScope.$broadcast('history_delete', {peerID: peerID, msgs: msgs}); // commented 11.02.2020
this.finalizePendingMessageCallbacks(tempID, mid);
} else {
@ -2255,7 +2258,7 @@ export class AppMessagesManager {
case 'updatePinnedDialogs': {
var newPinned: any = {};
if(!update.order) {
MTProto.apiManager.invokeApi('messages.getPinnedDialogs', {}).then((dialogsResult: any) => {
apiManager.invokeApi('messages.getPinnedDialogs', {}).then((dialogsResult: any) => {
dialogsResult.dialogs.reverse();
this.applyConversations(dialogsResult);
@ -2343,7 +2346,7 @@ export class AppMessagesManager {
} else {
var msgs: any = {};
msgs[mid] = true;
$rootScope.$broadcast('history_delete', {peerID: peerID, msgs: msgs});
/////////$rootScope.$broadcast('history_delete', {peerID: peerID, msgs: msgs}); // commented 11.02.2020
}
} else {
$rootScope.$broadcast('message_edit', {
@ -2654,7 +2657,7 @@ export class AppMessagesManager {
to_id: AppPeersManager.getOutputPeer(peerID),
flags: 0,
pFlags: {unread: true},
date: (update.inbox_date || tsNow(true)) + MTProto.serverTimeManager.serverTimeOffset,
date: (update.inbox_date || tsNow(true)) + serverTimeManager.serverTimeOffset,
message: update.message,
media: update.media,
entities: update.entities
@ -2845,7 +2848,7 @@ export class AppMessagesManager {
max_seen_msg: maxID
});
MTProto.apiManager.invokeApi('messages.receivedMessages', {
apiManager.invokeApi('messages.receivedMessages', {
max_id: maxID
});
}
@ -3079,7 +3082,7 @@ export class AppMessagesManager {
//console.trace('requestHistory', peerID, maxID, limit, offset);
return MTProto.apiManager.invokeApi('messages.getHistory', {
return apiManager.invokeApi('messages.getHistory', {
peer: AppPeersManager.getInputPeerByID(peerID),
offset_id: maxID ? appMessagesIDsManager.getMessageLocalID(maxID) : 0,
offset_date: 0,
@ -3124,7 +3127,7 @@ export class AppMessagesManager {
to_id: AppPeersManager.getOutputPeer(peerID),
flags: 0,
pFlags: {},
date: tsNow(true) + MTProto.serverTimeManager.serverTimeOffset,
date: tsNow(true) + serverTimeManager.serverTimeOffset,
action: {
_: 'messageActionBotIntro',
description: description
@ -3233,12 +3236,12 @@ export class AppMessagesManager {
var promise;
channelID = +channelID;
if(channelID > 0) {
promise = MTProto.apiManager.invokeApi('channels.getMessages', {
promise = apiManager.invokeApi('channels.getMessages', {
channel: appChatsManager.getChannelInput(channelID),
id: msgIDs
});
} else {
promise = MTProto.apiManager.invokeApi('messages.getMessages', {
promise = apiManager.invokeApi('messages.getMessages', {
id: msgIDs
});
}

View File

@ -105,6 +105,22 @@ const AppPeersManager = {
return (peerID < 0) && appChatsManager.isChannel(-peerID);
},
isMegagroup: (peerID: number) => {
return (peerID < 0) && appChatsManager.isMegagroup(-peerID);
},
isAnyGroup: (peerID: number): boolean => {
return (peerID < 0) && !appChatsManager.isBroadcast(-peerID);
},
isBroadcast: (id: number): boolean => {
return AppPeersManager.isChannel(id) && !AppPeersManager.isMegagroup(id);
},
isBot: (peerID: number): boolean => {
return (peerID > 0) && appUsersManager.isBot(peerID);
},
getInputPeerByID: (peerID: number) => {
if (!peerID) {
return {_: 'inputPeerEmpty'}
@ -137,10 +153,6 @@ const AppPeersManager = {
return color;
},
isMegagroup: (peerID: number) => {
return (peerID < 0) && appChatsManager.isMegagroup(-peerID);
},
getPeerSearchText: (peerID: number) => {
var text
if(peerID > 0) {

View File

@ -1,9 +1,11 @@
import { MTProto } from "../mtproto/mtproto";
import appUsersManager from "./appUsersManager";
import { copy, calcImageInBox } from "../utils";
import fileManager from '../filemanager';
import { bytesFromHex } from "../bin_utils";
import { MTPhotoSize } from "../../components/misc";
import { MTPhotoSize } from "../../components/wrappers";
import apiFileManager from "../mtproto/apiFileManager";
import apiManager from "../mtproto/apiManager";
//import { MTPhotoSize } from "../../components/misc";
type MTPhoto = {
_: 'photo',
@ -53,7 +55,7 @@ export class AppPhotosManager {
apiPhoto.sizes.forEach((photoSize: any) => {
if(photoSize._ == 'photoCachedSize') {
MTProto.apiFileManager.saveSmallFile(photoSize.location, photoSize.bytes);
apiFileManager.saveSmallFile(photoSize.location, photoSize.bytes);
console.log('clearing photo cached size', apiPhoto);
@ -109,7 +111,7 @@ export class AppPhotosManager {
public getUserPhotos(userID: number, maxID: number, limit: number) {
var inputUser = appUsersManager.getUserInput(userID);
return MTProto.apiManager.invokeApi('photos.getUserPhotos', {
return apiManager.invokeApi('photos.getUserPhotos', {
user_id: inputUser,
offset: 0,
limit: limit || 20,
@ -221,26 +223,26 @@ export class AppPhotosManager {
} : photoSize.location;
/* if(overwrite) {
await MTProto.apiFileManager.deleteFile(location);
await apiFileManager.deleteFile(location);
console.log('Photos deleted file!');
} */
if(isPhoto/* && photoSize.size >= 1e6 */) {
console.log('Photos downloadFile exec', photo);
/* let promise = MTProto.apiFileManager.downloadFile(photo.dc_id, location, photoSize.size);
/* let promise = apiFileManager.downloadFile(photo.dc_id, location, photoSize.size);
let blob = await promise;
if(blob.size < photoSize.size && overwrite) {
await MTProto.apiFileManager.deleteFile(location);
await apiFileManager.deleteFile(location);
console.log('Photos deleted file!');
return MTProto.apiFileManager.downloadFile(photo.dc_id, location, photoSize.size);
return apiFileManager.downloadFile(photo.dc_id, location, photoSize.size);
}
return blob; */
return MTProto.apiFileManager.downloadFile(photo.dc_id, location, photoSize.size);
return apiFileManager.downloadFile(photo.dc_id, location, photoSize.size);
} else {
console.log('Photos downloadSmallFile exec', photo, location);
return MTProto.apiFileManager.downloadSmallFile(location);
return apiFileManager.downloadSmallFile(location);
}
} else return Promise.reject('no photoSize');
}
@ -359,7 +361,7 @@ export class AppPhotosManager {
fileManager.chooseSaveFile(fileName, ext, mimeType).then((writableFileEntry) => {
if(writableFileEntry) {
MTProto.apiFileManager.downloadFile(photo.dc_id, inputFileLocation, fullPhotoSize.size, {
apiFileManager.downloadFile(photo.dc_id, inputFileLocation, fullPhotoSize.size, {
mimeType: mimeType,
toFileEntry: writableFileEntry
}).then(() => {
@ -369,12 +371,12 @@ export class AppPhotosManager {
});
}
}, () => {
var cachedBlob = MTProto.apiFileManager.getCachedFile(inputFileLocation)
var cachedBlob = apiFileManager.getCachedFile(inputFileLocation)
if (cachedBlob) {
return fileManager.download(cachedBlob, mimeType, fileName);
}
MTProto.apiFileManager.downloadFile(photo.dc_id, inputFileLocation, fullPhotoSize.size, {mimeType: mimeType})
apiFileManager.downloadFile(photo.dc_id, inputFileLocation, fullPhotoSize.size, {mimeType: mimeType})
.then((blob: Blob) => {
fileManager.download(blob, mimeType, fileName);
}, (e: any) => {

View File

@ -1,11 +1,42 @@
import { logger } from "../polyfill";
import { putPreloader } from "../../components/misc";
import { putPreloader, formatPhoneNumber } from "../../components/misc";
import Scrollable from '../../components/scrollable';
import appMessagesManager from "./appMessagesManager";
import appDialogsManager from "./appDialogsManager";
import { isElementInViewport } from "../utils";
import { isElementInViewport, numberWithCommas } from "../utils";
import appMessagesIDsManager from "./appMessagesIDsManager";
import appImManager from "./appImManager";
import appUsersManager from "./appUsersManager";
import { appPeersManager } from "../services";
class SearchGroup {
container: HTMLDivElement;
nameEl: HTMLDivElement;
list: HTMLUListElement;
constructor(public name: string, public type: string) {
this.list = document.createElement('ul');
this.container = document.createElement('div');
this.nameEl = document.createElement('div');
this.nameEl.classList.add('search-group__name');
this.nameEl.innerText = name;
this.container.classList.add('search-group');
this.container.append(this.nameEl, this.list);
this.container.style.display = 'none';
appDialogsManager.setListClickListener(this.list);
}
clear() {
this.container.style.display = 'none';
this.list.innerHTML = '';
}
setActive() {
this.container.style.display = '';
}
}
class AppSidebarLeft {
private sidebarEl = document.querySelector('.page-chats .chats-container') as HTMLDivElement;
@ -15,10 +46,11 @@ class AppSidebarLeft {
private menuEl = this.toolsBtn.querySelector('.btn-menu');
private savedBtn = this.menuEl.querySelector('.menu-saved');
private archivedBtn = this.menuEl.querySelector('.menu-archive');
private listsContainer: HTMLDivElement = null;
private searchMessagesList: HTMLUListElement = null;
private chatsArchivedContainer = document.getElementById('chats-archived-container') as HTMLDivElement;
private chatsContainer = document.getElementById('chats-container') as HTMLDivElement;
private chatsOffsetIndex = 0;
private chatsPreloader: HTMLDivElement;
@ -40,6 +72,13 @@ class AppSidebarLeft {
public scroll: Scrollable = null;
public searchGroups: {[group: string]: SearchGroup} = {
contacts: new SearchGroup('Contacts and Chats', 'contacts'),
globalContacts: new SearchGroup('Global Search', 'contacts'),
globalMessages: new SearchGroup('Global Search', 'messages'),
privateMessages: new SearchGroup('Private Search', 'messages')
};
constructor() {
this.chatsPreloader = document.createElement('div');
this.chatsPreloader.classList.add('preloader');
@ -55,7 +94,10 @@ class AppSidebarLeft {
this.scroll.container.addEventListener('scroll', this.onChatsScroll.bind(this));
this.listsContainer = new Scrollable(this.searchContainer).container;
this.searchMessagesList = document.createElement('ul');
for(let i in this.searchGroups) {
this.listsContainer.append(this.searchGroups[i].container);
}
this.savedBtn.addEventListener('click', (e) => {
this.log('savedbtn click');
@ -64,6 +106,12 @@ class AppSidebarLeft {
}, 0);
});
this.archivedBtn.addEventListener('click', (e) => {
this.chatsArchivedContainer.classList.add('active');
this.toolsBtn.classList.remove('tgico-menu', 'btn-menu-toggle');
this.toolsBtn.classList.add('tgico-back');
});
/* this.listsContainer.insertBefore(this.searchMessagesList, this.listsContainer.lastElementChild);
for(let i = 0; i < 25; ++i) {
let li = document.createElement('li');
@ -75,8 +123,6 @@ class AppSidebarLeft {
//this.searchContainer.append(this.listsContainer);
appDialogsManager.setListClickListener(this.searchMessagesList);
let clickTimeout = 0;
this.searchInput.addEventListener('focus', (e) => {
this.toolsBtn.classList.remove('tgico-menu', 'btn-menu-toggle');
@ -84,7 +130,9 @@ class AppSidebarLeft {
this.searchContainer.classList.add('active');
if(!this.searchInput.value) {
this.searchMessagesList.innerHTML = '';
for(let i in this.searchGroups) {
this.searchGroups[i].clear();
}
}
this.searchInput.addEventListener('blur', (e) => {
@ -111,10 +159,6 @@ class AppSidebarLeft {
let value = this.searchInput.value;
this.log('input', value);
if(this.listsContainer.contains(this.searchMessagesList)) {
this.listsContainer.removeChild(this.searchMessagesList);
}
if(!value.trim()) {
return;
}
@ -124,11 +168,13 @@ class AppSidebarLeft {
this.loadedCount = 0;
this.foundCount = 0;
this.offsetRate = 0;
this.searchMessagesList.innerHTML = '';
for(let i in this.searchGroups) {
this.searchGroups[i].clear();
}
this.searchPromise = null;
this.searchMore().then(() => {
this.listsContainer.append(this.searchMessagesList);
});
this.searchMore();
});
this.toolsBtn.addEventListener('click', (e) => {
@ -138,6 +184,7 @@ class AppSidebarLeft {
this.toolsBtn.classList.add('tgico-menu', 'btn-menu-toggle');
this.toolsBtn.classList.remove('tgico-back');
this.searchContainer.classList.remove('active');
this.chatsArchivedContainer.classList.remove('active');
this.peerID = 0;
e.stopPropagation();
e.cancelBubble = true;
@ -156,6 +203,10 @@ class AppSidebarLeft {
this.onChatsScroll();
}, 0);
});
/* appUsersManager.getTopPeers().then(categories => {
this.log('got top categories:', categories);
}); */
}
public async loadDialogs() {
@ -207,7 +258,7 @@ class AppSidebarLeft {
public onSidebarScroll() {
if(!this.query.trim()) return;
let elements = Array.from(this.searchMessagesList.childNodes).slice(-5);
let elements = Array.from(this.searchGroups[this.peerID ? 'privateMessages' : 'globalMessages'].list.childNodes).slice(-5);
for(let li of elements) {
if(isElementInViewport(li)) {
this.log('Will load more search');
@ -245,6 +296,64 @@ class AppSidebarLeft {
let maxID = appMessagesIDsManager.getMessageIDInfo(this.minMsgID)[0];
if(!this.peerID && !maxID) {
appUsersManager.searchContacts(query, 20).then((contacts: any) => {
if(this.searchInput.value != query) {
return;
}
this.log('input search contacts result:', contacts);
let setResults = (results: any, group: SearchGroup, showMembersCount = false) => {
results.forEach((inputPeer: any) => {
let peerID = appPeersManager.getPeerID(inputPeer);
let peer = appPeersManager.getPeer(peerID);
let originalDialog = appMessagesManager.getDialogByPeerID(peerID)[0];
this.log('contacts peer', peer);
if(!originalDialog) {
this.log('no original dialog by peerID:', peerID);
originalDialog = {
peerID: peerID,
pFlags: {},
peer: peer
};
}
let {dialog, dom} = appDialogsManager.addDialog(originalDialog, group.list, false);
if(showMembersCount && (peer.participants_count || peer.participants)) {
let isChannel = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID);
let participants_count = peer.participants_count || peer.participants.participants.length;
let subtitle = numberWithCommas(participants_count) + ' ' + (isChannel ? 'subscribers' : 'members');
dom.lastMessageSpan.innerText = subtitle;
} else {
let username = appPeersManager.getPeerUsername(peerID);
if(!username) {
let user = appUsersManager.getUser(peerID);
if(user && user.phone) {
username = '+' + formatPhoneNumber(user.phone).formatted;
}
} else {
username = '@' + username;
}
dom.lastMessageSpan.innerText = username;
}
});
if(results.length) {
group.setActive();
}
};
setResults(contacts.my_results, this.searchGroups.contacts, true);
setResults(contacts.results, this.searchGroups.globalContacts);
});
}
return this.searchPromise = appMessagesManager.getSearch(this.peerID, query, null, maxID, 20, this.offsetRate).then(res => {
this.searchPromise = null;
@ -260,6 +369,9 @@ class AppSidebarLeft {
history.shift();
}
let searchGroup = this.searchGroups[this.peerID ? 'privateMessages' : 'globalMessages'];
searchGroup.setActive();
history.forEach((msgID: number) => {
let message = appMessagesManager.getMessage(msgID);
let originalDialog = appMessagesManager.getDialogByPeerID(message.peerID)[0];
@ -274,7 +386,7 @@ class AppSidebarLeft {
};
}
let {dialog, dom} = appDialogsManager.addDialog(originalDialog, this.searchMessagesList, false);
let {dialog, dom} = appDialogsManager.addDialog(originalDialog, searchGroup.list, false);
appDialogsManager.setLastMessage(dialog, message, dom);
});

View File

@ -1,4 +1,4 @@
import { LazyLoadQueue, horizontalMenu, wrapDocument, formatPhoneNumber } from "../../components/misc";
import { horizontalMenu, formatPhoneNumber } from "../../components/misc";
import Scrollable from '../../components/scrollable';
import { isElementInViewport, $rootScope } from "../utils";
import appMessagesManager from "./appMessagesManager";
@ -10,6 +10,8 @@ import { RichTextProcessor } from "../richtextprocessor";
import { logger } from "../polyfill";
import appImManager from "./appImManager";
import appMediaViewer from "./appMediaViewer";
import LazyLoadQueue from "../../components/lazyLoadQueue";
import { wrapDocument } from "../../components/wrappers";
class AppSidebarRight {
public sidebarEl = document.querySelector('.profile-container') as HTMLDivElement;

View File

@ -1,6 +1,7 @@
import { MTDocument } from "../../components/misc";
import AppStorage from '../storage';
import { MTProto } from "../mtproto/mtproto";
import { MTDocument } from '../../components/wrappers';
import apiManager from '../mtproto/apiManager';
import apiFileManager from '../mtproto/apiFileManager';
export type MTStickerSet = {
_: 'stickerSet',
@ -78,7 +79,7 @@ class appStickersManager {
}) {
if(this.stickerSets[set.id]) return this.stickerSets[set.id];
let promise = MTProto.apiManager.invokeApi('messages.getStickerSet', {
let promise = apiManager.invokeApi('messages.getStickerSet', {
stickerset: {
_: 'inputStickerSetID',
id: set.id,
@ -132,7 +133,7 @@ class appStickersManager {
let thumb = stickerSet.thumb;
let dcID = stickerSet.thumb_dc_id;
let promise = MTProto.apiFileManager.downloadSmallFile({
let promise = apiFileManager.downloadSmallFile({
_: 'inputStickerSetThumb',
stickerset: {
_: 'inputStickerSetID',

View File

@ -1,6 +1,8 @@
import { SearchIndexManager, safeReplaceObject, isObject, tsNow, copy, $rootScope } from "../utils";
import { MTProto } from "../mtproto/mtproto";
import { RichTextProcessor } from "../richtextprocessor";
import appChatsManager from "./appChatsManager";
import apiManager from "../mtproto/apiManager";
import serverTimeManager from "../mtproto/serverTimeManager";
export class AppUsersManager {
public users: any = {};
@ -13,7 +15,7 @@ export class AppUsersManager {
public myID: number;
constructor() {
MTProto.apiManager.getUserID().then((id) => {
apiManager.getUserID().then((id) => {
this.myID = id;
});
@ -37,11 +39,11 @@ export class AppUsersManager {
user.status = update.status;
if(user.status) {
if(user.status.expires) {
user.status.expires -= MTProto.serverTimeManager.serverTimeOffset;
user.status.expires -= serverTimeManager.serverTimeOffset;
}
if(user.status.was_online) {
user.status.was_online -= MTProto.serverTimeManager.serverTimeOffset;
user.status.was_online -= serverTimeManager.serverTimeOffset;
}
}
@ -149,8 +151,7 @@ export class AppUsersManager {
}
public saveApiUsers(apiUsers: any[]) {
// @ts-ignore
apiUsers.forEach(this.saveApiUser.bind(this));
apiUsers.forEach((user) => this.saveApiUser(user));
}
public saveApiUser(apiUser: any, noReplace?: boolean) {
@ -200,11 +201,11 @@ export class AppUsersManager {
if(apiUser.status) {
if(apiUser.status.expires) {
apiUser.status.expires -= MTProto.serverTimeManager.serverTimeOffset
apiUser.status.expires -= serverTimeManager.serverTimeOffset
}
if(apiUser.status.was_online) {
apiUser.status.was_online -= MTProto.serverTimeManager.serverTimeOffset
apiUser.status.was_online -= serverTimeManager.serverTimeOffset
}
}
@ -349,20 +350,6 @@ export class AppUsersManager {
return user;
}
/* public openUser(userID: number, override) {
var scope = $rootScope.$new()
scope.userID = userID
scope.override = override || {}
var modalInstance = $modal.open({
templateUrl: templateUrl('user_modal'),
controller: 'UserModalController',
scope: scope,
windowClass: 'user_modal_window mobile_modal',
backdrop: 'single'
})
} */
/* function importContact (phone, firstName, lastName) {
return MtpApiManager.invokeApi('contacts.importContacts', {
contacts: [{
@ -424,7 +411,7 @@ export class AppUsersManager {
ids.push(this.getUserInput(userID));
})
return MTProto.apiManager.invokeApi('contacts.deleteContacts', {
return apiManager.invokeApi('contacts.deleteContacts', {
id: ids
}).then(() => {
userIDs.forEach((userID) => {
@ -433,6 +420,35 @@ export class AppUsersManager {
});
}
public getTopPeers() {
return apiManager.invokeApi('contacts.getTopPeers', {
flags: 1,
correspondents: true,
offset: 0,
limit: 5,
hash: 0,
}).then((peers: any) => {
//console.log(peers);
this.saveApiUsers(peers.users);
appChatsManager.saveApiChats(peers.chats);
return peers.categories;
});
}
public searchContacts(query: string, limit = 20) {
return apiManager.invokeApi('contacts.search', {
q: query,
limit
}).then((peers: any) => {
//console.log(peers);
this.saveApiUsers(peers.users);
appChatsManager.saveApiChats(peers.chats);
return peers;
});
}
public onContactUpdated(userID: number, isContact: boolean) {
userID = parseInt('' + userID);
@ -453,19 +469,6 @@ export class AppUsersManager {
}
}
/* function openImportContact () {
return $modal.open({
templateUrl: templateUrl('import_contact_modal'),
controller: 'ImportContactModalController',
windowClass: 'md_simple_modal_window mobile_modal'
}).result.then(function (foundUserID) {
if (!foundUserID) {
return $q.reject()
}
return foundUserID
})
} */
public setUserStatus(userID: number, offline: boolean) {
if(this.isBot(userID)) {
return;

View File

@ -8,7 +8,7 @@ var EmojiHelper = {
var emojiData = Config.Emoji;
var emojiIconSize = emojiData.img_size;
var emojiSupported = navigator.userAgent.search(/OS X|iPhone|iPad|iOS|Android/i) != -1 && false,
var emojiSupported = navigator.userAgent.search(/OS X|iPhone|iPad|iOS|Android/i) != -1/* && false */,
emojiCode;
//var emojiRegExp = '\\u0023\\u20E3|\\u00a9|\\u00ae|\\u203c|\\u2049|\\u2139|[\\u2194-\\u2199]|\\u21a9|\\u21aa|\\u231a|\\u231b|\\u23e9|[\\u23ea-\\u23ec]|\\u23f0|\\u24c2|\\u25aa|\\u25ab|\\u25b6|\\u2611|\\u2614|\\u26fd|\\u2705|\\u2709|[\\u2795-\\u2797]|\\u27a1|\\u27b0|\\u27bf|\\u2934|\\u2935|[\\u2b05-\\u2b07]|\\u2b1b|\\u2b1c|\\u2b50|\\u2b55|\\u3030|\\u303d|\\u3297|\\u3299|[\\uE000-\\uF8FF\\u270A-\\u2764\\u2122\\u25C0\\u25FB-\\u25FE\\u2615\\u263a\\u2648-\\u2653\\u2660-\\u2668\\u267B\\u267F\\u2693\\u261d\\u26A0-\\u26FA\\u2708\\u2702\\u2601\\u260E]|[\\u2600\\u26C4\\u26BE\\u23F3\\u2764]|\\uD83D[\\uDC00-\\uDFFF]|\\uD83C[\\uDDE8-\\uDDFA\uDDEC]\\uD83C[\\uDDEA-\\uDDFA\uDDE7]|[0-9]\\u20e3|\\uD83C[\\uDC00-\\uDFFF]';
//var emojiRegExp = '\\u00a9|\\u00ae|[\\u2000-\\u3300]|\\ud83c[\\ud000-\\udfff]|\\ud83d[\\ud000-\\udfff]|\\ud83e[\\ud000-\\udfff]';

View File

@ -137,6 +137,12 @@
box-sizing: border-box;
min-height: 100%;
justify-content: flex-end;
&.is-chat {
.in {
padding-left: 36px;
}
}
}
.service {
@ -474,12 +480,12 @@
.user-avatar {
position: absolute;
left: -2.5rem;
width: 32px;
height: 32px;
line-height: 32px;
left: -3rem;
width: 40px;
height: 40px;
line-height: 40px;
bottom: 0;
font-size: .85rem;
font-size: 1rem;
}
&:not(.forwarded).hide-name, &.emoji-big {
@ -657,18 +663,28 @@
}
}
.bubble.read {
.bubble.is-read {
.time .tgico:after {
content: $tgico-checks;
}
}
.bubble.sent {
.bubble.is-sent {
.time .tgico:after {
content: $tgico-check;
}
}
.bubble.is-sending {
.time .tgico:after {
content: $tgico-sending;
}
}
.bubble.is-reply .name {
display: none;
}
.bubble {
background-color: #eeffde;
border-radius: 12px 6px 6px 12px;

View File

@ -46,6 +46,7 @@
display: grid;
grid-auto-columns: 1fr;
/* grid-gap: 4px; */
width: 100%;
}
li {
@ -133,11 +134,19 @@
.user-title {
max-width: 80%;
.emoji {
img.emoji {
vertical-align: top;
width: 18px;
height: 18px;
}
span.emoji {
overflow: visible;
margin: 0;
width: auto;
font-size: 14px;
vertical-align: unset;
}
}
.user-last-message {
@ -148,11 +157,15 @@
color: $darkblue;
}
.emoji {
font-size: 1.2rem;
img.emoji {
width: 20px;
height: 20px;
}
span.emoji {
font-size: 1.2rem;
margin: 0 .125rem;
overflow: visible;
}
}

View File

@ -108,7 +108,7 @@
content: "\e918";
}
.tgico-sending:before {
content: "\e919";
content: $tgico-sending;
}
.tgico-sendingerror:before {
content: "\e91a";

View File

@ -3,3 +3,4 @@ $tgico-font-path: "../../assets/fonts" !default;
$tgico-check: "\e900";
$tgico-checks: "\e95a";
$tgico-sending: "\e919";

View File

@ -21,7 +21,7 @@
position: relative;
}
#search-container {
#search-container, #chats-archived-container {
display: none;
width: 100%;
max-height: 100%;
@ -37,4 +37,17 @@
display: flex;
}
}
.search-group {
width: 100%;
border-bottom: 1px solid #DADCE0;
padding: 1rem 0 .5rem;
margin-bottom: .5rem;
&__name {
color: $color-gray;
padding: 0 1.85rem;
padding-bottom: 1rem;
}
}
}

View File

@ -314,7 +314,10 @@ input {
}
.c-ripple.active .c-ripple__circle {
//-webkit-animation: a-ripple 750ms ease-in;
animation: a-ripple 750ms ease-in-out;
//animation: a-ripple 750ms ease-in-out;
will-change: padding-bottom, width, opacity;
-webkit-animation: a-ripple 625ms ease-in-out;
animation: a-ripple 625ms ease-in-out;
}
/**
@ -324,6 +327,7 @@ input {
@-webkit-keyframes a-ripple {
0% {
opacity: 0;
//opacity: 1;
}
25% {
opacity: 1;
@ -337,6 +341,7 @@ input {
@keyframes a-ripple {
0% {
opacity: 0;
//opacity: 1;
}
25% {
opacity: 1;
@ -397,6 +402,12 @@ input {
}
}
&.photo {
.document-ico {
border-radius: $border-radius;
}
}
.document-name {
white-space: nowrap;
font-weight: 500;
@ -488,7 +499,7 @@ input {
transform: translateY(-50%);
background-color: #fff;
font-size: 0.85rem;
transition: .2s all;
transition: .2s all, .1s opacity;
display: inline-block;
cursor: text;
}
@ -542,6 +553,8 @@ input {
transform: none;
padding: 0 5px;
left: 7.5px;
font-size: 0.85rem!important;
opacity: 1;
}
}
}
@ -930,8 +943,6 @@ $width: 100px;
display: inline-block;
/* width: 100%;
height: 100%; */
width: 18px;
height: 18px;
max-width: 100%;
max-height: 100%;
vertical-align: middle;
@ -940,7 +951,11 @@ $width: 100px;
font-size: 1em;
font-family: apple color emoji,segoe ui emoji,noto color emoji,android emoji,emojisymbols,emojione mozilla,twemoji mozilla,segoe ui symbol;
}
img.emoji {
width: 18px;
height: 18px;
}
.popup {
@ -1309,6 +1324,7 @@ div.scrollable::-webkit-scrollbar-thumb {
.popup-send-photo {
.popup-container {
width: 420px;
max-width: 420px;
max-height: 425px;
overflow: hidden;
@ -1352,6 +1368,7 @@ div.scrollable::-webkit-scrollbar-thumb {
justify-content: center;
width: fit-content;
border-radius: $border-radius-medium;
margin: 0 auto;
/* align-items: center; */
.document {
@ -1366,6 +1383,12 @@ div.scrollable::-webkit-scrollbar-thumb {
overflow: hidden;
text-overflow: ellipsis;
}
/* &.photo {
.document-ico {
border-radius: $border-radius;
}
} */
}
img {
@ -1381,6 +1404,15 @@ div.scrollable::-webkit-scrollbar-thumb {
font-size: 1.15rem;
padding: 0 15px;
border-radius: $border-radius-medium;
&:focus {
padding: 0 14.5px;
}
}
label {
font-size: inherit;
opacity: 0;
}
}
}