Browse Source

virtual scroll stickers & right sidebar & pinned dialogs & notify settings

master
Eduard Kuzmenko 5 years ago
parent
commit
01b8e80e05
  1. BIN
      .DS_Store
  2. 12
      src/components/misc.ts
  3. 34
      src/components/pageIm.ts
  4. 5
      src/components/pageSignIn.ts
  5. 244
      src/components/scrollable.ts
  6. 270
      src/components/scrollable_spliceCount.ts
  7. 23
      src/lib/appManagers/appDialogsManager.ts
  8. 205
      src/lib/appManagers/appImManager.ts
  9. 28
      src/lib/appManagers/appSidebarLeft.ts
  10. 62
      src/lib/appManagers/appSidebarRight.ts
  11. 2
      src/lib/appManagers/appUsersManager.ts
  12. 22
      src/lib/lottieLoader.ts
  13. 4
      src/lib/mtproto/networker.ts
  14. 8
      src/lib/tl_utils.ts
  15. 29
      src/lib/utils.js
  16. 56
      src/scss/partials/_chatlist.scss
  17. 10
      src/scss/partials/_sidebar.scss
  18. 3
      src/scss/style.scss

BIN
.DS_Store vendored

Binary file not shown.

12
src/components/misc.ts

@ -8,6 +8,7 @@ import appDocsManager from "../lib/appManagers/appDocsManager";
import {AppImManager} from "../lib/appManagers/appImManager"; import {AppImManager} from "../lib/appManagers/appImManager";
import {AppMediaViewer} from '../lib/appManagers/appMediaViewer'; import {AppMediaViewer} from '../lib/appManagers/appMediaViewer';
import { RichTextProcessor } from "../lib/richtextprocessor"; import { RichTextProcessor } from "../lib/richtextprocessor";
import lottieLoader from "../lib/lottieLoader";
export type MTDocument = { export type MTDocument = {
_: 'document', _: 'document',
@ -434,7 +435,8 @@ export function scrollable(el: HTMLDivElement, x = false, y = true) {
resize(); resize();
} }
let splitUp = container.querySelector('ul'); //let splitUp = container.querySelector('ul');
let splitUp = container.children[1];
let children = Array.from(splitUp.children) as HTMLElement[]; let children = Array.from(splitUp.children) as HTMLElement[];
let firstVisible = -1, lastVisible = -1; let firstVisible = -1, lastVisible = -1;
let length = children.length; let length = children.length;
@ -627,7 +629,15 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
}, {once: true}); }, {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) { if(play) {
animation.play(); animation.play();

34
src/components/pageIm.ts

@ -1,7 +1,8 @@
//import { appImManager, appMessagesManager, appDialogsManager, apiUpdatesManager, appUsersManager } from "../lib/services"; //import { appImManager, appMessagesManager, appDialogsManager, apiUpdatesManager, appUsersManager } from "../lib/services";
import { putPreloader, horizontalMenu, wrapSticker, MTDocument, LazyLoadQueue, scrollable } from "./misc"; import { horizontalMenu, wrapSticker, MTDocument, LazyLoadQueue } from "./misc";
import Scrollable from './scrollable';
import { isElementInViewport, whichChild, findUpTag } from "../lib/utils"; import { whichChild, findUpTag } from "../lib/utils";
import {stackBlurImage} from '../lib/StackBlur'; import {stackBlurImage} from '../lib/StackBlur';
import * as Config from '../lib/config'; import * as Config from '../lib/config';
import { RichTextProcessor } from "../lib/richtextprocessor"; import { RichTextProcessor } from "../lib/richtextprocessor";
@ -12,7 +13,6 @@ import CryptoWorker from '../lib/crypto/cryptoworker';
import appStickersManager, { MTStickerSet } from "../lib/appManagers/appStickersManager"; import appStickersManager, { MTStickerSet } from "../lib/appManagers/appStickersManager";
import { AppImManager } from "../lib/appManagers/appImManager"; import { AppImManager } from "../lib/appManagers/appImManager";
import { AppMessagesManager } from "../lib/appManagers/appMessagesManager"; import { AppMessagesManager } from "../lib/appManagers/appMessagesManager";
import appSidebarRight from "../lib/appManagers/appSidebarRight";
import appSidebarLeft from "../lib/appManagers/appSidebarLeft"; import appSidebarLeft from "../lib/appManagers/appSidebarLeft";
const EMOTICONSSTICKERGROUP = 'emoticons-dropdown'; const EMOTICONSSTICKERGROUP = 'emoticons-dropdown';
@ -176,12 +176,13 @@ let initEmoticonsDropdown = (pageEl: HTMLDivElement,
let prevCategoryIndex = 1; let prevCategoryIndex = 1;
let menu = contentEmojiDiv.nextElementSibling as HTMLUListElement; let menu = contentEmojiDiv.nextElementSibling as HTMLUListElement;
let emojiScroll = scrollable(contentEmojiDiv).container; let emojiScroll = new Scrollable(contentEmojiDiv);
emojiScroll.addEventListener('scroll', (e) => { emojiScroll.container.addEventListener('scroll', (e) => {
prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, emojiScroll); prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, emojiScroll.container);
}); });
//emojiScroll.setVirtualContainer(emojiScroll.container);
emoticonsMenuOnClick(menu, heights, emojiScroll); emoticonsMenuOnClick(menu, heights, emojiScroll.container);
} }
let stickersInit = () => { let stickersInit = () => {
@ -191,7 +192,7 @@ let initEmoticonsDropdown = (pageEl: HTMLDivElement,
let menuWrapper = contentStickersDiv.nextElementSibling as HTMLDivElement; let menuWrapper = contentStickersDiv.nextElementSibling as HTMLDivElement;
let menu = menuWrapper.firstElementChild as HTMLUListElement; let menu = menuWrapper.firstElementChild as HTMLUListElement;
let menuScroll = scrollable(menuWrapper, true, false); let menuScroll = new Scrollable(menuWrapper, true, false);
let stickersDiv = document.createElement('div'); let stickersDiv = document.createElement('div');
stickersDiv.classList.add('stickers-categories'); stickersDiv.classList.add('stickers-categories');
@ -265,6 +266,8 @@ let initEmoticonsDropdown = (pageEl: HTMLDivElement,
heights[i] = (heights[i - 1] || 0) + div.scrollHeight; heights[i] = (heights[i - 1] || 0) + div.scrollHeight;
}); });
//stickersScroll.onScroll();
//return heights.push(prevHeight + scrollHeight) - 1; //return heights.push(prevHeight + scrollHeight) - 1;
}; };
@ -342,15 +345,16 @@ let initEmoticonsDropdown = (pageEl: HTMLDivElement,
}); });
let prevCategoryIndex = 0; let prevCategoryIndex = 0;
let stickersScroll = scrollable(contentStickersDiv).container; let stickersScroll = new Scrollable(contentStickersDiv);
stickersScroll.addEventListener('scroll', (e) => { stickersScroll.container.addEventListener('scroll', (e) => {
lazyLoadQueue.check(); lazyLoadQueue.check();
lottieLoader.checkAnimations(); lottieLoader.checkAnimations();
prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, stickersScroll); prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, stickersScroll.container);
}); });
stickersScroll.setVirtualContainer(stickersDiv);
emoticonsMenuOnClick(menu, heights, stickersScroll); emoticonsMenuOnClick(menu, heights, stickersScroll.container);
stickersInit = null; stickersInit = null;
}; };
@ -367,8 +371,7 @@ export default () => import('../lib/services').then(services => {
let pageEl = document.body.getElementsByClassName('page-chats')[0] as HTMLDivElement; let pageEl = document.body.getElementsByClassName('page-chats')[0] as HTMLDivElement;
pageEl.style.display = ''; pageEl.style.display = '';
let sidebarScroll = scrollable(document.body.querySelector('.profile-container')).container; let chatScroll = new Scrollable(document.getElementById('bubbles') as HTMLDivElement).container;
let chatScroll = scrollable(document.getElementById('bubbles') as HTMLDivElement).container;
apiUpdatesManager.attach(); apiUpdatesManager.attach();
@ -577,7 +580,7 @@ export default () => import('../lib/services').then(services => {
let inputMessageContainer = document.getElementsByClassName('input-message-container')[0] as HTMLDivElement; let inputMessageContainer = document.getElementsByClassName('input-message-container')[0] as HTMLDivElement;
let inputScroll = scrollable(inputMessageContainer); let inputScroll = new Scrollable(inputMessageContainer);
let sendMessage = () => { let sendMessage = () => {
let str = serializeNodes(Array.from(messageInput.childNodes)); let str = serializeNodes(Array.from(messageInput.childNodes));
@ -744,6 +747,5 @@ export default () => import('../lib/services').then(services => {
appSidebarLeft.loadDialogs().then(result => { appSidebarLeft.loadDialogs().then(result => {
appSidebarLeft.onChatsScroll(); appSidebarLeft.onChatsScroll();
appImManager.setScroll(chatScroll); appImManager.setScroll(chatScroll);
appSidebarRight.setScroll(sidebarScroll);
}); });
}); });

5
src/components/pageSignIn.ts

@ -1,5 +1,6 @@
import { MTProto } from "../lib/mtproto/mtproto"; import { MTProto } from "../lib/mtproto/mtproto";
import { putPreloader, getNearestDc, scrollable, formatPhoneNumber } from "./misc"; import { putPreloader, getNearestDc, formatPhoneNumber } from "./misc";
import Scrollable from './scrollable';
import {RichTextProcessor} from '../lib/richtextprocessor'; import {RichTextProcessor} from '../lib/richtextprocessor';
import * as Config from '../lib/config'; import * as Config from '../lib/config';
@ -51,7 +52,7 @@ export default () => {
wrapper.appendChild(list); wrapper.appendChild(list);
//let wrapperScroll = OverlayScrollbars(wrapper, (window as any).scrollbarOptions); //let wrapperScroll = OverlayScrollbars(wrapper, (window as any).scrollbarOptions);
scrollable(wrapper); let scroll = new Scrollable(wrapper);
let initedSelect = false; let initedSelect = false;

244
src/components/scrollable.ts

@ -0,0 +1,244 @@
import { isElementInViewport, isScrolledIntoView } from "../lib/utils";
export default class Scrollable {
public container: HTMLDivElement;
public thumb: HTMLDivElement;
public type: string;
public side: string;
public scrollType: string;
public scrollSide: string;
public scrollSize = -1;
public size = 0;
public thumbSize = 0;
public hiddenElements: {
up: Element[],
down: Element[]
} = {
up: [],
down: []
};
public paddings = {up: 0, down: 0};
public paddingTopDiv: HTMLDivElement;
public paddingBottomDiv: HTMLDivElement;
public splitUp: HTMLElement;
/* public topObserver: IntersectionObserver;
public isTopIntersecting: boolean;
public bottomObserver: IntersectionObserver;
public isBottomIntersecting: boolean; */
constructor(public el: HTMLDivElement, x = false, y = true) {
this.container = document.createElement('div');
this.container.classList.add('scrollable');
/* this.bottomObserver = new IntersectionObserver(entries => {
let entry = entries[0];
this.isBottomIntersecting = entry.intersectionRatio > 0;
// @ts-ignore
//console.log('bottom instersection:', entry, entry.isVisible, entry.intersectionRatio, entry.isIntersecting);
console.log('bottom intersection:', this.isBottomIntersecting);
});
this.topObserver = new IntersectionObserver(entries => {
let entry = entries[0];
this.isTopIntersecting = entry.intersectionRatio > 0;
// @ts-ignore
//console.log('top instersection:', entry, entry.isVisible, entry.intersectionRatio, entry.isIntersecting);
console.log('top intersection:', this.isTopIntersecting);
}); */
if(x) {
this.container.classList.add('scrollable-x');
this.type = 'width';
this.side = 'left';
this.scrollType = 'scrollWidth';
this.scrollSide = 'scrollLeft';
} else if(y) {
this.container.classList.add('scrollable-y');
this.type = 'height';
this.side = 'top';
this.scrollType = 'scrollHeight';
this.scrollSide = 'scrollTop';
} else {
throw new Error('no side for scroll');
}
this.thumb = document.createElement('div');
this.thumb.className = 'scrollbar-thumb';
// @ts-ignore
this.thumb.style[this.type] = '30px';
this.container.addEventListener('mouseover', this.resize.bind(this));
window.addEventListener('resize', this.resize.bind(this));
this.paddingTopDiv = document.createElement('div');
this.paddingTopDiv.classList.add('scroll-padding');
this.paddingBottomDiv = document.createElement('div');
this.paddingBottomDiv.classList.add('scroll-padding');
this.container.addEventListener('scroll', this.onScroll.bind(this));
//this.container.append(this.paddingTopDiv);
Array.from(el.children).forEach(c => this.container.append(c));
//this.container.append(this.paddingBottomDiv);
el.append(this.container);//container.append(el);
this.container.parentElement.append(this.thumb);
this.resize();
}
public resize() {
// @ts-ignore
this.scrollSize = this.container[this.scrollType];
let rect = this.container.getBoundingClientRect();
// @ts-ignore
this.size = rect[this.type];
if(!this.size || this.size == this.scrollSize) {
this.thumbSize = 0;
// @ts-ignore
this.thumb.style[this.type] = this.thumbSize + 'px';
return;
}
//if(!height) return;
let divider = this.scrollSize / this.size / 0.5;
this.thumbSize = this.size / divider;
if(this.thumbSize < 20) this.thumbSize = 20;
// @ts-ignore
this.thumb.style[this.type] = this.thumbSize + 'px';
// @ts-ignore
//console.log('onresize', thumb.style[type], thumbHeight, height);
}
public setVirtualContainer(el: HTMLElement) {
this.splitUp = el;
this.hiddenElements = {
up: [],
down: []
};
this.paddings = {
up: 0,
down: 0
};
if(this.paddingTopDiv.parentElement) {
this.paddingTopDiv.style.height = '';
this.paddingBottomDiv.style.height = '';
}
//this.topObserver.observe(this.paddingTopDiv);
//this.bottomObserver.observe(this.paddingBottomDiv);
el.parentElement.insertBefore(this.paddingTopDiv, el);
el.parentNode.insertBefore(this.paddingBottomDiv, el.nextSibling);
}
public onScroll() {
// @ts-ignore
//let st = container[scrollSide];
if(this.container[this.scrollType] != this.scrollSize || this.thumbSize == 0) {
this.resize();
}
// @ts-ignore
let value = this.container[this.scrollSide] / (this.scrollSize - this.size) * 100;
let maxValue = 100 - (this.thumbSize / this.size * 100);
//console.log('onscroll', container.scrollHeight, thumbHeight, height, value, maxValue);
// @ts-ignore
this.thumb.style[this.side] = (value >= maxValue ? maxValue : value) + '%';
if(!this.splitUp) {
return;
}
let splitUp = this.splitUp;
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) || isScrolledIntoView(child)) {
if(firstVisible < 0) firstVisible = i;
lastVisible = i;
}
}
//console.log('onscroll', firstVisible, lastVisible);
if(firstVisible > 0) {
let sliced = children.slice(0, firstVisible);
for(let child of sliced) {
this.paddings.up += child.scrollHeight;
this.hiddenElements.up.push(child);
child.parentElement.removeChild(child);
}
//console.log('onscroll sliced up', sliced.length);
//sliced.forEach(child => child.style.display = 'none');
this.paddingTopDiv.style.height = this.paddings.up + 'px';
//console.log('onscroll need to add padding: ', paddings.up);
} else if(this.hiddenElements.up.length) {
//console.log('onscroll up', isElementInViewport(this.paddingTopDiv), isScrolledIntoView(this.paddingTopDiv), this.paddings.up);
while((isElementInViewport(this.paddingTopDiv) || isScrolledIntoView(this.paddingTopDiv)) && this.paddings.up) {
let child = this.hiddenElements.up.pop();
splitUp.prepend(child);
this.paddings.up -= child.scrollHeight;
this.paddingTopDiv.style.height = this.paddings.up + 'px';
}
}
if(lastVisible < (length - 1)) {
let sliced = children.slice(lastVisible + 1).reverse();
for(let child of sliced) {
this.paddings.down += child.scrollHeight;
this.hiddenElements.down.unshift(child);
child.parentElement.removeChild(child);
}
//console.log('onscroll sliced down', splitUp, sliced.length, this.paddings.down + 'px');
this.paddingBottomDiv.style.height = this.paddings.down + 'px';
//console.log('onscroll need to add padding: ', paddings.up);
} else if(this.hiddenElements.down.length) {
//console.log('onscroll down', isElementInViewport(this.paddingBottomDiv),
//isScrolledIntoView(this.paddingBottomDiv), this.paddings.down, this.hiddenElements);
while((isElementInViewport(this.paddingBottomDiv) || isScrolledIntoView(this.paddingBottomDiv)) && this.paddings.down) {
let child = this.hiddenElements.down.shift();
splitUp.append(child);
this.paddings.down -= child.scrollHeight;
this.paddingBottomDiv.style.height = this.paddings.down + 'px';
}
}
//console.log('onscroll', container, firstVisible, lastVisible, hiddenElements);
//lastScrollPos = st;
}
}

270
src/components/scrollable_spliceCount.ts

@ -0,0 +1,270 @@
import { isElementInViewport } from "../lib/utils";
export default class Scrollable {
public container: HTMLDivElement;
public thumb: HTMLDivElement;
public type: string;
public side: string;
public scrollType: string;
public scrollSide: string;
public scrollSize = -1;
public size = 0;
public thumbSize = 0;
public hiddenElements: {
up: Element[],
down: Element[]
} = {
up: [],
down: []
};
public paddings = {up: 0, down: 0};
public paddingTopDiv: HTMLDivElement;
public paddingBottomDiv: HTMLDivElement;
public splitUp: HTMLElement;
public spliceCount = 1;
public useStylePadding = false;
public useOneHeight = false;
constructor(public el: HTMLDivElement, x = false, y = true) {
this.container = document.createElement('div');
this.container.classList.add('scrollable');
if(x) {
this.container.classList.add('scrollable-x');
this.type = 'width';
this.side = 'left';
this.scrollType = 'scrollWidth';
this.scrollSide = 'scrollLeft';
} else if(y) {
this.container.classList.add('scrollable-y');
this.type = 'height';
this.side = 'top';
this.scrollType = 'scrollHeight';
this.scrollSide = 'scrollTop';
} else {
throw new Error('no side for scroll');
}
this.thumb = document.createElement('div');
this.thumb.className = 'scrollbar-thumb';
// @ts-ignore
this.thumb.style[this.type] = '30px';
this.container.addEventListener('mouseover', this.resize.bind(this));
window.addEventListener('resize', this.resize.bind(this));
this.paddingTopDiv = document.createElement('div');
this.paddingTopDiv.classList.add('scroll-padding');
this.paddingBottomDiv = document.createElement('div');
this.paddingBottomDiv.classList.add('scroll-padding');
this.container.addEventListener('scroll', this.onScroll.bind(this));
this.container.append(this.paddingTopDiv);
Array.from(el.children).forEach(c => this.container.append(c));
this.container.append(this.paddingBottomDiv);
el.append(this.container);//container.append(el);
this.container.parentElement.append(this.thumb);
this.resize();
}
public resize() {
// @ts-ignore
this.scrollSize = this.container[this.scrollType];
let rect = this.container.getBoundingClientRect();
// @ts-ignore
this.size = rect[this.type];
if(!this.size || this.size == this.scrollSize) {
this.thumbSize = 0;
// @ts-ignore
this.thumb.style[this.type] = this.thumbSize + 'px';
return;
}
//if(!height) return;
let divider = this.scrollSize / this.size / 0.5;
this.thumbSize = this.size / divider;
if(this.thumbSize < 20) this.thumbSize = 20;
// @ts-ignore
this.thumb.style[this.type] = this.thumbSize + 'px';
// @ts-ignore
//console.log('onresize', thumb.style[type], thumbHeight, height);
}
public setVirtualContainer(el: HTMLElement, spliceCount = 1, useStylePadding = false, useOneHeight = false) {
this.splitUp = el;
this.hiddenElements = {
up: [],
down: []
};
this.paddings = {
up: 0,
down: 0
};
this.spliceCount = spliceCount;
this.useStylePadding = useStylePadding;
this.useOneHeight = useOneHeight;
if(this.paddingTopDiv.parentElement) {
this.paddingTopDiv.style.height = '';
this.paddingBottomDiv.style.height = '';
}
/* if(useStylePadding) {
this.paddingTopDiv.parentElement.removeChild(this.paddingTopDiv);
this.paddingBottomDiv.parentElement.removeChild(this.paddingBottomDiv);
} else { */
el.parentElement.insertBefore(this.paddingTopDiv, el);
el.parentNode.insertBefore(this.paddingBottomDiv, el.nextSibling);
//}
if(useStylePadding) {
this.paddingTopDiv.style.height = '10px';
this.paddingBottomDiv.style.height = '10px';
}
}
public onScroll() {
// @ts-ignore
//let st = container[scrollSide];
if(this.container[this.scrollType] != this.scrollSize || this.thumbSize == 0) {
this.resize();
}
// @ts-ignore
let value = this.container[this.scrollSide] / (this.scrollSize - this.size) * 100;
let maxValue = 100 - (this.thumbSize / this.size * 100);
//console.log('onscroll', container.scrollHeight, thumbHeight, height, value, maxValue);
// @ts-ignore
this.thumb.style[this.side] = (value >= maxValue ? maxValue : value) + '%';
if(!this.splitUp) {
return;
}
let splitUp = this.splitUp;
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;
}
}
console.log('onscroll', firstVisible, lastVisible);
if(firstVisible > 0) {
let sliced = children.slice(0, firstVisible);
let height = 0, singleHeight = sliced[0].scrollHeight;
for(let child of sliced) {
height += child.scrollHeight;
this.hiddenElements.up.push(child);
child.parentElement.removeChild(child);
}
this.paddings.up += this.useOneHeight ? singleHeight : height;
//console.log('sliced up', sliced.length);
//sliced.forEach(child => child.style.display = 'none');
if(this.useStylePadding) splitUp.style.paddingTop = this.paddings.up + 'px';
else this.paddingTopDiv.style.height = this.paddings.up + 'px';
//console.log('onscroll need to add padding: ', paddings.up);
} else if(this.hiddenElements.up.length) {
console.log('onscroll up', isElementInViewport(this.paddingTopDiv), this.paddings.up);
while(isElementInViewport(this.paddingTopDiv) && this.paddings.up) {
//let child = this.hiddenElements.up.pop();
/*
splitUp.prepend(...childs);
this.paddings.up -= child.scrollHeight;
this.paddingTopDiv.style.height = this.paddings.up + 'px';*/
let childs = this.hiddenElements.up.splice(-this.spliceCount).reverse();
let height = 0;
for(let child of childs) {
splitUp.prepend(child);
height += child.scrollHeight;
}
this.paddings.up -= this.useOneHeight ? childs[0].scrollHeight : height;
if(this.useStylePadding) splitUp.style.paddingTop = this.paddings.up + 'px';
else this.paddingTopDiv.style.height = this.paddings.up + 'px';
}
}
if(lastVisible < (length - 1)) {
let sliced = children.slice(lastVisible + 1, this.useOneHeight ? lastVisible + 1 + this.spliceCount : undefined).reverse();
let height = 0, singleHeight = sliced[0].scrollHeight;
for(let child of sliced) {
height += child.scrollHeight;
this.hiddenElements.down.unshift(child);
child.parentElement.removeChild(child);
}
this.paddings.down += this.useOneHeight ? singleHeight : height;
console.log('onscroll sliced down', splitUp, sliced.length, this.paddings.down + 'px');
//sliced.forEach(child => child.style.display = 'none');
/* if(this.useStylePadding) splitUp.style.paddingBottom = this.paddings.down + 'px';
else */ this.paddingBottomDiv.style.height = this.paddings.down + 'px';
//console.log('onscroll need to add padding: ', paddings.up);
} else if(this.hiddenElements.down.length) {
console.log('onscroll down', isElementInViewport(this.paddingBottomDiv), this.paddings.down, this.hiddenElements);
while(isElementInViewport(this.paddingBottomDiv) && this.paddings.down) {
/* let child = this.hiddenElements.down.shift();
splitUp.append(child);
this.paddings.down -= child.scrollHeight;
this.paddingBottomDiv.style.height = this.paddings.down + 'px'; */
let childs = this.hiddenElements.down.splice(0, this.spliceCount);
let height = 0;
for(let child of childs) {
splitUp.append(child);
height += child.scrollHeight;
}
this.paddings.down -= this.useOneHeight ? childs[0].scrollHeight : height;
/* if(this.useStylePadding) splitUp.style.paddingBottom = this.paddings.down + 'px';
else */ this.paddingBottomDiv.style.height = this.paddings.down + 'px';
}
}
//console.log('onscroll', container, firstVisible, lastVisible, hiddenElements);
//lastScrollPos = st;
}
}

