Browse Source

New (not) virtual chatlist & chat slicer & prepare to fix dialogs sort

master
morethanwords 4 years ago
parent
commit
0bc57d8ea0
  1. 12
      package-lock.json
  2. 7
      package.json
  3. 6
      src/components/appSearch.ts
  4. 2
      src/components/chatInput.ts
  5. 33
      src/components/emoticonsDropdown.ts
  6. 425
      src/components/scrollable_new.ts
  7. 78
      src/lib/appManagers/appDialogsManager.ts
  8. 2
      src/lib/appManagers/appDocsManager.ts
  9. 261
      src/lib/appManagers/appImManager.ts
  10. 75
      src/lib/appManagers/appMessagesManager.ts
  11. 2
      src/lib/appManagers/appPhotosManager.ts
  12. 30
      src/lib/appManagers/appSidebarRight.ts
  13. 29
      src/lib/bin_utils.ts
  14. 12
      src/lib/crypto/crypto_utils.ts
  15. 7
      src/lib/crypto/cryptoworker.ts
  16. 42
      src/lib/mtproto/apiManager.ts
  17. 3
      src/lib/mtproto/authorizer.ts
  18. 3
      src/lib/mtproto/mtproto.ts
  19. 3
      src/lib/mtproto/mtprotoworker.ts
  20. 14
      src/lib/mtproto/networker.ts
  21. 5
      src/lib/mtproto/timeManager.ts
  22. 11
      src/lib/mtproto/tl_utils.ts
  23. 4
      src/lib/utils.js
  24. 2
      src/pages/pageSignIn.ts
  25. 6
      src/scss/partials/_chat.scss
  26. 10
      src/scss/partials/_emojiDropdown.scss
  27. 49
      src/scss/partials/_scrollable.scss
  28. 27952
      stats.json

12
package-lock.json generated

@ -11123,12 +11123,6 @@
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
}, },
"offscreen-canvas": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/offscreen-canvas/-/offscreen-canvas-0.1.1.tgz",
"integrity": "sha512-gZYQ++TK0X9BD6bnMrgk7TGQ0TCVLP5YsCOnLaCP18WpKh3dYfkt0ma4gFhElohBizSwMzjDptam2zPxMUQ2wg==",
"dev": true
},
"on-build-webpack": { "on-build-webpack": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/on-build-webpack/-/on-build-webpack-0.1.0.tgz", "resolved": "https://registry.npmjs.org/on-build-webpack/-/on-build-webpack-0.1.0.tgz",
@ -11224,12 +11218,6 @@
"os-tmpdir": "^1.0.0" "os-tmpdir": "^1.0.0"
} }
}, },
"overlayscrollbars": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-1.12.0.tgz",
"integrity": "sha512-zJGYLeBfaPx2VmiDfBMNTPzm9N8w8wZ6M7dm1ee8TGuet8tsK4nxOzGvEEu0SmueqMHQxhLsstf7iTWCGiYa9Q==",
"dev": true
},
"p-defer": { "p-defer": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",

7
package.json

@ -6,10 +6,11 @@
"scripts": { "scripts": {
"start": "webpack-dev-server --open --config webpack.dev.js", "start": "webpack-dev-server --open --config webpack.dev.js",
"start-production": "webpack-dev-server --open --config webpack.prod.js", "start-production": "webpack-dev-server --open --config webpack.prod.js",
"serve": "node server.js", "serve": "npm run build; node server.js",
"build": "webpack --config webpack.prod.js", "build": "webpack --config webpack.prod.js",
"test": "jest --config=jest.config.js", "test": "jest --config=jest.config.js",
"profile": "webpack --profile --json > stats.json --config webpack.prod.js" "profile": "webpack --profile --json > stats.json --config webpack.prod.js",
"profile-dev": "webpack --profile --json > stats.json --config webpack.dev.js"
}, },
"author": "", "author": "",
"license": "ISC", "license": "ISC",
@ -43,9 +44,7 @@
"lottie-web": "^5.6.8", "lottie-web": "^5.6.8",
"node-sass": "^4.13.1", "node-sass": "^4.13.1",
"npm": "^6.14.4", "npm": "^6.14.4",
"offscreen-canvas": "^0.1.1",
"on-build-webpack": "^0.1.0", "on-build-webpack": "^0.1.0",
"overlayscrollbars": "^1.12.0",
"pako": "^1.0.11", "pako": "^1.0.11",
"resolve-url-loader": "^3.1.1", "resolve-url-loader": "^3.1.1",
"sass-loader": "^8.0.2", "sass-loader": "^8.0.2",

6
src/components/appSearch.ts

@ -1,5 +1,5 @@
import appDialogsManager from "../lib/appManagers/appDialogsManager"; import appDialogsManager from "../lib/appManagers/appDialogsManager";
import Scrollable from "./scrollable"; import Scrollable from "./scrollable_new";
import appMessagesIDsManager from "../lib/appManagers/appMessagesIDsManager"; import appMessagesIDsManager from "../lib/appManagers/appMessagesIDsManager";
import appUsersManager from "../lib/appManagers/appUsersManager"; import appUsersManager from "../lib/appManagers/appUsersManager";
import appPeersManager from '../lib/appManagers/appPeersManager'; import appPeersManager from '../lib/appManagers/appPeersManager';
@ -147,7 +147,7 @@ export default class AppSearch {
peerID: peerID, peerID: peerID,
pFlags: {}, pFlags: {},
peer: peer peer: peer
}; } as any;
} }
let {dialog, dom} = appDialogsManager.addDialog(originalDialog, group.list, false); let {dialog, dom} = appDialogsManager.addDialog(originalDialog, group.list, false);
@ -213,7 +213,7 @@ export default class AppSearch {
peerID: message.peerID, peerID: message.peerID,
pFlags: {}, pFlags: {},
peer: message.to_id peer: message.to_id
}; } as any;
} }
let {dialog, dom} = appDialogsManager.addDialog(originalDialog, searchGroup.list, false); let {dialog, dom} = appDialogsManager.addDialog(originalDialog, searchGroup.list, false);

2
src/components/chatInput.ts

@ -1,4 +1,4 @@
import Scrollable from "./scrollable"; import Scrollable from "./scrollable_new";
import LazyLoadQueue from "./lazyLoadQueue"; import LazyLoadQueue from "./lazyLoadQueue";
import { RichTextProcessor } from "../lib/richtextprocessor"; import { RichTextProcessor } from "../lib/richtextprocessor";
//import apiManager from "../lib/mtproto/apiManager"; //import apiManager from "../lib/mtproto/apiManager";

33
src/components/emoticonsDropdown.ts

@ -2,7 +2,8 @@ import { AppImManager } from "../lib/appManagers/appImManager";
import { AppMessagesManager } from "../lib/appManagers/appMessagesManager"; import { AppMessagesManager } from "../lib/appManagers/appMessagesManager";
import { horizontalMenu } from "./misc"; import { horizontalMenu } from "./misc";
import lottieLoader from "../lib/lottieLoader"; import lottieLoader from "../lib/lottieLoader";
import Scrollable from "./scrollable"; //import Scrollable from "./scrollable";
import Scrollable from "./scrollable_new";
import { findUpTag, whichChild, calcImageInBox } from "../lib/utils"; import { findUpTag, whichChild, calcImageInBox } from "../lib/utils";
import { RichTextProcessor } from "../lib/richtextprocessor"; import { RichTextProcessor } from "../lib/richtextprocessor";
import appStickersManager, { MTStickerSet } from "../lib/appManagers/appStickersManager"; import appStickersManager, { MTStickerSet } from "../lib/appManagers/appStickersManager";
@ -62,6 +63,18 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
scroll.onAddedBottom = () => {}; scroll.onAddedBottom = () => {};
}; */ }; */
scroll.container.scrollTop = y; scroll.container.scrollTop = y;
setTimeout(() => {
lottieLoader.checkAnimations(true, EMOTICONSSTICKERGROUP);
lazyLoadQueue.check();
}, 100);
/* window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
lottieLoader.checkAnimations(true, EMOTICONSSTICKERGROUP);
lazyLoadQueue.check();
});
}); */
}); });
}; };
@ -262,11 +275,14 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
let categoryPush = (categoryDiv: HTMLDivElement, docs: MTDocument[], prepend?: boolean) => { let categoryPush = (categoryDiv: HTMLDivElement, docs: MTDocument[], prepend?: boolean) => {
//if((docs.length % 5) != 0) categoryDiv.classList.add('not-full'); //if((docs.length % 5) != 0) categoryDiv.classList.add('not-full');
let container = document.createElement('div');
categoryDiv.append(container);
docs.forEach(doc => { docs.forEach(doc => {
let div = document.createElement('div'); let div = document.createElement('div');
wrapSticker(doc, div, undefined, lazyLoadQueue, EMOTICONSSTICKERGROUP, true, false, true); wrapSticker(doc, div, undefined, lazyLoadQueue, EMOTICONSSTICKERGROUP, true, false, true);
categoryDiv.append(div); container.append(div);
}); });
if(prepend) stickersScroll.prepend(categoryDiv); if(prepend) stickersScroll.prepend(categoryDiv);
@ -290,9 +306,13 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
let paddingTop = parseInt(window.getComputedStyle(stickersScroll.container).getPropertyValue('padding-top')) || 0; let paddingTop = parseInt(window.getComputedStyle(stickersScroll.container).getPropertyValue('padding-top')) || 0;
heights.length = 0; heights.length = 0;
let concated = stickersScroll.hiddenElements.up.concat(stickersScroll.visibleElements, stickersScroll.hiddenElements.down); /* let concated = stickersScroll.hiddenElements.up.concat(stickersScroll.visibleElements, stickersScroll.hiddenElements.down);
concated.forEach((el, i) => { concated.forEach((el, i) => {
heights[i] = (heights[i - 1] || 0) + el.height + (i == 0 ? paddingTop : 0); heights[i] = (heights[i - 1] || 0) + el.height + (i == 0 ? paddingTop : 0);
}); */
let concated = Array.from(stickersScroll.splitUp.children);
concated.forEach((el, i) => {
heights[i] = (heights[i - 1] || 0) + el.scrollHeight + (i == 0 ? paddingTop : 0);
}); });
//console.log('stickers concated', concated, heights); //console.log('stickers concated', concated, heights);
@ -308,7 +328,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
}; };
let prevCategoryIndex = 0; let prevCategoryIndex = 0;
let stickersScroll = new Scrollable(contentStickersDiv, 'y', 500, 'STICKERS'); let stickersScroll = new Scrollable(contentStickersDiv, 'y', 500, 'STICKERS', undefined, undefined, 2);
stickersScroll.container.addEventListener('scroll', (e) => { stickersScroll.container.addEventListener('scroll', (e) => {
lazyLoadQueue.check(); lazyLoadQueue.check();
lottieLoader.checkAnimations(); lottieLoader.checkAnimations();
@ -316,7 +336,6 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, stickersScroll.container, menuScroll); prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, stickersScroll.container, menuScroll);
}); });
stickersScroll.setVirtualContainer(stickersDiv); stickersScroll.setVirtualContainer(stickersDiv);
stickersScroll.lock('both');
emoticonsMenuOnClick(menu, heights, stickersScroll, menuScroll); emoticonsMenuOnClick(menu, heights, stickersScroll, menuScroll);
@ -397,9 +416,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
categoryPush(categoryDiv, stickerSet.documents, false); categoryPush(categoryDiv, stickerSet.documents, false);
} }
}) })
]).then(() => { ]);
stickersScroll.unlock('both');
});
}; };
let gifsInit = () => { let gifsInit = () => {

425
src/components/scrollable_new.ts

@ -0,0 +1,425 @@
import { logger, deferredPromise, CancellablePromise } from "../lib/polyfill";
/*
var el = $0;
var height = 0;
var checkUp = false;
do {
height += el.scrollHeight;
} while(el = (checkUp ? el.previousElementSibling : el.nextElementSibling));
console.log(height);
*/
/*
Array.from($0.querySelectorAll('.bubble__container')).forEach(_el => {
//_el.style.display = '';
//return;
let el = _el.parentElement;
let height = el.scrollHeight;
let width = el.scrollWidth;
el.style.width = width + 'px';
el.style.height = height + 'px';
_el.style.display = 'none';
});
*/
export default class Scrollable {
public container: HTMLDivElement;
public type: string;
public side: string;
public translate: string;
public scrollType: string;
public scrollSide: string;
public clientAxis: string;
public clientSize: string;
public scrollSize = -1; // it will be scrollHeight
public size = 0; // it will be outerHeight of container (not scrollHeight)
public splitUp: HTMLElement;
public onScrolledTop: () => void = null;
public onScrolledBottom: () => void = null;
public onScrolledTopFired = false;
public onScrolledBottomFired = false;
public onScrollMeasure: number = null;
public lastScrollTop: number = 0;
public scrollTopOffset: number = 0;
private disableHoverTimeout: number = 0;
private log: ReturnType<typeof logger>;
private debug = false;
private measureMutex: CancellablePromise<void>;
private observer: IntersectionObserver;
private visible: Set<HTMLElement>;
private virtualTempIDTop = 0;
private virtualTempIDBottom = 0;
private lastTopID = 0;
private lastBottomID = 0;
private lastScrollDirection = true; // true = bottom
private setVisible(element: HTMLElement) {
if(this.visible.has(element)) return;
this.debug && this.log('setVisible id:', element.dataset.virtual);
(element.firstElementChild as HTMLElement).style.display = '';
this.visible.add(element);
}
private setHidden(element: HTMLElement) {
if(!this.visible.has(element)) return;
this.debug && this.log('setHidden id:', element.dataset.virtual);
(element.firstElementChild as HTMLElement).style.display = 'none';
this.visible.delete(element);
}
constructor(public el: HTMLElement, axis: 'y' | 'x' = 'y', public splitOffset = 300, logPrefix = '', public appendTo = el, public onScrollOffset = splitOffset, public splitCount = 15) {
this.container = document.createElement('div');
this.container.classList.add('scrollable');
this.visible = new Set();
this.observer = new IntersectionObserver(entries => {
let filtered = entries.filter(entry => entry.isIntersecting);
//this.log('entries:', entries);
entries.forEach(entry => {
let target = entry.target as HTMLElement;
if(entry.isIntersecting) {
this.setVisible(target);
this.debug && this.log('intersection entry:', entry, this.lastTopID, this.lastBottomID);
} else {
let id = +target.dataset.virtual;
let isTop = entry.boundingClientRect.top < 0;
if(isTop) {
this.lastTopID = id + 1;
} else {
this.lastBottomID = id - 1;
}
//this.setHidden(target);
//this.log('intersection entry setHidden:', entry);
}
//this.debug && this.log('intersection entry:', entry, isTop, isBottom, this.lastTopID, this.lastBottomID);
});
if(!filtered.length) {
return;
}
if(this.lastScrollDirection) { // bottom
let target = filtered[filtered.length - 1].target as HTMLElement;
this.lastBottomID = +target.dataset.virtual;
for(let i = 0; i < this.splitCount; ++i) {
target = target.nextElementSibling as HTMLElement;
if(!target) break;
this.setVisible(target);
}
} else {
let target = filtered[0].target as HTMLElement;
this.lastTopID = +target.dataset.virtual;
for(let i = 0; i < this.splitCount; ++i) {
target = target.previousElementSibling as HTMLElement;
if(!target) break;
this.setVisible(target);
}
}
this.debug && this.log('entries:', entries, filtered, this.lastScrollDirection, this.lastTopID, this.lastBottomID);
let minVisibleID = this.lastTopID - this.splitCount;
let maxVisibleID = this.lastBottomID + this.splitCount;
for(let target of this.visible) {
let id = +target.dataset.virtual;
if(id < minVisibleID || id > maxVisibleID) {
this.setHidden(target);
}
}
});
// внизу - самый производительный вариант
if(false) this.observer = new IntersectionObserver(entries => {
entries/* .filter(entry => entry.isIntersecting) */.forEach((entry, idx, arr) => {
let target = entry.target as HTMLElement;
if(entry.isIntersecting) {
let isTop = entry.boundingClientRect.top <= 0;
let isBottom = entry.rootBounds.height <= (entry.boundingClientRect.top + entry.boundingClientRect.height);
/* let id = +target.dataset.virtual;
let isOutOfRange = id < (this.lastTopID - 15) || id > (this.lastBottomID + 15);
if(isOutOfRange) {
this.debug && this.log('out of range, scroll jumped!');
if(idx == 0) this.lastTopID = id;
else if(idx == (arr.length - 1)) this.lastBottomID = id;
} */
this.setVisible(target);
if(isTop) {
/* this.lastTopID = id;
this.debug && this.log('set lastTopID to:', this.lastTopID); */
for(let i = 0; i < 15; ++i) {
target = target.previousElementSibling as HTMLElement;
if(!target) break;
this.setVisible(target);
}
} else if(isBottom) {
/* this.lastBottomID = id;
this.debug && this.log('set lastBottomID to:', this.lastBottomID); */
for(let i = 0; i < 15; ++i) {
target = target.nextElementSibling as HTMLElement;
if(!target) break;
this.setVisible(target);
}
}
} else {
this.setHidden(target);
}
//this.debug && this.log('intersection entry:', entry, isTop, isBottom, this.lastTopID, this.lastBottomID);
});
/* let minVisibleID = this.lastTopID - 15;
let maxVisibleID = this.lastBottomID + 15;
for(let target of this.visible) {
let id = +target.dataset.virtual;
if(id < minVisibleID || id > maxVisibleID) {
this.setHidden(target);
}
} */
});
if(!appendTo) {
this.appendTo = this.container;
}
this.log = logger('SCROLL' + (logPrefix ? '-' + logPrefix : ''));
this.measureMutex = deferredPromise<void>();
this.measureMutex.resolve();
if(axis == 'x') {
this.container.classList.add('scrollable-x');
this.type = 'width';
this.side = 'left';
this.translate = 'translateX';
this.scrollType = 'scrollWidth';
this.scrollSide = 'scrollLeft';
this.clientAxis = 'clientX';
this.clientSize = 'clientWidth';
let scrollHorizontally = (e: any) => {
e = window.event || e;
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
this.container.scrollLeft -= (delta * 20);
e.preventDefault();
};
if(this.container.addEventListener) {
// IE9, Chrome, Safari, Opera
this.container.addEventListener("mousewheel", scrollHorizontally, false);
// Firefox
this.container.addEventListener("DOMMouseScroll", scrollHorizontally, false);
} else {
// IE 6/7/8
// @ts-ignore
this.container.attachEvent("onmousewheel", scrollHorizontally);
}
} else if(axis == 'y') {
this.container.classList.add('scrollable-y');
this.type = 'height';
this.side = 'top';
this.translate = 'translateY';
this.scrollType = 'scrollHeight';
this.scrollSide = 'scrollTop';
this.clientAxis = 'clientY';
this.clientSize = 'clientHeight';
} else {
throw new Error('no side for scroll');
}
//this.container.addEventListener('mouseover', this.resize.bind(this)); // omg
window.addEventListener('resize', () => {
window.requestAnimationFrame(() => {
this.onScroll();
});
});
this.container.addEventListener('scroll', () => this.onScroll(), {passive: true, capture: true});
Array.from(el.children).forEach(c => this.container.append(c));
el.append(this.container);
//this.onScroll();
}
public setVirtualContainer(el?: HTMLElement) {
this.splitUp = el;
this.onScrolledBottomFired = this.onScrolledTopFired = false;
this.lastScrollTop = 0;
this.log('setVirtualContainer:', el, this);
}
public onScroll() {
/* let scrollTop = this.scrollTop;
this.lastScrollDirection = this.lastScrollTop < scrollTop;
this.lastScrollTop = scrollTop;
return; */
/* if(this.debug) {
this.log('onScroll call', this.onScrollMeasure);
} */
let appendTo = this.splitUp || this.appendTo;
clearTimeout(this.disableHoverTimeout);
if(this.el != this.appendTo && this.appendTo != this.container) {
if(!appendTo.classList.contains('disable-hover')) {
appendTo.classList.add('disable-hover');
}
}
this.disableHoverTimeout = setTimeout(() => {
appendTo.classList.remove('disable-hover');
if(!this.measureMutex.isFulfilled) {
this.measureMutex.resolve();
}
}, 100);
if(this.onScrollMeasure) return; //window.cancelAnimationFrame(this.onScrollMeasure);
this.onScrollMeasure = window.requestAnimationFrame(() => {
// @ts-ignore
let scrollPos = this.container[this.scrollSide];
//if(this.measureMutex.isFulfilled) {
// @ts-ignore quick brown fix
this.size = this.container[this.clientSize];
// @ts-ignore
let scrollSize = this.container[this.scrollType];
this.scrollSize = scrollSize;
//this.measureMutex = deferredPromise<void>();
//}
let scrollTop = scrollPos - this.scrollTopOffset;
let maxScrollTop = this.scrollSize - this.scrollTopOffset - this.size;
if(this.onScrolledBottom) {
if((maxScrollTop - scrollTop) <= this.onScrollOffset) {
//if(!this.onScrolledBottomFired) {
this.onScrolledBottomFired = true;
this.onScrolledBottom();
//}
} else {
this.onScrolledBottomFired = false;
}
}
if(this.onScrolledTop) {
//this.log('onScrolledTop:', scrollTop, this.onScrollOffset);
if(scrollTop <= this.onScrollOffset) {
this.onScrolledTopFired = true;
this.onScrolledTop();
} else {
this.onScrolledTopFired = false;
}
}
this.lastScrollDirection = this.lastScrollTop < scrollTop;
this.lastScrollTop = scrollTop;
this.onScrollMeasure = 0;
});
}
public prepareElement(element: HTMLElement, append = true) {
element.dataset.virtual = '' + (append ? this.virtualTempIDBottom++ : this.virtualTempIDTop--);
this.debug && this.log('prepareElement: prepared');
window.requestAnimationFrame(() => {
let {scrollHeight/* , scrollWidth */} = element;
this.debug && this.log('prepareElement: first rAF');
window.requestAnimationFrame(() => {
//element.style.height = scrollHeight + 'px';
element.style.minHeight = scrollHeight + 'px'; // height doesn't work for safari
//element.style.width = scrollWidth + 'px';
//(element.firstElementChild as HTMLElement).style.display = 'none';
});
this.visible.add(element);
this.observer.observe(element);
});
}
public prepend(element: HTMLElement, splitable = true) {
if(splitable) this.prepareElement(element, false);
if(this.splitUp) this.splitUp.prepend(element);
else this.appendTo.prepend(element);
}
public append(element: HTMLElement, splitable = true) {
if(splitable) this.prepareElement(element);
if(this.splitUp) this.splitUp.append(element);
else this.appendTo.append(element);
}
public contains(element: Element) {
if(!this.splitUp) {
return this.appendTo.contains(element);
}
return !!element.parentElement;
}
public scrollIntoView(element: Element) {
if(element.parentElement) {
element.scrollIntoView();
}
}
public removeElement(element: Element) {
element.remove();
}
set scrollTop(y: number) {
this.container.scrollTop = y;
}
get scrollTop() {
//this.log.trace('get scrollTop');
return this.container.scrollTop;
}
get scrollHeight() {
return this.container.scrollHeight;
}
get length() {
return this.appendTo.childElementCount;
}
}

78
src/lib/appManagers/appDialogsManager.ts

@ -1,11 +1,12 @@
import { langPack, findUpClassName, $rootScope, escapeRegExp } from "../utils"; import { langPack, findUpClassName, $rootScope, escapeRegExp, whichChild } from "../utils";
import appImManager, { AppImManager } from "./appImManager"; import appImManager, { AppImManager } from "./appImManager";
import appPeersManager from './appPeersManager'; import appPeersManager from './appPeersManager';
import appMessagesManager, { AppMessagesManager } from "./appMessagesManager"; import appMessagesManager, { AppMessagesManager } from "./appMessagesManager";
import appUsersManager from "./appUsersManager"; import appUsersManager from "./appUsersManager";
import { RichTextProcessor } from "../richtextprocessor"; import { RichTextProcessor } from "../richtextprocessor";
import { ripple, putPreloader } from "../../components/misc"; import { ripple, putPreloader } from "../../components/misc";
import Scrollable from "../../components/scrollable"; //import Scrollable from "../../components/scrollable";
import Scrollable from "../../components/scrollable_new";
import appProfileManager from "./appProfileManager"; import appProfileManager from "./appProfileManager";
import { logger } from "../polyfill"; import { logger } from "../polyfill";
@ -27,10 +28,10 @@ export class AppDialogsManager {
public chatList = document.getElementById('dialogs') as HTMLUListElement; public chatList = document.getElementById('dialogs') as HTMLUListElement;
public chatListArchived = document.getElementById('dialogs-archived') as HTMLUListElement; public chatListArchived = document.getElementById('dialogs-archived') as HTMLUListElement;
public pinnedDelimiter: HTMLDivElement; public pinnedDelimiter: HTMLDivElement;
public chatsHidden: Scrollable["hiddenElements"]; /* public chatsHidden: Scrollable["hiddenElements"];
public chatsVisible: Scrollable["visibleElements"]; public chatsVisible: Scrollable["visibleElements"];
public chatsArchivedHidden: Scrollable["hiddenElements"]; public chatsArchivedHidden: Scrollable["hiddenElements"];
public chatsArchivedVisible: Scrollable["visibleElements"]; public chatsArchivedVisible: Scrollable["visibleElements"]; */
public doms: {[peerID: number]: DialogDom} = {}; public doms: {[peerID: number]: DialogDom} = {};
public domsArchived: {[peerID: number]: DialogDom} = {}; public domsArchived: {[peerID: number]: DialogDom} = {};
@ -72,14 +73,14 @@ export class AppDialogsManager {
this.scroll = new Scrollable(this.chatsContainer, 'y', splitOffset, 'CL', this.chatList, 500); this.scroll = new Scrollable(this.chatsContainer, 'y', splitOffset, 'CL', this.chatList, 500);
this.scroll.setVirtualContainer(this.chatList); this.scroll.setVirtualContainer(this.chatList);
this.scroll.onScrolledBottom = this.onChatsScroll.bind(this); this.scroll.onScrolledBottom = this.onChatsScroll.bind(this);
this.chatsHidden = this.scroll.hiddenElements; /* this.chatsHidden = this.scroll.hiddenElements;
this.chatsVisible = this.scroll.visibleElements; this.chatsVisible = this.scroll.visibleElements; */
this.scrollArchived = new Scrollable(this.chatsArchivedContainer, 'y', splitOffset, 'CLA', this.chatListArchived, 500); this.scrollArchived = new Scrollable(this.chatsArchivedContainer, 'y', splitOffset, 'CLA', this.chatListArchived, 500);
this.scrollArchived.setVirtualContainer(this.chatListArchived); this.scrollArchived.setVirtualContainer(this.chatListArchived);
this.scrollArchived.onScrolledBottom = this.onChatsArchivedScroll.bind(this); this.scrollArchived.onScrolledBottom = this.onChatsArchivedScroll.bind(this);
this.chatsArchivedHidden = this.scrollArchived.hiddenElements; /* this.chatsArchivedHidden = this.scrollArchived.hiddenElements;
this.chatsArchivedVisible = this.scrollArchived.visibleElements; this.chatsArchivedVisible = this.scrollArchived.visibleElements; */
//this.scrollArchived.container.addEventListener('scroll', this.onChatsArchivedScroll.bind(this)); //this.scrollArchived.container.addEventListener('scroll', this.onChatsArchivedScroll.bind(this));
//let chatClosedDiv = document.getElementById('chat-closed'); //let chatClosedDiv = document.getElementById('chat-closed');
@ -88,12 +89,19 @@ export class AppDialogsManager {
this.setListClickListener(this.chatListArchived); this.setListClickListener(this.chatListArchived);
if(testScroll) { if(testScroll) {
for(let i = 0; i < 1000; ++i) { let i = 0;
let add = () => {
let li = document.createElement('li'); let li = document.createElement('li');
li.dataset.id = '' + i; li.dataset.id = '' + i;
li.innerHTML = `<div class="rp"><div class="user-avatar" style="background-color: rgb(166, 149, 231); font-size: 0px;"><img src="#"></div><div class="user-caption"><p><span class="user-title">${i}</span><span><span class="message-status"></span><span class="message-time">18:33</span></span></p><p><span class="user-last-message"><b>-_-_-_-: </b>qweasd</span><span></span></p></div></div>`; li.id = '' + i;
li.innerHTML = `<div class="rp"><div class="user-avatar" style="background-color: rgb(166, 149, 231); font-size: 0px;"><img src="assets/img/pepe.jpg"></div><div class="user-caption"><p><span class="user-title">${i}</span><span><span class="message-status"></span><span class="message-time">18:33</span></span></p><p><span class="user-last-message"><b>-_-_-_-: </b>qweasd</span><span></span></p></div></div>`;
i++;
this.scroll.append(li); this.scroll.append(li);
};
for(let i = 0; i < 1000; ++i) {
add();
} }
(window as any).addElement = add;
} }
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
@ -134,7 +142,7 @@ export class AppDialogsManager {
let dialog: any = e.detail; let dialog: any = e.detail;
this.setLastMessage(dialog); this.setLastMessage(dialog);
this.sortDom(); this.setDialogPosition(dialog);
}); });
$rootScope.$on('dialogs_multiupdate', (e: CustomEvent) => { $rootScope.$on('dialogs_multiupdate', (e: CustomEvent) => {
@ -154,12 +162,7 @@ export class AppDialogsManager {
} }
this.setLastMessage(dialog); this.setLastMessage(dialog);
} this.setDialogPosition(dialog);
if(performed/* && false */) {
/////////console.log('will sortDom');
this.sortDom();
this.sortDom(true);
} }
}); });
@ -200,8 +203,7 @@ export class AppDialogsManager {
//let offset = 0; //let offset = 0;
let scroll = archived ? this.scrollArchived : this.scroll; let scroll = archived ? this.scrollArchived : this.scroll;
scroll.lock();
try { try {
console.time('getDialogs time'); console.time('getDialogs time');
@ -241,17 +243,16 @@ export class AppDialogsManager {
this.chatsPreloader.remove(); this.chatsPreloader.remove();
this.loadDialogsPromise = undefined; this.loadDialogsPromise = undefined;
scroll.unlock();
} }
public onChatsScroll() { public onChatsScroll() {
if(this.loadedAll || this.scroll.hiddenElements.down.length > 0 || this.loadDialogsPromise/* || 1 == 1 */) return; if(this.loadedAll /* || this.scroll.hiddenElements.down.length > 0 */ || this.loadDialogsPromise/* || 1 == 1 */) return;
this.loadDialogs(); this.loadDialogs();
} }
public onChatsArchivedScroll() { public onChatsArchivedScroll() {
if(this.loadedArchivedAll || this.scrollArchived.hiddenElements.down.length > 0 || this.loadDialogsPromise/* || 1 == 1 */) return; if(this.loadedArchivedAll /* || this.scrollArchived.hiddenElements.down.length > 0 */ || this.loadDialogsPromise/* || 1 == 1 */) return;
this.loadDialogs(true); this.loadDialogs(true);
} }
@ -329,8 +330,32 @@ export class AppDialogsManager {
}); });
} }
public sortDom(archived = false) { public setDialogPosition(dialog: any) {
//return; let pos = appMessagesManager.getDialogByPeerID(dialog.peerID)[1];
let dom = this.getDialogDom(dialog.peerID);
let prevPos = whichChild(dom.listEl);
if(prevPos == pos) {
return;
} else if(prevPos < pos) { // was higher
pos += 1;
}
let chatList = dialog.folder_id == 1 ? this.chatListArchived : this.chatList;
if(chatList.childElementCount > pos) {
chatList.insertBefore(dom.listEl, chatList.children[pos]);
} else {
chatList.append(dom.listEl);
}
// fix order
(Array.from(chatList.children) as HTMLElement[]).forEach((el, idx) => {
el.dataset.virtual = '' + idx;
});
this.log('setDialogPosition:', dialog, dom, pos);
}
/* public sortDom(archived = false) {
//if(archived) return; //if(archived) return;
let dialogs = appMessagesManager.dialogsStorage.dialogs.slice(); let dialogs = appMessagesManager.dialogsStorage.dialogs.slice();
@ -338,7 +363,6 @@ export class AppDialogsManager {
let inUpper: Scrollable['hiddenElements']['up'] = []; let inUpper: Scrollable['hiddenElements']['up'] = [];
let inBottom: Scrollable['hiddenElements']['down'] = []; let inBottom: Scrollable['hiddenElements']['down'] = [];
let inVisible: Scrollable['visibleElements'] = []; let inVisible: Scrollable['visibleElements'] = [];
let pinnedDialogs = []; let pinnedDialogs = [];
let sorted = dialogs; let sorted = dialogs;
@ -396,7 +420,7 @@ export class AppDialogsManager {
let dom = this.getDialogDom(d.peerID); let dom = this.getDialogDom(d.peerID);
if(!dom) return; if(!dom) return;
let child = concated.find(obj => obj.element == dom.listEl); let child = concated.find((obj: any) => obj.element == dom.listEl);
if(!child) { if(!child) {
return this.log.error('no child by listEl:', dom.listEl, archived, concated); return this.log.error('no child by listEl:', dom.listEl, archived, concated);
} }
@ -418,7 +442,7 @@ export class AppDialogsManager {
chatsVisible.length = 0; chatsVisible.length = 0;
chatsVisible.push(...inVisible); chatsVisible.push(...inVisible);
chatsHidden.down = inBottom; chatsHidden.down = inBottom;
} } */
public setLastMessage(dialog: any, lastMessage?: any, dom?: DialogDom, highlightWord?: string) { public setLastMessage(dialog: any, lastMessage?: any, dom?: DialogDom, highlightWord?: string) {
if(!lastMessage) { if(!lastMessage) {

2
src/lib/appManagers/appDocsManager.ts

@ -9,7 +9,7 @@ class AppDocsManager {
private docs: {[docID: string]: MTDocument} = {}; private docs: {[docID: string]: MTDocument} = {};
public saveDoc(apiDoc: MTDocument/* any */, context?: any) { public saveDoc(apiDoc: MTDocument/* any */, context?: any) {
console.log('saveDoc', apiDoc, this.docs[apiDoc.id]); //console.log('saveDoc', apiDoc, this.docs[apiDoc.id]);
if(this.docs[apiDoc.id]) { if(this.docs[apiDoc.id]) {
let d = this.docs[apiDoc.id]; let d = this.docs[apiDoc.id];

261
src/lib/appManagers/appImManager.ts

@ -21,7 +21,8 @@ import { wrapDocument, wrapPhoto, wrapVideo, wrapSticker, wrapReply, wrapAlbum }
import ProgressivePreloader from '../../components/preloader'; import ProgressivePreloader from '../../components/preloader';
import { openBtnMenu, formatPhoneNumber } from '../../components/misc'; import { openBtnMenu, formatPhoneNumber } from '../../components/misc';
import { ChatInput } from '../../components/chatInput'; import { ChatInput } from '../../components/chatInput';
import Scrollable from '../../components/scrollable'; //import Scrollable from '../../components/scrollable';
import Scrollable from '../../components/scrollable_new';
import BubbleGroups from '../../components/bubbleGroups'; import BubbleGroups from '../../components/bubbleGroups';
import LazyLoadQueue from '../../components/lazyLoadQueue'; import LazyLoadQueue from '../../components/lazyLoadQueue';
import appDocsManager from './appDocsManager'; import appDocsManager from './appDocsManager';
@ -59,8 +60,7 @@ export class AppImManager {
container: HTMLDivElement, container: HTMLDivElement,
timeout?: number timeout?: number
}} = {}; }} = {};
public unreaded: number[] = []; public unreadOut = new Set<number>();
public unreadOut: number[] = [];
public needUpdate: {replyMid: number, mid: number}[] = []; // if need wrapSingleMessage public needUpdate: {replyMid: number, mid: number}[] = []; // if need wrapSingleMessage
public offline = false; public offline = false;
@ -84,8 +84,8 @@ export class AppImManager {
private typingTimeouts: {[peerID: number]: number} = {}; private typingTimeouts: {[peerID: number]: number} = {};
private typingUsers: {[userID: number]: number} = {} // to peerID private typingUsers: {[userID: number]: number} = {} // to peerID
private topbar: HTMLDivElement = null; private topbar = document.getElementById('topbar') as HTMLDivElement;
private chatInput: HTMLDivElement = null; private chatInput = document.getElementById('chat-input') as HTMLDivElement;
private scrolledAll: boolean; private scrolledAll: boolean;
private scrolledAllDown: boolean; private scrolledAllDown: boolean;
@ -111,6 +111,11 @@ export class AppImManager {
private datesIntersectionObserver: IntersectionObserver = null; private datesIntersectionObserver: IntersectionObserver = null;
private lastDateMessageDiv: HTMLDivElement = null; private lastDateMessageDiv: HTMLDivElement = null;
private unreadedObserver: IntersectionObserver = null;
private loadedTopTimes = 0;
private loadedBottomTimes = 0;
constructor() { constructor() {
/* if(!lottieLoader.loaded) { /* if(!lottieLoader.loaded) {
@ -131,10 +136,7 @@ export class AppImManager {
apiManager.getUserID().then((id) => { apiManager.getUserID().then((id) => {
this.myID = $rootScope.myID = id; this.myID = $rootScope.myID = id;
}); });
this.topbar = document.getElementById('topbar') as HTMLDivElement;
this.chatInput = document.getElementById('chat-input') as HTMLDivElement;
$rootScope.$on('user_auth', (e: CustomEvent) => { $rootScope.$on('user_auth', (e: CustomEvent) => {
let userAuth = e.detail; let userAuth = e.detail;
this.myID = $rootScope.myID = userAuth ? userAuth.id : 0; this.myID = $rootScope.myID = userAuth ? userAuth.id : 0;
@ -238,12 +240,10 @@ export class AppImManager {
} else { } else {
this.log.warn('message_sent there is no bubble', e.detail); this.log.warn('message_sent there is no bubble', e.detail);
} }
let length = this.unreadOut.length; if(this.unreadOut.has(tempID)) {
for(let i = 0; i < length; i++) { this.unreadOut.delete(tempID);
if(this.unreadOut[i] == tempID) { this.unreadOut.add(mid);
this.unreadOut[i] = mid;
}
} }
}); });
@ -618,7 +618,7 @@ export class AppImManager {
apiUpdatesManager.attach(); apiUpdatesManager.attach();
this.datesIntersectionObserver = new IntersectionObserver((entries) => { this.datesIntersectionObserver = new IntersectionObserver((entries) => {
this.log('intersection', entries); //this.log('intersection', entries);
let entry = entries.filter(entry => entry.boundingClientRect.top < 0).sort((a, b) => b.boundingClientRect.top - a.boundingClientRect.top)[0]; let entry = entries.filter(entry => entry.boundingClientRect.top < 0).sort((a, b) => b.boundingClientRect.top - a.boundingClientRect.top)[0];
if(!entry) return; if(!entry) return;
@ -636,6 +636,35 @@ export class AppImManager {
} }
} }
}/* , {root: this.chatInner} */); }/* , {root: this.chatInner} */);
this.unreadedObserver = new IntersectionObserver((entries) => {
let readed: number[] = [];
entries.forEach(entry => {
if(entry.isIntersecting) {
let target = entry.target as HTMLElement;
let mid = +target.dataset.mid;
readed.push(mid);
this.unreadedObserver.unobserve(target);
}
});
if(readed.length) {
let max = Math.max(...readed);
let min = Math.min(...readed);
if(this.peerID < 0) {
max = appMessagesIDsManager.getMessageIDInfo(max)[0];
min = appMessagesIDsManager.getMessageIDInfo(min)[0];
}
//appMessagesManager.readMessages(readed);
appMessagesManager.readHistory(this.peerID, max, min).catch((err: any) => {
this.log.error('readHistory err:', err);
appMessagesManager.readHistory(this.peerID, max, min);
});
}
});
} }
public deleteMessages(revoke = false) { public deleteMessages(revoke = false) {
@ -709,7 +738,6 @@ export class AppImManager {
} }
if(top && !this.scrolledAll) { 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); this.log('Will load more (up) history by id:', history[0], 'maxID:', history[history.length - 1], history);
/* false && */this.getHistory(history[0], true); /* false && */this.getHistory(history[0], true);
} }
@ -724,7 +752,6 @@ export class AppImManager {
// if scroll down after search // if scroll down after search
if(!top && (!dialog || history.indexOf(dialog.top_message) === -1)) { 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); this.log('Will load more (down) history by maxID:', history[history.length - 1], history);
/* false && */this.getHistory(history[history.length - 1], false, true); /* false && */this.getHistory(history[history.length - 1], false, true);
} }
@ -734,34 +761,7 @@ export class AppImManager {
if(this.onScrollRAF) window.cancelAnimationFrame(this.onScrollRAF); if(this.onScrollRAF) window.cancelAnimationFrame(this.onScrollRAF);
this.onScrollRAF = window.requestAnimationFrame(() => { this.onScrollRAF = window.requestAnimationFrame(() => {
let readed: number[] = [];
this.unreaded.forEachReverse((msgID, idx) => {
let bubble = this.bubbles[msgID];
if(isElementInViewport(bubble)) {
readed.push(msgID);
this.unreaded.splice(idx, 1);
}
});
lottieLoader.checkAnimations(false, 'chat'); lottieLoader.checkAnimations(false, 'chat');
if(readed.length) {
let max = Math.max(...readed);
let min = Math.min(...readed);
if(this.peerID < 0) {
max = appMessagesIDsManager.getMessageIDInfo(max)[0];
min = appMessagesIDsManager.getMessageIDInfo(min)[0];
}
//appMessagesManager.readMessages(readed);
appMessagesManager.readHistory(this.peerID, max, min).catch((err: any) => {
this.log.error('readHistory err:', err);
appMessagesManager.readHistory(this.peerID, max, min);
});
}
if(this.isScrollingTimeout) { if(this.isScrollingTimeout) {
clearTimeout(this.isScrollingTimeout); clearTimeout(this.isScrollingTimeout);
@ -915,8 +915,7 @@ export class AppImManager {
this.bubbles = {}; this.bubbles = {};
this.dateMessages = {}; this.dateMessages = {};
this.bubbleGroups.cleanup(); this.bubbleGroups.cleanup();
this.unreaded = []; this.unreadOut.clear();
this.unreadOut = [];
this.needUpdate.length = 0; this.needUpdate.length = 0;
this.lazyLoadQueue.clear(); this.lazyLoadQueue.clear();
@ -936,13 +935,17 @@ export class AppImManager {
this.datesIntersectionObserver.disconnect(); this.datesIntersectionObserver.disconnect();
this.lastDateMessageDiv = null; this.lastDateMessageDiv = null;
this.unreadedObserver.disconnect();
this.loadedTopTimes = this.loadedBottomTimes = 0;
////console.timeEnd('appImManager cleanup'); ////console.timeEnd('appImManager cleanup');
} }
public setPeer(peerID: number, lastMsgID = 0, forwarding = false, fromClick = false) { public setPeer(peerID: number, lastMsgID = 0, forwarding = false, fromClick = false) {
//console.time('appImManager setPeer'); console.time('appImManager setPeer');
//console.time('appImManager setPeer pre promise'); console.time('appImManager setPeer pre promise');
////console.time('appImManager: pre render start'); ////console.time('appImManager: pre render start');
if(peerID == 0) { if(peerID == 0) {
appSidebarRight.toggleSidebar(false); appSidebarRight.toggleSidebar(false);
@ -1050,7 +1053,7 @@ export class AppImManager {
//////appSidebarRight.toggleSidebar(true); //////appSidebarRight.toggleSidebar(true);
//console.timeEnd('appImManager setPeer pre promise'); console.timeEnd('appImManager setPeer pre promise');
this.preloader.attach(this.bubblesContainer); this.preloader.attach(this.bubblesContainer);
return this.setPeerPromise = Promise.all([ return this.setPeerPromise = Promise.all([
this.getHistory(forwarding ? lastMsgID + 1 : lastMsgID, true, false, additionMsgID).then(() => { this.getHistory(forwarding ? lastMsgID + 1 : lastMsgID, true, false, additionMsgID).then(() => {
@ -1074,7 +1077,7 @@ export class AppImManager {
this.preloader.detach(); this.preloader.detach();
this.chatInner.style.visibility = ''; this.chatInner.style.visibility = '';
//console.timeEnd('appImManager setPeer'); console.timeEnd('appImManager setPeer');
//setTimeout(() => { //setTimeout(() => {
//appSidebarRight.fillProfileElements(); //appSidebarRight.fillProfileElements();
@ -1120,19 +1123,22 @@ export class AppImManager {
///////this.log('updateUnreadByDialog', maxID, dialog, this.unreadOut); ///////this.log('updateUnreadByDialog', maxID, dialog, this.unreadOut);
this.unreadOut.forEachReverse((msgID, idx) => { for(let msgID of this.unreadOut) {
if(msgID > 0 && msgID <= maxID) { if(msgID > 0 && msgID <= maxID) {
let bubble = this.bubbles[msgID]; let bubble = this.bubbles[msgID];
bubble.classList.remove('is-sent'); if(bubble) {
bubble.classList.add('is-read'); bubble.classList.remove('is-sent');
this.unreadOut.splice(idx, 1); bubble.classList.add('is-read');
}
this.unreadOut.delete(msgID);
} }
}); }
} }
public deleteMessagesByIDs(msgIDs: number[]) { public deleteMessagesByIDs(msgIDs: number[], forever = true) {
msgIDs.forEach(id => { msgIDs.forEach(id => {
if(this.firstTopMsgID == id) { if(this.firstTopMsgID == id && forever) {
let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0]; let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0];
if(dialog) { if(dialog) {
@ -1145,11 +1151,14 @@ export class AppImManager {
let bubble = this.bubbles[id]; let bubble = this.bubbles[id];
delete this.bubbles[id]; delete this.bubbles[id];
this.unreadedObserver.unobserve(bubble);
this.scrollable.removeElement(bubble); this.scrollable.removeElement(bubble);
//bubble.remove(); //bubble.remove();
}); });
lottieLoader.checkAnimations(); lottieLoader.checkAnimations();
this.deleteEmptyDateGroups();
} }
public renderNewMessagesByIDs(msgIDs: number[]) { public renderNewMessagesByIDs(msgIDs: number[]) {
@ -1206,16 +1215,17 @@ export class AppImManager {
}; };
container.append(div); container.append(div);
//this.scrollable.prepareElement(div, false);
if(reverse) { if(reverse) {
let scrollTopPrevious = this.scrollable.scrollTop; //let scrollTopPrevious = this.scrollable.scrollTop;
this.scrollable.prepend(container); this.scrollable.prepend(container, false);
if(!scrollTopPrevious) { /* if(!scrollTopPrevious) {
this.scrollable.scrollTop += container.scrollHeight; this.scrollable.scrollTop += container.scrollHeight;
} } */
} else { } else {
this.scrollable.append(container); this.scrollable.append(container, false);
} }
this.datesIntersectionObserver.observe(container); this.datesIntersectionObserver.observe(container);
@ -1298,8 +1308,10 @@ export class AppImManager {
} else if(reverse) { } else if(reverse) {
dateContainer.container.insertBefore(bubble, dateContainer.div.nextSibling); dateContainer.container.insertBefore(bubble, dateContainer.div.nextSibling);
//this.scrollable.prepareElement(bubble, false);
} else { } else {
dateContainer.container.append(bubble); dateContainer.container.append(bubble);
//this.scrollable.prepareElement(bubble, true);
} }
return bubble; return bubble;
@ -1387,7 +1399,7 @@ export class AppImManager {
//bubble.prepend(timeSpan, messageDiv); // that's bad //bubble.prepend(timeSpan, messageDiv); // that's bad
if(our) { if(our) {
if(message.pFlags.unread || message.mid < 0) this.unreadOut.push(message.mid); // message.mid < 0 added 11.02.2020 if(message.pFlags.unread || message.mid < 0) this.unreadOut.add(message.mid); // message.mid < 0 added 11.02.2020
let status = ''; let status = '';
if(message.mid < 0) status = 'is-sending'; if(message.mid < 0) status = 'is-sending';
else status = message.pFlags.unread ? 'is-sent' : 'is-read'; else status = message.pFlags.unread ? 'is-sent' : 'is-read';
@ -1395,7 +1407,7 @@ export class AppImManager {
} else { } else {
//this.log('not our message', message, message.pFlags.unread); //this.log('not our message', message, message.pFlags.unread);
if(message.pFlags.unread) { if(message.pFlags.unread) {
this.unreaded.push(message.mid); this.unreadedObserver.observe(bubble);
} }
} }
@ -1839,8 +1851,10 @@ export class AppImManager {
let dateMessage = this.getDateContainerByMessage(message, reverse); let dateMessage = this.getDateContainerByMessage(message, reverse);
if(reverse) { if(reverse) {
dateMessage.container.insertBefore(bubble, dateMessage.div.nextSibling); dateMessage.container.insertBefore(bubble, dateMessage.div.nextSibling);
//this.scrollable.prepareElement(bubble, false);
} else { } else {
dateMessage.container.append(bubble); dateMessage.container.append(bubble);
//this.scrollable.prepareElement(bubble, true);
} }
} else { } else {
this.bubbleGroups.updateGroupByMessageID(message.mid); this.bubbleGroups.updateGroupByMessageID(message.mid);
@ -1868,14 +1882,17 @@ export class AppImManager {
if(additionMsgID) { if(additionMsgID) {
history.unshift(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); if(testScroll && additionMsgID) {
for(let i = 0; i < 3; ++i) {
let _history = history.slice();
setTimeout(() => {
this.performHistoryResult(_history, reverse, isBackLimit, 0, resetPromises);
}, (i + 1) * 2500);
}
}
console.time('appImManager render history');
let firstLoad = !!this.setPeerPromise && false; let firstLoad = !!this.setPeerPromise && false;
@ -1889,13 +1906,15 @@ export class AppImManager {
bubbles.push(bubble); bubbles.push(bubble);
}); */ }); */
let leftHeightToScroll = this.scrollable.innerHeight; //let leftHeightToScroll = this.scrollable.innerHeight;
//console.timeEnd('appImManager: pre render start'); //console.timeEnd('appImManager: pre render start');
//this.log('start performHistoryResult, scrollTop:', this.scrollable.scrollTop, this.scrollable.scrollHeight, this.scrollable.innerHeight); //this.log('start performHistoryResult, scrollTop:', this.scrollable.scrollTop, this.scrollable.scrollHeight, this.scrollable.innerHeight);
let renderedFirstScreen = false; let method = (reverse ? history.shift : history.pop).bind(history);
let renderedFirstScreen = !!this.scrollable.scrollTop;
let r = () => { let r = () => {
//let bubble = bubbles.shift(); //let bubble = bubbles.shift();
//if(!bubble && !resolved) return resolve(); //if(!bubble && !resolved) return resolve();
@ -1912,14 +1931,13 @@ export class AppImManager {
//let startTime = Date.now(); //let startTime = Date.now();
//let elapsedTime = 0; //let elapsedTime = 0;
//do { //do {
let msgID = history.shift(); //let msgID = history.shift();
let msgID = method();
if(!msgID) { if(!msgID) {
if(resetPromises) { if(resetPromises) {
(reverse ? this.getHistoryTopPromise = undefined : this.getHistoryBottomPromise = undefined); (reverse ? this.getHistoryTopPromise = undefined : this.getHistoryBottomPromise = undefined);
} }
this.scrollable.unlock('both');
if(!resolved) { if(!resolved) {
resolve(true); resolve(true);
} }
@ -1995,19 +2013,11 @@ export class AppImManager {
this.renderMessage(message, reverse, true); this.renderMessage(message, reverse, true);
}); });
}); */ }); */
})/* .then(() => { }).then(() => {
if(!isBackLimit) { console.timeEnd('appImManager render history');
this.scrollPosition.restore(() => {
this.scrollable.unlock('both');
});
} else {
this.scrollable.unlock('both');
}
//console.timeEnd('appImManager render history');
return true; return true;
}) */; });
} }
// reverse means scroll up // reverse means scroll up
@ -2021,10 +2031,13 @@ export class AppImManager {
maxID = dialog.top_message/* + 1 */; maxID = dialog.top_message/* + 1 */;
} }
let loadCount = Object.keys(this.bubbles).length > 0 ? 20 : this.scrollable.innerHeight / 38/* * 1.25 */ | 0; let pageCount = this.bubblesContainer.clientHeight / 38/* * 1.25 */ | 0;
//let loadCount = Object.keys(this.bubbles).length > 0 ? 50 : pageCount;
let realLoadCount = 50;
let loadCount = realLoadCount;
if(testScroll) { if(testScroll) {
loadCount = 1; //loadCount = 1;
if(Object.keys(this.bubbles).length > 0) if(Object.keys(this.bubbles).length > 0)
return Promise.resolve(true); return Promise.resolve(true);
} }
@ -2044,8 +2057,10 @@ export class AppImManager {
$rootScope.$broadcast('history_request'); // for ripple $rootScope.$broadcast('history_request'); // for ripple
result = new Promise((resolve, reject) => setTimeout(() => resolve(_result), 150)); result = new Promise((resolve, reject) => setTimeout(() => resolve(_result), 150));
} */ } */
let promise: Promise<boolean>;
if(result instanceof Promise) { if(result instanceof Promise) {
let promise = result.then((result) => { promise = result.then((result) => {
this.log('getHistory result by maxID:', maxID, reverse, isBackLimit, result); this.log('getHistory result by maxID:', maxID, reverse, isBackLimit, result);
//console.timeEnd('appImManager call getHistory'); //console.timeEnd('appImManager call getHistory');
@ -2062,17 +2077,73 @@ export class AppImManager {
}, (err) => { }, (err) => {
this.log.error('getHistory error:', err); this.log.error('getHistory error:', err);
(reverse ? this.getHistoryTopPromise = undefined : this.getHistoryBottomPromise = undefined); (reverse ? this.getHistoryTopPromise = undefined : this.getHistoryBottomPromise = undefined);
this.scrollable.unlock('both');
return false; return false;
}); });
return (reverse ? this.getHistoryTopPromise = promise : this.getHistoryBottomPromise = promise); (reverse ? this.getHistoryTopPromise = promise : this.getHistoryBottomPromise = promise);
} else { } else {
let promise = this.performHistoryResult(result.history || [], reverse, isBackLimit, additionMsgID, true); promise = this.performHistoryResult(result.history || [], reverse, isBackLimit, additionMsgID, true);
//return (reverse ? this.getHistoryTopPromise = promise : this.getHistoryBottomPromise = promise); //return (reverse ? this.getHistoryTopPromise = promise : this.getHistoryBottomPromise = promise);
return promise;
//return this.performHistoryResult(result.history || [], reverse, isBackLimit, additionMsgID, true); //return this.performHistoryResult(result.history || [], reverse, isBackLimit, additionMsgID, true);
} }
/* false && */promise.then(() => {
if(reverse) {
this.loadedTopTimes++;
this.loadedBottomTimes = Math.max(0, --this.loadedBottomTimes);
} else {
this.loadedBottomTimes++;
this.loadedTopTimes = Math.max(0, --this.loadedTopTimes);
}
let ids: number[];
if((reverse && this.loadedTopTimes > 2) || (!reverse && this.loadedBottomTimes > 2)) {
ids = Object.keys(this.bubbles).map(i => +i).sort((a, b) => a - b);
}
this.log('getHistory: slice loadedTimes:', reverse, pageCount, this.loadedTopTimes, this.loadedBottomTimes, ids && ids.length);
let removeCount = loadCount / 2;
let safeCount = realLoadCount * 2;
if(ids && ids.length > safeCount) {
if(reverse) {
//ids = ids.slice(-removeCount);
//ids = ids.slice(removeCount * 2);
ids = ids.slice(safeCount);
this.scrolledAllDown = false;
} else {
//ids = ids.slice(0, removeCount);
//ids = ids.slice(0, ids.length - (removeCount * 2));
ids = ids.slice(0, ids.length - safeCount);
this.scrolledAll = false;
this.log('getHistory: slice bottom: to:', ids.length, loadCount);
}
this.log('getHistory: will slice ids:', ids, reverse);
this.deleteMessagesByIDs(ids, false);
/* ids.forEach(id => {
this.bubbles[id].remove();
delete this.bubbles[id];
});
this.deleteEmptyDateGroups(); */
}
});
return promise;
}
public deleteEmptyDateGroups() {
for(let i in this.dateMessages) {
let dateMessage = this.dateMessages[i];
if(dateMessage.container.childElementCount == 1) { // only date div
dateMessage.container.remove();
this.datesIntersectionObserver.unobserve(dateMessage.container);
delete this.dateMessages[i];
}
}
} }
public setMutedState(muted = false) { public setMutedState(muted = false) {

75
src/lib/appManagers/appMessagesManager.ts

@ -22,6 +22,8 @@ import apiManager from '../mtproto/mtprotoworker';
import appWebPagesManager from "./appWebPagesManager"; import appWebPagesManager from "./appWebPagesManager";
import { CancellablePromise, deferredPromise } from "../polyfill"; import { CancellablePromise, deferredPromise } from "../polyfill";
const APITIMEOUT = 0;
export type HistoryStorage = { export type HistoryStorage = {
count: number | null, count: number | null,
history: number[], history: number[],
@ -36,6 +38,28 @@ export type HistoryResult = {
unreadSkip: boolean unreadSkip: boolean
}; };
type Dialog = {
_: 'dialog',
top_message: number,
read_inbox_max_id: number,
read_outbox_max_id: number,
peer: any,
notify_settings: any,
folder_id: number,
flags: number,
draft: any,
unread_count: number,
unread_mentions_count: number,
index: number,
peerID: number,
pinnedIndex: number,
pFlags: Partial<{
pinned: boolean
}>,
pts: number
}
export class AppMessagesManager { export class AppMessagesManager {
public messagesStorage: any = {}; public messagesStorage: any = {};
public messagesForDialogs: any = {}; public messagesForDialogs: any = {};
@ -45,7 +69,7 @@ export class AppMessagesManager {
} = {}; } = {};
public dialogsStorage: { public dialogsStorage: {
count: any, count: any,
dialogs: any[] dialogs: Dialog[]
} = {count: null, dialogs: []}; } = {count: null, dialogs: []};
public pendingByRandomID: {[randomID: string]: [number, number]} = {}; public pendingByRandomID: {[randomID: string]: [number, number]} = {};
public pendingByMessageID: any = {}; public pendingByMessageID: any = {};
@ -1222,7 +1246,7 @@ export class AppMessagesManager {
limit: limit, limit: limit,
hash: hash hash: hash
}, { }, {
timeout: 300 timeout: APITIMEOUT
}).then((dialogsResult: any) => { }).then((dialogsResult: any) => {
///////console.log('messages.getDialogs result:', dialogsResult); ///////console.log('messages.getDialogs result:', dialogsResult);
@ -1351,11 +1375,24 @@ export class AppMessagesManager {
return message.from_id; return message.from_id;
} }
public getDialogByPeerID(peerID: number) { public getDialogByPeerID(peerID: number): [Dialog, number] | [] {
let length = this.dialogsStorage.dialogs.length; let dialogs = this.dialogsStorage.dialogs;
for(var i = 0; i < length; i++) { let byFolders: {[id: number]: number} = {};
if(this.dialogsStorage.dialogs[i].peerID == peerID) { for(let i = 0, length = dialogs.length; i < length; i++) {
return [this.dialogsStorage.dialogs[i], i]; let dialog = dialogs[i];
if(!byFolders[dialog.folder_id]) byFolders[dialog.folder_id] = 0;
byFolders[dialog.folder_id]++;
if(dialog.peerID == peerID) {
//return [dialog, i];
let sum = 0;
for(let id in byFolders) {
if(+id != dialog.folder_id) {
sum += byFolders[id];
}
}
return [dialog, i - sum];
} }
} }
@ -1408,11 +1445,9 @@ export class AppMessagesManager {
var dialog = this.getDialogByPeerID(peerID)[0]; var dialog = this.getDialogByPeerID(peerID)[0];
if(dialog && mid > 0) { if(dialog && mid > 0) {
let dialogKey = apiMessage.pFlags.out apiMessage.pFlags.unread = mid > dialog[apiMessage.pFlags.out
? 'read_outbox_max_id' ? 'read_outbox_max_id'
: 'read_inbox_max_id'; : 'read_inbox_max_id'];
apiMessage.pFlags.unread = mid > dialog[dialogKey];
} else if(options.isNew) { } else if(options.isNew) {
apiMessage.pFlags.unread = true; apiMessage.pFlags.unread = true;
} }
@ -1723,7 +1758,7 @@ export class AppMessagesManager {
delete this.messagesForDialogs[msgID]; delete this.messagesForDialogs[msgID];
} }
public saveConversation(dialog: any) { public saveConversation(dialog: Dialog) {
var peerID = AppPeersManager.getPeerID(dialog.peer); var peerID = AppPeersManager.getPeerID(dialog.peer);
if(!peerID) { if(!peerID) {
return false; return false;
@ -1792,9 +1827,8 @@ export class AppMessagesManager {
this.pushDialogToStorage(dialog, offsetDate); this.pushDialogToStorage(dialog, offsetDate);
// Because we saved message without dialog present // Because we saved message without dialog present
var unreadKey = message.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id';
if(message.mid > 0) { if(message.mid > 0) {
if(message.mid > dialog[unreadKey]) message.pFlags.unread = true; if(message.mid > dialog[message.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id']) message.pFlags.unread = true;
else message.pFlags.unread = false; else message.pFlags.unread = false;
} }
@ -2289,7 +2323,7 @@ export class AppMessagesManager {
max_id: 0, max_id: 0,
min_id: 0 min_id: 0
}, { }, {
timeout: 300, timeout: APITIMEOUT,
noErrorBox: true noErrorBox: true
}); });
} else { } else {
@ -2311,7 +2345,7 @@ export class AppMessagesManager {
offset_id: appMessagesIDsManager.getMessageLocalID(offsetID), offset_id: appMessagesIDsManager.getMessageLocalID(offsetID),
limit: limit || 20 limit: limit || 20
}, { }, {
timeout: 300, timeout: APITIMEOUT,
noErrorBox: true noErrorBox: true
}); });
} }
@ -2888,8 +2922,7 @@ export class AppMessagesManager {
newUnreadCount = foundDialog[0].unread_count = 0; newUnreadCount = foundDialog[0].unread_count = 0;
} }
let dialogKey = isOut ? 'read_outbox_max_id' : 'read_inbox_max_id'; foundDialog[0][isOut ? 'read_outbox_max_id' : 'read_inbox_max_id'] = maxID;
foundDialog[0][dialogKey] = maxID;
} }
// need be commented for read out messages // need be commented for read out messages
@ -2949,7 +2982,7 @@ export class AppMessagesManager {
var channelID: number = update.channel_id; var channelID: number = update.channel_id;
var messageID: number; var messageID: number;
var message, i; var message, i;
var peerID: number, foundDialog: any[]; var peerID: number, foundDialog: ReturnType<AppMessagesManager['getDialogByPeerID']>;
let history: any; let history: any;
var peerMessagesToHandle; var peerMessagesToHandle;
var peerMessagesHandlePos; var peerMessagesHandlePos;
@ -3451,7 +3484,7 @@ export class AppMessagesManager {
min_id: 0, min_id: 0,
hash: 0 hash: 0
}, { }, {
timeout: 300, timeout: APITIMEOUT,
noErrorBox: true noErrorBox: true
}).then((historyResult: any) => { }).then((historyResult: any) => {
console.log('requestHistory result:', historyResult, maxID, limit, offset); console.log('requestHistory result:', historyResult, maxID, limit, offset);
@ -3474,7 +3507,7 @@ export class AppMessagesManager {
// will load more history if last message is album grouped (because it can be not last item) // will load more history if last message is album grouped (because it can be not last item)
let historyStorage = this.historiesStorage[peerID]; let historyStorage = this.historiesStorage[peerID];
// historyResult.messages: desc sorted // historyResult.messages: desc sorted
if(historyResult.messages[length - 1].grouped_id && (historyStorage.history.length + historyResult.messages.length) < historyResult.count) { if(length && historyResult.messages[length - 1].grouped_id && (historyStorage.history.length + historyResult.messages.length) < historyResult.count) {
return this.requestHistory(peerID, historyResult.messages[length - 1].mid, 10, 0).then((_historyResult: any) => { return this.requestHistory(peerID, historyResult.messages[length - 1].mid, 10, 0).then((_historyResult: any) => {
return historyResult; return historyResult;
}); });

2
src/lib/appManagers/appPhotosManager.ts

@ -142,7 +142,7 @@ export class AppPhotosManager {
//console.log('diff', diff, photoSize, bestPhotoSize); //console.log('diff', diff, photoSize, bestPhotoSize);
}); */ }); */
console.log('choosing', photo, width, height, bestPhotoSize); //console.log('choosing', photo, width, height, bestPhotoSize);
return bestPhotoSize; return bestPhotoSize;
} }

30
src/lib/appManagers/appSidebarRight.ts

@ -1,5 +1,6 @@
import { horizontalMenu, formatPhoneNumber, putPreloader, renderImageFromUrl } from "../../components/misc"; import { horizontalMenu, formatPhoneNumber, putPreloader, renderImageFromUrl } from "../../components/misc";
import Scrollable from '../../components/scrollable'; //import Scrollable from '../../components/scrollable';
import Scrollable from '../../components/scrollable_new';
import { $rootScope } from "../utils"; import { $rootScope } from "../utils";
import appMessagesManager from "./appMessagesManager"; import appMessagesManager from "./appMessagesManager";
import appPhotosManager from "./appPhotosManager"; import appPhotosManager from "./appPhotosManager";
@ -79,10 +80,7 @@ class AppSidebarRight {
private peerID = 0; private peerID = 0;
public scroll: Scrollable = null; public scroll: Scrollable = null;
private savedVirtualStates: {
[id: number]: Scrollable['state']
} = {};
private profileTabs: HTMLUListElement; private profileTabs: HTMLUListElement;
private prevTabID = -1; private prevTabID = -1;
@ -107,7 +105,7 @@ class AppSidebarRight {
//this.scroll = new Scrollable(this.profileContentEl, 'y', 1200, 'SR', undefined, 400); //this.scroll = new Scrollable(this.profileContentEl, 'y', 1200, 'SR', undefined, 400);
this.scroll.container.addEventListener('scroll', this.onSidebarScroll.bind(this)); this.scroll.container.addEventListener('scroll', this.onSidebarScroll.bind(this));
this.scroll.onScrolledBottom = () => { this.scroll.onScrolledBottom = () => {
if(this.sharedMediaSelected && !this.scroll.hiddenElements.down.length && this.sharedMediaSelected.childElementCount/* && false */) { if(this.sharedMediaSelected && this.sharedMediaSelected.childElementCount/* && false */) {
this.log('onScrolledBottom will load media'); this.log('onScrolledBottom will load media');
this.loadSidebarMedia(true); this.loadSidebarMedia(true);
} }
@ -123,17 +121,8 @@ class AppSidebarRight {
this.scroll.scrollTop -= this.profileTabs.offsetTop; this.scroll.scrollTop -= this.profileTabs.offsetTop;
} }
if(this.prevTabID != -1) {
this.savedVirtualStates[this.prevTabID] = this.scroll.state;
}
this.log('setVirtualContainer', id, this.sharedMediaSelected); this.log('setVirtualContainer', id, this.sharedMediaSelected);
this.scroll.setVirtualContainer(this.sharedMediaSelected); this.scroll.setVirtualContainer(this.sharedMediaSelected);
if(this.savedVirtualStates[id]) {
this.log(this.savedVirtualStates[id]);
this.scroll.state = this.savedVirtualStates[id];
}
if(this.prevTabID != -1 && !this.sharedMediaSelected.childElementCount) { // quick brown fix if(this.prevTabID != -1 && !this.sharedMediaSelected.childElementCount) { // quick brown fix
this.contentContainer.classList.remove('loaded'); this.contentContainer.classList.remove('loaded');
@ -423,7 +412,7 @@ class AppSidebarRight {
if(elemsToAppend.length) { if(elemsToAppend.length) {
//window.requestAnimationFrame(() => { //window.requestAnimationFrame(() => {
elemsToAppend.forEach(el => this.scroll.append(el)); elemsToAppend.forEach(el => this.scroll.append(el, false));
//}); //});
} }
@ -452,9 +441,7 @@ class AppSidebarRight {
if(!typesToLoad.length) return; if(!typesToLoad.length) return;
let historyStorage = this.historiesStorage[peerID] ?? (this.historiesStorage[peerID] = {}); let historyStorage = this.historiesStorage[peerID] ?? (this.historiesStorage[peerID] = {});
this.scroll.lock();
let promises = typesToLoad.map(type => { let promises = typesToLoad.map(type => {
if(this.loadSidebarMediaPromises[type]) return this.loadSidebarMediaPromises[type]; if(this.loadSidebarMediaPromises[type]) return this.loadSidebarMediaPromises[type];
@ -513,9 +500,7 @@ class AppSidebarRight {
}); });
}); });
return Promise.all(promises).then(() => { return Promise.all(promises);
this.scroll.unlock();
});
} }
public fillProfileElements() { public fillProfileElements() {
@ -546,7 +531,6 @@ class AppSidebarRight {
} }
}); });
this.savedVirtualStates = {};
this.prevTabID = -1; this.prevTabID = -1;
this.scroll.setVirtualContainer(null); this.scroll.setVirtualContainer(null);
(this.profileTabs.firstElementChild.children[1] as HTMLLIElement).click(); // set media (this.profileTabs.firstElementChild.children[1] as HTMLLIElement).click(); // set media

29
src/lib/bin_utils.ts

@ -7,12 +7,28 @@
// @ts-ignore // @ts-ignore
import {BigInteger, SecureRandom} from 'jsbn'; import {BigInteger, SecureRandom} from 'jsbn';
/// #if !MTPROTO_WORKER
// @ts-ignore // @ts-ignore
import pako from 'pako/dist/pako_inflate.min.js'; import pako from 'pako/dist/pako_inflate.min.js';
var _logTimer = (new Date()).getTime() export function gzipUncompress(bytes: ArrayBuffer, toString: true): string;
export function gzipUncompress(bytes: ArrayBuffer, toString?: false): Uint8Array;
export function gzipUncompress(bytes: ArrayBuffer, toString?: boolean): string | Uint8Array {
//console.log(dT(), 'Gzip uncompress start');
var result = pako.inflate(bytes, toString ? {to: 'string'} : undefined);
//console.log(dT(), 'Gzip uncompress finish'/* , result */);
return result;
}
/// #endif
var _logTimer = Date.now();
export function dT () { export function dT () {
return '[' + (((new Date()).getTime() - _logTimer) / 1000).toFixed(3) + ']' return '[' + ((Date.now() - _logTimer) / 1000).toFixed(3) + ']'
}
export function isObject(object: any) {
return typeof(object) === 'object' && object !== null;
} }
export function bigint(num: number) { export function bigint(num: number) {
@ -379,15 +395,6 @@ export function addPadding(bytes: any, blockSize: number = 16, zeroes?: boolean,
return bytes; return bytes;
} }
export function gzipUncompress(bytes: ArrayBuffer, toString: true): string;
export function gzipUncompress(bytes: ArrayBuffer, toString?: false): Uint8Array;
export function gzipUncompress(bytes: ArrayBuffer, toString?: boolean): string | Uint8Array {
//console.log(dT(), 'Gzip uncompress start');
var result = pako.inflate(bytes, toString ? {to: 'string'} : undefined);
//console.log(dT(), 'Gzip uncompress finish'/* , result */);
return result;
}
export function nextRandomInt(maxValue: number) { export function nextRandomInt(maxValue: number) {
return Math.floor(Math.random() * maxValue); return Math.floor(Math.random() * maxValue);
} }

12
src/lib/crypto/crypto_utils.ts

@ -2,6 +2,9 @@ import sha1 from '@cryptography/sha1';
import sha256 from '@cryptography/sha256'; import sha256 from '@cryptography/sha256';
import {IGE} from '@cryptography/aes'; import {IGE} from '@cryptography/aes';
// @ts-ignore
import pako from 'pako/dist/pako_inflate.min.js';
import {str2bigInt, bpe, equalsInt, greater, import {str2bigInt, bpe, equalsInt, greater,
copy_, eGCD_, add_, rightShift_, sub_, copyInt_, isZero, copy_, eGCD_, add_, rightShift_, sub_, copyInt_, isZero,
// @ts-ignore // @ts-ignore
@ -239,3 +242,12 @@ export function bytesModPow(x: any, y: any, m: any) {
return bytesFromBigInt(new BigInteger(x).modPow(new BigInteger(y), new BigInteger(m)), 256); return bytesFromBigInt(new BigInteger(x).modPow(new BigInteger(y), new BigInteger(m)), 256);
} }
export function gzipUncompress(bytes: ArrayBuffer, toString: true): string;
export function gzipUncompress(bytes: ArrayBuffer, toString?: false): Uint8Array;
export function gzipUncompress(bytes: ArrayBuffer, toString?: boolean): string | Uint8Array {
//console.log(dT(), 'Gzip uncompress start');
var result = pako.inflate(bytes, toString ? {to: 'string'} : undefined);
//console.log(dT(), 'Gzip uncompress finish'/* , result */);
return result;
}

7
src/lib/crypto/cryptoworker.ts

@ -37,13 +37,14 @@ class CryptoWorker extends CryptoWorkerMethods {
'aes-decrypt': utils.aesDecryptSync, 'aes-decrypt': utils.aesDecryptSync,
'rsa-encrypt': utils.rsaEncrypt, 'rsa-encrypt': utils.rsaEncrypt,
'factorize': utils.pqPrimeFactorization, 'factorize': utils.pqPrimeFactorization,
'mod-pow': utils.bytesModPow 'mod-pow': utils.bytesModPow,
'unzip': utils.gzipUncompress
}); });
}), })/* ,
import('../bin_utils').then(utils => { import('../bin_utils').then(utils => {
this.utils.unzip = utils.gzipUncompress; this.utils.unzip = utils.gzipUncompress;
}) }) */
]); ]);
/// #else /// #else
if(window.Worker) { if(window.Worker) {

42
src/lib/mtproto/apiManager.ts

@ -1,17 +1,20 @@
import AppStorage from '../storage'; import AppStorage from '../storage';
import { MTPNetworker } from './networker'; import { MTPNetworker } from './networker';
import { bytesFromHex, bytesToHex } from '../bin_utils'; import { bytesFromHex, bytesToHex, isObject } from '../bin_utils';
import networkerFactory from './networkerFactory'; import networkerFactory from './networkerFactory';
import { telegramMeWebService } from './mtproto'; import { telegramMeWebService } from './mtproto';
import authorizer from './authorizer'; import authorizer from './authorizer';
import { isObject, tsNow, $rootScope } from '../utils';
import {App, Modes} from './mtproto_config'; import {App, Modes} from './mtproto_config';
import dcConfigurator from './dcConfigurator'; import dcConfigurator from './dcConfigurator';
import HTTP from './transports/http'; import HTTP from './transports/http';
import { logger } from '../polyfill'; import { logger } from '../polyfill';
import passwordManager from './passwordManager'; import passwordManager from './passwordManager';
/// #if !MTPROTO_WORKER
import { $rootScope } from '../utils';
/// #endif
//console.error('apiManager included!'); //console.error('apiManager included!');
export class ApiManager { export class ApiManager {
@ -51,7 +54,10 @@ export class ApiManager {
}); });
this.telegramMeNotify(true); this.telegramMeNotify(true);
/// #if !MTPROTO_WORKER
$rootScope.$broadcast('user_auth', fullUserAuth); $rootScope.$broadcast('user_auth', fullUserAuth);
/// #endif
} }
// mtpLogOut // mtpLogOut
@ -189,21 +195,21 @@ export class ApiManager {
} }
// mtpInvokeApi // mtpInvokeApi
public invokeApi(method: string, params: any = {}, options: { public invokeApi(method: string, params: any = {}, options: Partial<{
dcID?: number, dcID: number,
timeout?: number, timeout: number,
noErrorBox?: boolean, noErrorBox: boolean,
fileUpload?: boolean, fileUpload: boolean,
ignoreErrors?: boolean, ignoreErrors: boolean,
fileDownload?: boolean, fileDownload: boolean,
createNetworker?: boolean, createNetworker: boolean,
singleInRequest?: boolean, singleInRequest: boolean,
startMaxLength?: number, startMaxLength: number,
waitTime?: number, waitTime: number,
stopTime?: number, stopTime: number,
rawError?: any rawError: any
} = {}) { }> = {}) {
///////this.log('Invoke api', method, params, options); ///////this.log('Invoke api', method, params, options);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -287,7 +293,7 @@ export class ApiManager {
} else if(!options.rawError && error.code == 420) { } else if(!options.rawError && error.code == 420) {
var waitTime = error.type.match(/^FLOOD_WAIT_(\d+)/)[1] || 10; var waitTime = error.type.match(/^FLOOD_WAIT_(\d+)/)[1] || 10;
if(waitTime > (options.timeout || 60)) { if(waitTime > (options.timeout !== undefined ? options.timeout : 60)) {
return rejectPromise(error); return rejectPromise(error);
} }
@ -295,7 +301,7 @@ export class ApiManager {
performRequest(cachedNetworker); performRequest(cachedNetworker);
}, (waitTime + 5) * 1000); // 03.02.2020 }, (waitTime + 5) * 1000); // 03.02.2020
} else if(!options.rawError && (error.code == 500 || error.type == 'MSG_WAIT_FAILED')) { } else if(!options.rawError && (error.code == 500 || error.type == 'MSG_WAIT_FAILED')) {
var now = tsNow(); var now = Date.now();
if(options.stopTime) { if(options.stopTime) {
if(now >= options.stopTime) { if(now >= options.stopTime) {
return rejectPromise(error); return rejectPromise(error);

3
src/lib/mtproto/authorizer.ts

@ -2,7 +2,6 @@ import { TLSerialization, TLDeserialization } from "./tl_utils";
import dcConfigurator from "./dcConfigurator"; import dcConfigurator from "./dcConfigurator";
import { dT, bytesToHex, bytesCmp, bytesFromHex, bytesXor } from "../bin_utils"; import { dT, bytesToHex, bytesCmp, bytesFromHex, bytesXor } from "../bin_utils";
import rsaKeysManager from "./rsaKeysManager"; import rsaKeysManager from "./rsaKeysManager";
import { tsNow } from "../utils";
import timeManager from "./timeManager"; import timeManager from "./timeManager";
// @ts-ignore // @ts-ignore
@ -303,7 +302,7 @@ export class Authorizer {
} }
public async mtpDecryptServerDhDataAnswer(auth: AuthOptions, encryptedAnswer: any) { public async mtpDecryptServerDhDataAnswer(auth: AuthOptions, encryptedAnswer: any) {
auth.localTime = tsNow(); auth.localTime = Date.now();
// can't concat Array with Uint8Array! // can't concat Array with Uint8Array!
//auth.tmpAesKey = sha1BytesSync(auth.newNonce.concat(auth.serverNonce)).concat(sha1BytesSync(auth.serverNonce.concat(auth.newNonce)).slice(0, 12)); //auth.tmpAesKey = sha1BytesSync(auth.newNonce.concat(auth.serverNonce)).concat(sha1BytesSync(auth.serverNonce.concat(auth.newNonce)).slice(0, 12));

3
src/lib/mtproto/mtproto.ts

@ -1,5 +1,4 @@
import AppStorage from '../storage'; import AppStorage from '../storage';
import {tsNow} from '../utils';
import { Modes, App } from './mtproto_config'; import { Modes, App } from './mtproto_config';
/* import PasswordManager from './passwordManager'; /* import PasswordManager from './passwordManager';
@ -24,7 +23,7 @@ export class TelegramMeWebService {
} }
AppStorage.get<any>('tgme_sync').then((curValue) => { AppStorage.get<any>('tgme_sync').then((curValue) => {
var ts = tsNow(true); var ts = Date.now() / 1000;
if(canRedirect && if(canRedirect &&
curValue && curValue &&
curValue.canRedirect == canRedirect && curValue.canRedirect == canRedirect &&

3
src/lib/mtproto/mtprotoworker.ts

@ -1,4 +1,4 @@
import {dT, isObject} from '../utils'; import {dT, isObject, $rootScope} from '../utils';
import AppStorage from '../storage'; import AppStorage from '../storage';
import CryptoWorkerMethods from '../crypto/crypto_methods'; import CryptoWorkerMethods from '../crypto/crypto_methods';
@ -126,6 +126,7 @@ class ApiManagerProxy extends CryptoWorkerMethods {
} }
public setUserAuth(userAuth: {id: number}) { public setUserAuth(userAuth: {id: number}) {
$rootScope.$broadcast('user_auth', userAuth);
return this.performTaskWorker('setUserAuth', userAuth); return this.performTaskWorker('setUserAuth', userAuth);
} }

14
src/lib/mtproto/networker.ts

@ -1,4 +1,4 @@
import {tsNow, isObject} from '../utils'; import {isObject} from '../bin_utils';
import {convertToUint8Array, import {convertToUint8Array,
bufferConcat, nextRandomInt, bytesToHex, longToBytes, bufferConcat, nextRandomInt, bytesToHex, longToBytes,
bytesCmp, uintToInt, bigStringInt} from '../bin_utils'; bytesCmp, uintToInt, bigStringInt} from '../bin_utils';
@ -295,7 +295,7 @@ class MTPNetworker {
public checkLongPoll() { public checkLongPoll() {
var isClean = this.cleanupSent(); var isClean = this.cleanupSent();
//this.log('Check lp', this.longPollPending, tsNow(), this.dcID, isClean, this); //this.log('Check lp', this.longPollPending, tsNow(), this.dcID, isClean, this);
if((this.longPollPending && tsNow() < this.longPollPending) || if((this.longPollPending && Date.now() < this.longPollPending) ||
this.offline || this.offline ||
NetworkerFactory.akStopped) { NetworkerFactory.akStopped) {
//this.log('No lp this time'); //this.log('No lp this time');
@ -307,7 +307,7 @@ class MTPNetworker {
if(isClean && ( if(isClean && (
baseDcID != self.dcID || baseDcID != self.dcID ||
self.upload || self.upload ||
(self.sleepAfter && tsNow() > self.sleepAfter) (self.sleepAfter && Date.now() > self.sleepAfter)
)) { )) {
//console.warn(dT(), 'Send long-poll for DC is delayed', self.dcID, self.sleepAfter); //console.warn(dT(), 'Send long-poll for DC is delayed', self.dcID, self.sleepAfter);
return; return;
@ -320,7 +320,7 @@ class MTPNetworker {
public sendLongPoll() { public sendLongPoll() {
let maxWait = 25000; let maxWait = 25000;
this.longPollPending = tsNow() + maxWait; this.longPollPending = Date.now() + maxWait;
//this.log('Set lp', this.longPollPending, tsNow()) //this.log('Set lp', this.longPollPending, tsNow())
this.wrapMtpCall('http_wait', { this.wrapMtpCall('http_wait', {
@ -364,7 +364,7 @@ class MTPNetworker {
} }
public pushResend(messageID: string, delay = 0) { public pushResend(messageID: string, delay = 0) {
var value = delay ? tsNow() + delay : 0; var value = delay ? Date.now() + delay : 0;
var sentMessage = this.sentMessages[messageID]; var sentMessage = this.sentMessages[messageID];
if(sentMessage.container) { if(sentMessage.container) {
for(var i = 0; i < sentMessage.inner.length; i++) { for(var i = 0; i < sentMessage.inner.length; i++) {
@ -548,7 +548,7 @@ class MTPNetworker {
var messages: Message[] = [], var messages: Message[] = [],
message: Message; message: Message;
var messagesByteLen = 0; var messagesByteLen = 0;
var currentTime: number = tsNow(); var currentTime = Date.now();
var hasApiCall = false; var hasApiCall = false;
var hasHttpWait = false; var hasHttpWait = false;
var lengthOverflow = false; var lengthOverflow = false;
@ -952,7 +952,7 @@ class MTPNetworker {
delay = 0; delay = 0;
} */ } */
var nextReq = tsNow() + delay; var nextReq = Date.now() + delay;
if(delay && this.nextReq && this.nextReq <= nextReq) { if(delay && this.nextReq && this.nextReq <= nextReq) {
return false; return false;

5
src/lib/mtproto/timeManager.ts

@ -1,5 +1,4 @@
import AppStorage from '../storage'; import AppStorage from '../storage';
import { tsNow } from '../utils';
import { nextRandomInt, longFromInts, dT } from '../bin_utils'; import { nextRandomInt, longFromInts, dT } from '../bin_utils';
export class TimeManager { export class TimeManager {
@ -15,7 +14,7 @@ export class TimeManager {
} }
public generateID(): string { public generateID(): string {
var timeTicks = tsNow(), var timeTicks = Date.now(),
timeSec = Math.floor(timeTicks / 1000) + this.timeOffset, timeSec = Math.floor(timeTicks / 1000) + this.timeOffset,
timeMSec = timeTicks % 1000, timeMSec = timeTicks % 1000,
random = nextRandomInt(0xFFFF); random = nextRandomInt(0xFFFF);
@ -34,7 +33,7 @@ export class TimeManager {
} }
public applyServerTime(serverTime: number, localTime?: number) { public applyServerTime(serverTime: number, localTime?: number) {
var newTimeOffset = serverTime - Math.floor((localTime || tsNow()) / 1000); var newTimeOffset = serverTime - Math.floor((localTime || Date.now()) / 1000);
var changed = Math.abs(this.timeOffset - newTimeOffset) > 10; var changed = Math.abs(this.timeOffset - newTimeOffset) > 10;
AppStorage.set({ AppStorage.set({
server_time_offset: newTimeOffset server_time_offset: newTimeOffset

11
src/lib/mtproto/tl_utils.ts

@ -5,10 +5,17 @@
* https://github.com/zhukov/webogram/blob/master/LICENSE * https://github.com/zhukov/webogram/blob/master/LICENSE
*/ */
import {bigint, intToUint, bigStringInt, bytesToHex, gzipUncompress, uintToInt} from '../bin_utils'; import {bigint, intToUint, bigStringInt, bytesToHex, uintToInt, isObject} from '../bin_utils';
import {isObject} from '../utils';
import Schema from './schema'; import Schema from './schema';
/// #if MTPROTO_WORKER
// @ts-ignore
import {gzipUncompress} from '../crypto/crypto_utils';
/// #else
// @ts-ignore
import {gzipUncompress} from '../bin_utils';
/// #endif
const boolFalse = +Schema.API.constructors.find((c: any) => c.predicate == 'boolFalse').id >>> 0; const boolFalse = +Schema.API.constructors.find((c: any) => c.predicate == 'boolFalse').id >>> 0;
const boolTrue = +Schema.API.constructors.find((c: any) => c.predicate == 'boolTrue').id >>> 0; const boolTrue = +Schema.API.constructors.find((c: any) => c.predicate == 'boolTrue').id >>> 0;
const vector = +Schema.API.constructors.find((c: any) => c.predicate == 'vector').id >>> 0; const vector = +Schema.API.constructors.find((c: any) => c.predicate == 'vector').id >>> 0;

4
src/lib/utils.js

@ -4,9 +4,9 @@
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com> * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
* https://github.com/zhukov/webogram/blob/master/LICENSE * https://github.com/zhukov/webogram/blob/master/LICENSE
*/ */
var _logTimer = (new Date()).getTime() var _logTimer = Date.now();
export function dT () { export function dT () {
return '[' + (((new Date()).getTime() - _logTimer) / 1000).toFixed(3) + ']' return '[' + ((Date.now() - _logTimer) / 1000).toFixed(3) + ']'
} }
export function checkClick (e, noprevent) { export function checkClick (e, noprevent) {

2
src/pages/pageSignIn.ts

@ -1,5 +1,5 @@
import { putPreloader, formatPhoneNumber } from "../components/misc"; import { putPreloader, formatPhoneNumber } from "../components/misc";
import Scrollable from '../components/scrollable'; import Scrollable from '../components/scrollable_new';
import {RichTextProcessor} from '../lib/richtextprocessor'; import {RichTextProcessor} from '../lib/richtextprocessor';
import Config from '../lib/config'; import Config from '../lib/config';

6
src/scss/partials/_chat.scss

@ -125,14 +125,16 @@ $chat-max-width: 696px;
flex: 1 1 auto; /* Lets middle column shrink/grow to available width */ flex: 1 1 auto; /* Lets middle column shrink/grow to available width */
overflow: hidden; overflow: hidden;
position: relative; position: relative;
padding: 0 .5rem;
> .scrollable { > .scrollable {
//position: unset; //position: unset;
height: auto; height: auto;
position: absolute; /* position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0; */
position: relative;
//display: flex; // for end //display: flex; // for end
//flex-direction: unset; //flex-direction: unset;

10
src/scss/partials/_emojiDropdown.scss

@ -108,8 +108,16 @@
content: ""; content: "";
flex: auto; flex: auto;
} */ } */
> div { > div {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
}
> div > div {
width: 80px; width: 80px;
height: 80px; height: 80px;
display: flex; display: flex;

49
src/scss/partials/_scrollable.scss

@ -45,11 +45,12 @@ div.scrollable::-webkit-scrollbar-thumb {
&.scrollable-y { &.scrollable-y {
overflow-y: auto; overflow-y: auto;
overflow-y: overlay;
scrollbar-width: none; scrollbar-width: none;
-ms-overflow-style: none; -ms-overflow-style: none;
} }
&.scrollable-x ~ .scrollbar-thumb { /* &.scrollable-x ~ .scrollbar-thumb {
top: auto; top: auto;
right: auto; right: auto;
width: auto; width: auto;
@ -63,10 +64,10 @@ div.scrollable::-webkit-scrollbar-thumb {
&:first-child + * { &:first-child + * {
flex: 1 1 auto; flex: 1 1 auto;
} }
} } */
} }
.scrollbar-thumb { /* .scrollbar-thumb {
position: absolute; position: absolute;
top: 0; top: 0;
right: 2px; right: 2px;
@ -80,7 +81,7 @@ div.scrollable::-webkit-scrollbar-thumb {
transition-duration: .2s; transition-duration: .2s;
transition-timing-function: ease-in-out; transition-timing-function: ease-in-out;
//display: none; display: none;
border-radius: $border-radius; border-radius: $border-radius;
z-index: 2; z-index: 2;
@ -88,4 +89,42 @@ div.scrollable::-webkit-scrollbar-thumb {
:hover > .scrollbar-thumb { :hover > .scrollbar-thumb {
opacity: .4; opacity: .4;
} } */
// BROWSER SCROLL
div.scrollable-y::-webkit-scrollbar {
width: .375rem;
height: 200px;
}
/* div.scrollable-y::-webkit-scrollbar-thumb {
border: 2px solid rgba(0, 0, 0, 0);
background-clip: padding-box;
} */
::-webkit-scrollbar-thumb {
opacity: 0;
transition: .2s ease-in-out;
}
div.scrollable:hover::-webkit-scrollbar-thumb {
height: 200px;
border-radius: $border-radius-medium;
background-color: rgba(0, 0, 0, 0.2);
opacity: 1;
}
::-webkit-scrollbar-button {
width: 0;
height: 0;
display: none;
}
::-webkit-scrollbar-corner {
background-color: transparent;
}

27952
stats.json

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save