23
src/lib/appManagers/appDialogsManager.ts

@ -16,17 +16,23 @@ type DialogDom = {
lastTimeSpan: HTMLSpanElement, lastTimeSpan: HTMLSpanElement,
unreadMessagesSpan: HTMLSpanElement, unreadMessagesSpan: HTMLSpanElement,
lastMessageSpan: HTMLSpanElement, lastMessageSpan: HTMLSpanElement,
containerEl: HTMLDivElement,
listEl: HTMLLIElement listEl: HTMLLIElement
}; };
export class AppDialogsManager { export class AppDialogsManager {
public chatList = document.getElementById('dialogs') as HTMLUListElement; public chatList = document.getElementById('dialogs') as HTMLUListElement;
public pinnedDelimiter: HTMLDivElement;
public chatsHidden: any; public chatsHidden: any;
public myID = 0; public myID = 0;
public doms: {[x: number]: any} = {}; public doms: {[x: number]: any} = {};
constructor() { constructor() {
this.pinnedDelimiter = document.createElement('div');
this.pinnedDelimiter.classList.add('pinned-delimiter');
this.pinnedDelimiter.appendChild(document.createElement('span'));
apiManager.getUserID().then((id) => { apiManager.getUserID().then((id) => {
this.myID = id; this.myID = id;
}); });
@ -168,7 +174,7 @@ export class AppDialogsManager {
} else if(this.chatsHidden.down.find((d: HTMLLIElement) => d === dom.listEl)) { } else if(this.chatsHidden.down.find((d: HTMLLIElement) => d === dom.listEl)) {
inBottom.push(dom.listEl); inBottom.push(dom.listEl);
} else { } else {
console.warn('found no dom!', dom); //console.warn('found no dom!', dom, d);
} }
//this.chatList.append(dom.listEl); //this.chatList.append(dom.listEl);
@ -395,12 +401,22 @@ export class AppDialogsManager {
//captionDiv.append(titleSpan); //captionDiv.append(titleSpan);
//captionDiv.append(span); //captionDiv.append(span);
let paddingDiv = document.createElement('div');
paddingDiv.classList.add('rp');
paddingDiv.append(avatarDiv, captionDiv);
ripple(paddingDiv);
let li = document.createElement('li'); let li = document.createElement('li');
li.append(paddingDiv);
li.setAttribute('data-peerID', '' + peerID);
/* let li = document.createElement('li');
li.classList.add('rp'); li.classList.add('rp');
li.append(avatarDiv, captionDiv); li.append(avatarDiv, captionDiv);
li.setAttribute('data-peerID', '' + peerID); li.setAttribute('data-peerID', '' + peerID);
ripple(li); ripple(li); */
/* let detailsDiv = document.createElement('div'); /* let detailsDiv = document.createElement('div');
detailsDiv.classList.add('dialog-details'); */ detailsDiv.classList.add('dialog-details'); */
@ -432,6 +448,7 @@ export class AppDialogsManager {
lastTimeSpan, lastTimeSpan,
unreadMessagesSpan, unreadMessagesSpan,
lastMessageSpan: span, lastMessageSpan: span,
containerEl: paddingDiv,
listEl: li listEl: li
}; };
@ -441,6 +458,8 @@ export class AppDialogsManager {
if(dialog.pFlags.pinned) { if(dialog.pFlags.pinned) {
li.classList.add('dialog-pinned'); li.classList.add('dialog-pinned');
//this.chatList.insertBefore(this.pinnedDelimiter, li.nextSibling);
dom.listEl.append(this.pinnedDelimiter);
} }
this.doms[dialog.peerID] = dom; this.doms[dialog.peerID] = dom;

205
src/lib/appManagers/appImManager.ts

@ -101,6 +101,7 @@ export class AppImManager {
public myID = 0; public myID = 0;
public peerID = 0; public peerID = 0;
public muted = false;
public lastDialog: any; public lastDialog: any;
public bubbles: {[mid: number]: HTMLDivElement} = {}; public bubbles: {[mid: number]: HTMLDivElement} = {};
@ -198,54 +199,8 @@ export class AppImManager {
$rootScope.$on('apiUpdate', (e: CustomEvent) => { $rootScope.$on('apiUpdate', (e: CustomEvent) => {
let update = e.detail; let update = e.detail;
switch(update._) { this.handleUpdate(update);
case 'updateUserTyping': });
case 'updateChatUserTyping':
if(this.myID == update.user_id) {
return;
}
var peerID = update._ == 'updateUserTyping' ? update.user_id : -update.chat_id;
this.typingUsers[update.user_id] = peerID;
if(!appUsersManager.hasUser(update.user_id)) {
if(update.chat_id &&
appChatsManager.hasChat(update.chat_id) &&
!appChatsManager.isChannel(update.chat_id)) {
appProfileManager.getChatFull(update.chat_id);
}
//return;
}
appUsersManager.forceUserOnline(update.user_id);
let dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
let currentPeer = this.peerID == peerID;
if(this.typingTimeouts[peerID]) clearTimeout(this.typingTimeouts[peerID]);
else if(dialog) {
appDialogsManager.setTyping(dialog, appUsersManager.getUser(update.user_id));
if(currentPeer) { // user
this.setPeerStatus();
}
}
this.typingTimeouts[peerID] = setTimeout(() => {
this.typingTimeouts[peerID] = 0;
delete this.typingUsers[update.user_id];
if(dialog) {
appDialogsManager.unsetTyping(dialog);
}
// лень просчитывать случаи
this.setPeerStatus();
}, 6000);
break;
}
})
window.addEventListener('blur', () => { window.addEventListener('blur', () => {
lottieLoader.checkAnimations(true); lottieLoader.checkAnimations(true);
@ -298,6 +253,9 @@ export class AppImManager {
this.setPeer(this.peerID, mid); this.setPeer(this.peerID, mid);
}); });
this.btnMenuMute.addEventListener('click', () => this.mutePeer());
this.btnMute.addEventListener('click', () => this.mutePeer());
this.updateStatusInterval = window.setInterval(() => this.updateStatus(), 50e3); this.updateStatusInterval = window.setInterval(() => this.updateStatus(), 50e3);
this.updateStatus(); this.updateStatus();
setInterval(() => this.setPeerStatus(), 60e3); setInterval(() => this.setPeerStatus(), 60e3);
@ -524,11 +482,10 @@ export class AppImManager {
} }
} }
public cleanup() { public cleanup() {
this.peerID = $rootScope.selectedPeerID = 0; this.peerID = $rootScope.selectedPeerID = 0;
this.scrolledAll = false; this.scrolledAll = false;
this.muted = false;
if(this.lastContainerDiv) this.lastContainerDiv.remove(); if(this.lastContainerDiv) this.lastContainerDiv.remove();
if(this.firstContainerDiv) this.firstContainerDiv.remove(); if(this.firstContainerDiv) this.firstContainerDiv.remove();
@ -545,6 +502,8 @@ export class AppImManager {
this.unreadOut = []; this.unreadOut = [];
this.loadMediaQueue = []; this.loadMediaQueue = [];
lottieLoader.checkAnimations(false, 'chat', true);
console.time('chatInner clear'); console.time('chatInner clear');
this.chatInner.innerHTML = ''; this.chatInner.innerHTML = '';
@ -954,7 +913,7 @@ export class AppImManager {
} }
return true; return true;
}, null, '', false, !!message.pending)/* .then(() => { }, null, 'chat', false, !!message.pending || !multipleRender)/* .then(() => {
attachmentDiv.style.width = ''; attachmentDiv.style.width = '';
attachmentDiv.style.height = ''; attachmentDiv.style.height = '';
@ -1239,6 +1198,150 @@ export class AppImManager {
return true; return true;
}); });
} }
public setMutedState(muted = false) {
appSidebarRight.profileElements.notificationsCheckbox.checked = !muted;
appSidebarRight.profileElements.notificationsStatus.innerText = muted ? 'Disabled' : 'Enabled';
let peerID = this.peerID;
this.muted = muted;
if(peerID < 0) { // not human
let isChannel = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID);
if(isChannel) {
this.btnMute.classList.remove('tgico-mute', 'tgico-unmute');
this.btnMute.classList.add(muted ? 'tgico-unmute' : 'tgico-mute');
this.btnMute.style.display = '';
} else {
this.btnMute.style.display = 'none';
}
} else {
this.btnMute.style.display = 'none';
}
this.btnMenuMute.classList.remove('tgico-mute', 'tgico-unmute');
this.btnMenuMute.classList.add(muted ? 'tgico-unmute' : 'tgico-mute');
let rp = this.btnMenuMute.firstElementChild;
this.btnMenuMute.innerText = muted ? 'Unmute' : 'Mute';
this.btnMenuMute.appendChild(rp);
}
public mutePeer() {
let inputPeer = appPeersManager.getInputPeerByID(this.peerID);
let inputNotifyPeer = {
_: 'inputNotifyPeer',
peer: inputPeer
};
let settings: any = {
_: 'inputPeerNotifySettings',
flags: 0,
mute_until: 0
};
if(!this.muted) {
settings.flags |= 2 << 1;
settings.mute_until = 2147483646;
} else {
settings.flags |= 1 << 1;
}
apiManager.invokeApi('account.updateNotifySettings', {
peer: inputNotifyPeer,
settings: settings
}).then(res => {
this.handleUpdate({_: 'updateNotifySettings', peer: inputNotifyPeer, notify_settings: settings});
});
/* return apiManager.invokeApi('account.getNotifySettings', {
peer: inputNotifyPeer
}).then((settings: any) => {
settings.flags |= 2 << 1;
settings.mute_until = 2000000000; // 2147483646
return apiManager.invokeApi('account.updateNotifySettings', {
peer: inputNotifyPeer,
settings: Object.assign(settings, {
_: 'inputPeerNotifySettings'
})
}).then(res => {
this.log('mute result:', res);
});
}); */
}
public handleUpdate(update: any) {
switch(update._) {
case 'updateUserTyping':
case 'updateChatUserTyping':
if(this.myID == update.user_id) {
return;
}
var peerID = update._ == 'updateUserTyping' ? update.user_id : -update.chat_id;
this.typingUsers[update.user_id] = peerID;
if(!appUsersManager.hasUser(update.user_id)) {
if(update.chat_id &&
appChatsManager.hasChat(update.chat_id) &&
!appChatsManager.isChannel(update.chat_id)) {
appProfileManager.getChatFull(update.chat_id);
}
//return;
}
appUsersManager.forceUserOnline(update.user_id);
let dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
let currentPeer = this.peerID == peerID;
if(this.typingTimeouts[peerID]) clearTimeout(this.typingTimeouts[peerID]);
else if(dialog) {
appDialogsManager.setTyping(dialog, appUsersManager.getUser(update.user_id));
if(currentPeer) { // user
this.setPeerStatus();
}
}
this.typingTimeouts[peerID] = setTimeout(() => {
this.typingTimeouts[peerID] = 0;
delete this.typingUsers[update.user_id];
if(dialog) {
appDialogsManager.unsetTyping(dialog);
}
// лень просчитывать случаи
this.setPeerStatus();
}, 6000);
break;
case 'updateNotifySettings': {
let {peer, notify_settings} = update;
// peer was NotifyPeer
peer = peer.peer;
let peerID = appPeersManager.getPeerID(peer);
let dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
if(dialog) {
dialog.notify_settings = notify_settings;
}
if(peerID == this.peerID) {
let muted = notify_settings.mute_until ? new Date(notify_settings.mute_until * 1000) > new Date() : false;
this.setMutedState(muted);
}
this.log('updateNotifySettings', peerID, notify_settings);
break;
}
}
}
} }
export default new AppImManager(); export default new AppImManager();

28
src/lib/appManagers/appSidebarLeft.ts

@ -1,5 +1,6 @@
import { logger } from "../polyfill"; import { logger } from "../polyfill";
import { scrollable, putPreloader } from "../../components/misc"; import { putPreloader } from "../../components/misc";
import Scrollable from '../../components/scrollable';
import appMessagesManager from "./appMessagesManager"; import appMessagesManager from "./appMessagesManager";
import appDialogsManager from "./appDialogsManager"; import appDialogsManager from "./appDialogsManager";
import { isElementInViewport } from "../utils"; import { isElementInViewport } from "../utils";
@ -20,12 +21,9 @@ class AppSidebarLeft {
private chatsContainer = document.getElementById('chats-container') as HTMLDivElement; private chatsContainer = document.getElementById('chats-container') as HTMLDivElement;
private chatsOffsetIndex = 0; private chatsOffsetIndex = 0;
private chatsScroll: HTMLDivElement;
private chatsHidden: any;
private chatsPreloader: HTMLDivElement; private chatsPreloader: HTMLDivElement;
private chatsLoadCount = 0; private chatsLoadCount = 0;
private loadDialogsPromise: Promise<any>; private loadDialogsPromise: Promise<any>;
private hiddenScroll: any;
private log = logger('SL'); private log = logger('SL');
@ -40,6 +38,8 @@ class AppSidebarLeft {
private query = ''; private query = '';
public scroll: Scrollable = null;
constructor() { constructor() {
this.chatsPreloader = document.createElement('div'); this.chatsPreloader = document.createElement('div');
this.chatsPreloader.classList.add('preloader'); this.chatsPreloader.classList.add('preloader');
@ -48,16 +48,13 @@ class AppSidebarLeft {
this.chatsLoadCount = Math.round(document.body.scrollHeight / 70 * 1.5); this.chatsLoadCount = Math.round(document.body.scrollHeight / 70 * 1.5);
let {container: chatsScroll, hiddenElements: chatsHidden, onScroll: hiddenScroll} = scrollable(this.chatsContainer as HTMLDivElement); this.scroll = new Scrollable(this.chatsContainer as HTMLDivElement);
this.chatsScroll = chatsScroll; appDialogsManager.chatsHidden = this.scroll.hiddenElements;
this.chatsHidden = chatsHidden; this.scroll.setVirtualContainer(appDialogsManager.chatList);
this.hiddenScroll = hiddenScroll;
appDialogsManager.chatsHidden = this.chatsHidden;
chatsScroll.addEventListener('scroll', this.onChatsScroll.bind(this)); this.scroll.container.addEventListener('scroll', this.onChatsScroll.bind(this));
this.listsContainer = scrollable(this.searchContainer).container; this.listsContainer = new Scrollable(this.searchContainer).container;
this.searchMessagesList = document.createElement('ul'); this.searchMessagesList = document.createElement('ul');
this.savedBtn.addEventListener('click', () => { this.savedBtn.addEventListener('click', () => {
@ -163,8 +160,8 @@ class AppSidebarLeft {
}); });
} }
this.log('loaded ' + this.chatsLoadCount + ' dialogs by offset:', this.chatsOffsetIndex, result, this.chatsHidden); this.log('loaded ' + this.chatsLoadCount + ' dialogs by offset:', this.chatsOffsetIndex, result, this.scroll.hiddenElements);
this.hiddenScroll(); this.scroll.onScroll();
} catch(err) { } catch(err) {
this.log.error(err); this.log.error(err);
} }
@ -174,7 +171,8 @@ class AppSidebarLeft {
} }
public onChatsScroll() { public onChatsScroll() {
if(this.chatsHidden.down.length > 0/* || 1 == 1 */) return; //this.log(this.scroll);
if(this.scroll.hiddenElements.down.length > 0/* || 1 == 1 */) return;
if(!this.loadDialogsPromise) { if(!this.loadDialogsPromise) {
let d = Array.from(appDialogsManager.chatList.childNodes).slice(-5); let d = Array.from(appDialogsManager.chatList.childNodes).slice(-5);

62
src/lib/appManagers/appSidebarRight.ts

@ -1,4 +1,5 @@
import { LazyLoadQueue, horizontalMenu, wrapDocument, formatPhoneNumber } from "../../components/misc"; import { LazyLoadQueue, horizontalMenu, wrapDocument, formatPhoneNumber } from "../../components/misc";
import Scrollable from '../../components/scrollable';
import { isElementInViewport, $rootScope } from "../utils"; import { isElementInViewport, $rootScope } from "../utils";
import appMessagesManager from "./appMessagesManager"; import appMessagesManager from "./appMessagesManager";
import appPhotosManager from "./appPhotosManager"; import appPhotosManager from "./appPhotosManager";
@ -33,7 +34,7 @@ class AppSidebarRight {
contentAudio: this.profileContentEl.querySelector('#content-audio') as HTMLDivElement, contentAudio: this.profileContentEl.querySelector('#content-audio') as HTMLDivElement,
}; };
public sidebarScroll: HTMLDivElement = null; public lastSharedMediaDiv: HTMLDivElement = null;
private loadSidebarMediaPromises: { private loadSidebarMediaPromises: {
[type: string]: Promise<void> [type: string]: Promise<void>
@ -67,13 +68,21 @@ class AppSidebarRight {
private peerID = 0; private peerID = 0;
public sidebarScroll: Scrollable = null;
constructor() { constructor() {
let container = this.profileContentEl.querySelector('.profile-tabs-content') as HTMLDivElement; let container = this.profileContentEl.querySelector('.profile-tabs-content') as HTMLDivElement;
let tabs = this.profileContentEl.querySelector('.profile-tabs') as HTMLUListElement; let tabs = this.profileContentEl.querySelector('.profile-tabs') as HTMLUListElement;
this.sidebarScroll = new Scrollable(this.sidebarEl);
this.sidebarScroll.container.addEventListener('scroll', this.onSidebarScroll.bind(this));
horizontalMenu(tabs, container, (id, tabContent) => { horizontalMenu(tabs, container, (id, tabContent) => {
this.sharedMediaType = this.sharedMediaTypes[id]; this.sharedMediaType = this.sharedMediaTypes[id];
this.sharedMediaSelected = tabContent.firstElementChild as HTMLDivElement; this.sharedMediaSelected = tabContent.firstElementChild as HTMLDivElement;
this.log('setVirtualContainer', this.sharedMediaSelected);
this.sidebarScroll.setVirtualContainer(this.sharedMediaSelected);
}, this.onSidebarScroll.bind(this)); }, this.onSidebarScroll.bind(this));
(tabs.children[1] as HTMLLIElement).click(); // set media (tabs.children[1] as HTMLLIElement).click(); // set media
@ -96,6 +105,11 @@ class AppSidebarRight {
appMediaViewer.openMedia(message, false); appMediaViewer.openMedia(message, false);
}); });
this.profileElements.notificationsCheckbox.addEventListener('change', () => {
let checked = this.profileElements.notificationsCheckbox.checked;
appImManager.mutePeer();
});
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
setTimeout(() => this.onSidebarScroll(), 0); setTimeout(() => this.onSidebarScroll(), 0);
}); });
@ -104,7 +118,7 @@ class AppSidebarRight {
public onSidebarScroll() { public onSidebarScroll() {
this.lazyLoadQueueSidebar.check(); this.lazyLoadQueueSidebar.check();
if(this.sharedMediaSelected) { if(this.sharedMediaSelected && !this.sidebarScroll.hiddenElements.down.length/* && false */) {
let media = Array.from(this.sharedMediaSelected.childNodes).slice(-15); let media = Array.from(this.sharedMediaSelected.childNodes).slice(-15);
for(let div of media) { for(let div of media) {
if(isElementInViewport(div)) { if(isElementInViewport(div)) {
@ -117,16 +131,6 @@ class AppSidebarRight {
} }
} }
public setScroll(scroll: HTMLDivElement) {
this.sidebarScroll = scroll;
this.sidebarScroll.addEventListener('scroll', this.onSidebarScroll.bind(this));
/* this.sidebarScroll.options({
callbacks: {
onScroll: this.onSidebarScroll.bind(this)
}
}); */
}
public toggleSidebar(enable?: boolean) { public toggleSidebar(enable?: boolean) {
this.log('sidebarEl', this.sidebarEl, enable, isElementInViewport(this.sidebarEl)); this.log('sidebarEl', this.sidebarEl, enable, isElementInViewport(this.sidebarEl));
@ -240,7 +244,13 @@ class AppSidebarRight {
this.lazyLoadQueueSidebar.push({div, load}); this.lazyLoadQueueSidebar.push({div, load});
this.sharedMedia.contentMedia.append(div); this.lastSharedMediaDiv.append(div);
if(this.lastSharedMediaDiv.childElementCount == 3) {
this.sharedMedia.contentMedia.append(this.lastSharedMediaDiv);
this.lastSharedMediaDiv = document.createElement('div');
}
//this.sharedMedia.contentMedia.append(div);
break; break;
} }
@ -285,12 +295,13 @@ class AppSidebarRight {
public fillProfileElements() { public fillProfileElements() {
let peerID = this.peerID = $rootScope.selectedPeerID; let peerID = this.peerID = $rootScope.selectedPeerID;
this.loadSidebarMediaPromises = {}; this.loadSidebarMediaPromises = {};
this.lastSharedMediaDiv = document.createElement('div');
this.profileContentEl.parentElement.scrollTop = 0; this.profileContentEl.parentElement.scrollTop = 0;
this.profileElements.bio.style.display = 'none'; this.profileElements.bio.style.display = 'none';
this.profileElements.phone.style.display = 'none'; this.profileElements.phone.style.display = 'none';
this.profileElements.username.style.display = 'none'; this.profileElements.username.style.display = 'none';
this.profileElements.notificationsCheckbox.setAttribute('checked', 'checked'); this.profileElements.notificationsCheckbox.checked = true;
this.profileElements.notificationsStatus.innerText = 'Enabled'; this.profileElements.notificationsStatus.innerText = 'Enabled';
Object.keys(this.sharedMedia).forEach(key => { Object.keys(this.sharedMedia).forEach(key => {
@ -365,30 +376,9 @@ class AppSidebarRight {
let muted = false; let muted = false;
if(dialog.notify_settings && dialog.notify_settings.mute_until) { if(dialog.notify_settings && dialog.notify_settings.mute_until) {
muted = new Date(dialog.notify_settings.mute_until * 1000) > new Date(); muted = new Date(dialog.notify_settings.mute_until * 1000) > new Date();
if(muted) {
this.profileElements.notificationsCheckbox.removeAttribute('checked');
this.profileElements.notificationsStatus.innerText = 'Disabled';
}
}
if(peerID < 0) { // not human
let isChannel = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID);
if(isChannel) {
appImManager.btnMute.classList.remove('tgico-mute', 'tgico-unmute');
appImManager.btnMute.classList.add(muted ? 'tgico-unmute' : 'tgico-mute');
appImManager.btnMute.style.display = '';
} else {
appImManager.btnMute.style.display = 'none';
}
} else {
appImManager.btnMute.style.display = 'none';
} }
appImManager.btnMenuMute.classList.remove('tgico-mute', 'tgico-unmute'); appImManager.setMutedState(muted);
appImManager.btnMenuMute.classList.add(muted ? 'tgico-unmute' : 'tgico-mute');
let rp = appImManager.btnMenuMute.firstElementChild;
appImManager.btnMenuMute.innerText = muted ? 'Unmute' : 'Mute';
appImManager.btnMenuMute.appendChild(rp);
} }
//this.loadSidebarMedia(); //this.loadSidebarMedia();

2
src/lib/appManagers/appUsersManager.ts

@ -28,7 +28,7 @@ export class AppUsersManager {
$rootScope.$on('apiUpdate', (e: CustomEvent) => { $rootScope.$on('apiUpdate', (e: CustomEvent) => {
let update = e.detail; let update = e.detail;
console.log('on apiUpdate', update); //console.log('on apiUpdate', update);
switch(update._) { switch(update._) {
case 'updateUserStatus': case 'updateUserStatus':
var userID = update.user_id; var userID = update.user_id;

22
src/lib/lottieLoader.ts

@ -2,18 +2,19 @@ import LottiePlayer, { AnimationConfigWithPath, AnimationConfigWithData, Animati
import { isElementInViewport, isInDOM } from "./utils"; import { isElementInViewport, isInDOM } from "./utils";
class LottieLoader { class LottieLoader {
private lottie: /* any */ typeof LottiePlayer = null; public lottie: /* any */ typeof LottiePlayer = null;
private animations: { private animations: {
[group: string]: { [group: string]: {
animation: AnimationItem, animation: AnimationItem,
container: HTMLDivElement, container: HTMLDivElement,
paused: boolean, paused: boolean,
autoplay: boolean autoplay: boolean,
canvas: boolean
}[] }[]
} = {}; } = {};
private debug = false; private debug = false;
public checkAnimations(blurred?: boolean, group?: string) { public checkAnimations(blurred?: boolean, group?: string, destroy = false) {
let groups = group ? [group] : Object.keys(this.animations); let groups = group ? [group] : Object.keys(this.animations);
if(group && !this.animations[group]) { if(group && !this.animations[group]) {
@ -27,9 +28,17 @@ class LottieLoader {
let length = animations.length; let length = animations.length;
for(let i = length - 1; i >= 0; --i) { for(let i = length - 1; i >= 0; --i) {
let {animation, container, paused, autoplay} = animations[i]; let {animation, container, paused, autoplay, canvas} = animations[i];
if(!isInDOM(container)) { if(canvas && isElementInViewport(container)) {
let c = container.firstElementChild as HTMLCanvasElement;
if(!c.height && !c.width) {
console.log('lottie need resize');
animation.resize();
}
}
if(destroy && !isInDOM(container)) {
this.debug && console.log('destroy animation'); this.debug && console.log('destroy animation');
animation.destroy(); animation.destroy();
animations.splice(i, 1); animations.splice(i, 1);
@ -82,7 +91,8 @@ class LottieLoader {
animation, animation,
container: params.container as HTMLDivElement, container: params.container as HTMLDivElement,
paused: !params.autoplay, paused: !params.autoplay,
autoplay: params.autoplay autoplay: params.autoplay,
canvas: params.renderer == 'canvas'
}); });
if(params.autoplay) { if(params.autoplay) {

4
src/lib/mtproto/networker.ts

@ -291,6 +291,10 @@ class MTPNetworker {
options.resultType = serializer.storeMethod(method, params); options.resultType = serializer.storeMethod(method, params);
if(method == 'account.updateNotifySettings') {
this.log('api call body:', serializer.getBytes(true));
}
var messageID = timeManager.generateID(); var messageID = timeManager.generateID();
var seqNo = this.generateSeqNo(); var seqNo = this.generateSeqNo();
var message = { var message = {

8
src/lib/tl_utils.ts

@ -242,6 +242,7 @@ class TLSerialization {
var i, condType; var i, condType;
var fieldBit; var fieldBit;
var len = methodData.params.length; var len = methodData.params.length;
//console.log('storeMethod', len, methodData);
for(i = 0; i < len; i++) { for(i = 0; i < len; i++) {
param = methodData.params[i]; param = methodData.params[i];
type = param.type; type = param.type;
@ -339,18 +340,22 @@ class TLSerialization {
var condType; var condType;
var fieldBit; var fieldBit;
var len = constructorData.params.length; var len = constructorData.params.length;
//console.log('storeObject', len, constructorData);
for(i = 0; i < len; i++) { for(i = 0; i < len; i++) {
param = constructorData.params[i]; param = constructorData.params[i];
type = param.type; type = param.type;
//console.log('storeObject', param, type);
if(type.indexOf('?') !== -1) { if(type.indexOf('?') !== -1) {
condType = type.split('?'); condType = type.split('?');
fieldBit = condType[0].split('.'); fieldBit = condType[0].split('.');
//console.log('storeObject fieldBit', fieldBit, obj[fieldBit[0]]);
if(!(obj[fieldBit[0]] & (1 << +fieldBit[1]))) { if(!(obj[fieldBit[0]] & (1 << +fieldBit[1]))) {
continue; continue;
} }
type = condType[1]; type = condType[1];
} }
//console.log('storeObject', param, type);
this.storeObject(obj[param.name], type, field + '[' + predicate + '][' + param.name + ']'); this.storeObject(obj[param.name], type, field + '[' + predicate + '][' + param.name + ']');
} }
@ -707,9 +712,12 @@ class TLDeserialization {
fieldBit = condType[0].split('.'); fieldBit = condType[0].split('.');
if(!(result[fieldBit[0]] & (1 << fieldBit[1]))) { if(!(result[fieldBit[0]] & (1 << fieldBit[1]))) {
//console.log('fetchObject bad', constructorData, result[fieldBit[0]], fieldBit);
continue; continue;
} }
//console.log('fetchObject good', constructorData, result[fieldBit[0]], fieldBit);
type = condType[1]; type = condType[1];
} }

29
src/lib/utils.js

@ -359,6 +359,35 @@ export function isElementInViewport(el) {
return elements.find(e => el.contains(e) || el.parentElement == e) !== undefined; return elements.find(e => el.contains(e) || el.parentElement == e) !== undefined;
} }
export function isScrolledIntoView(el) {
var rect = el.getBoundingClientRect();
var elemTop = rect.top;
var elemBottom = rect.bottom;
// Only completely visible elements return true:
//var isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);
// Partially visible elements return true:
var isVisible = elemTop < window.innerHeight && elemBottom >= 0;
return isVisible;
}
/* export function isScrolledIntoView(el) {
var rect = el.getBoundingClientRect(), top = rect.top, height = rect.height,
el = el.parentNode
// Check if bottom of the element is off the page
if (rect.bottom < 0) return false
// Check its within the document viewport
if (top > document.documentElement.clientHeight) return false
do {
rect = el.getBoundingClientRect()
if (top <= rect.bottom === false) return false
// Check if the element is out of view due to a container scrolling
if ((top + height) <= rect.top) return false
el = el.parentNode
} while (el != document.body)
return true
}; */
export function whichChild(elem/* : Node */) { export function whichChild(elem/* : Node */) {
let i = 0; let i = 0;
// @ts-ignore // @ts-ignore

56
src/scss/partials/_chatlist.scss

@ -42,49 +42,61 @@
ul { ul {
margin: 0; margin: 0;
padding: 0 .5rem; //padding: 0 .5rem;
display: grid;
grid-auto-columns: 1fr;
/* grid-gap: 4px; */
} }
#dialogs-pinned { li {
border-bottom: 1px solid #DADCE0; padding: 2px 0;
overflow: hidden;
&:empty { background-color: #fff;
display: none;
}
} }
li { li > .rp {
padding: 0 .5rem;
height: 70px; height: 70px;
max-height: 70px; max-height: 70px;
overflow: hidden;
border-radius: $border-radius; border-radius: $border-radius;
background-color: #fff;
display: grid; display: grid;
align-items: center; align-items: center;
grid-template-columns: 60px calc(100% - 60px); grid-template-columns: 60px calc(100% - 60px);
position: relative; position: relative;
/* grid-template-columns: 60px calc(85% - 60px) 15%; */
cursor: pointer; cursor: pointer;
padding: 0 .5rem;
margin: 0 .5rem;
overflow: hidden;
&:hover { &:hover {
background: rgba(112, 117, 121, .08); background: rgba(112, 117, 121, .08);
} }
}
&.active { li.active > .rp {
background: darken(rgba(112, 117, 121, .1), .8); background: darken(rgba(112, 117, 121, .1), .8);
} }
/* &.dialog-pinned + li:not(.dialog-pinned) { .pinned-delimiter {
display: flex;
padding: 8px 0 4px;
} */ span {
margin: 0;
width: 100%;
height: 1px;
background-color: #DADCE0;
}
} }
/* li.dialog-pinned + .pinned-delimiter {
display: flex;
} */
p { p {
margin: 0; margin: 0;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center;
height: 1.7rem; // hot-fix height: 1.7rem; // hot-fix
span:not(.tgico-pinnedchat):not(.emoji):last-child { span:not(.tgico-pinnedchat):not(.emoji):last-child {
@ -117,9 +129,6 @@
} }
.user-last-message { .user-last-message {
/* display: block; */
/* display: flex;
align-items: center; */
max-width: 80%; max-width: 80%;
i { i {
@ -144,15 +153,6 @@
} }
} }
/* .dialog-details {
text-align: right;
:first-child {
font-size: .9rem;
display: block;
}
} */
.unread, .unread-muted { .unread, .unread-muted {
border-radius: 12px; border-radius: 12px;
min-width: 24px; min-width: 24px;

10
src/scss/partials/_sidebar.scss

@ -121,12 +121,21 @@
} */ } */
#content-media { #content-media {
width: 100%;
display: flex;
flex-direction: column;
> div {
display: grid; display: grid;
grid-template-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr;
grid-auto-rows: max-content; grid-auto-rows: max-content;
grid-gap: 3.5px; grid-gap: 3.5px;
place-items: start; place-items: start;
& + div {
margin-top: 3.5px;
}
> div { > div {
width: 100%; width: 100%;
cursor: pointer; cursor: pointer;
@ -144,6 +153,7 @@
} }
} }
} }
}
#content-docs { #content-docs {
padding: 15px; padding: 15px;

3
src/scss/style.scss

@ -144,7 +144,7 @@ input {
position: absolute; position: absolute;
background: #fff; background: #fff;
box-shadow: 0 5px 8px 1px rgba(0,0,0,.24); box-shadow: 0 5px 8px 1px rgba(0,0,0,.24);
z-index: 1; z-index: 3;
top: 100%; top: 100%;
margin-top: 8px; margin-top: 8px;
padding: 9px 0; padding: 9px 0;
@ -1301,6 +1301,7 @@ div.scrollable::-webkit-scrollbar-thumb {
&.active { &.active {
display: flex; display: flex;
flex-direction: column;
} }
> div { > div {

Loading…
Cancel
Save