Browse Source

webp polyfill & archived dialogs & improved lazy load

master
Eduard Kuzmenko 5 years ago
parent
commit
d93613b6f2
  1. BIN
      .DS_Store
  2. 2
      src/components/emoticonsDropdown.ts
  3. 105
      src/components/lazyLoadQueue.ts
  4. 9
      src/components/pageAuthCode.ts
  5. 7
      src/components/pageIm.ts
  6. 10
      src/components/pagePassword.ts
  7. 14
      src/components/pageSignUp.ts
  8. 4
      src/components/scrollable.ts
  9. 8
      src/components/wrappers.ts
  10. 38
      src/lib/appManagers/appDialogsManager.ts
  11. 5
      src/lib/appManagers/appImManager.ts
  12. 64
      src/lib/appManagers/appMessagesManager.ts
  13. 48
      src/lib/appManagers/appSidebarLeft.ts
  14. 10
      src/lib/appManagers/appSidebarRight.ts
  15. 72
      src/lib/appManagers/appWebpManager.ts
  16. 4
      src/lib/mtproto/apiFileManager.ts
  17. 17
      src/scss/partials/_chat.scss
  18. 6
      src/scss/partials/_chatlist.scss
  19. 5
      src/scss/style.scss
  20. 1
      webp-hero.bundle.js

BIN
.DS_Store vendored

Binary file not shown.

2
src/components/emoticonsDropdown.ts

@ -19,7 +19,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
let dropdown = pageEl.querySelector('.emoji-dropdown') as HTMLDivElement; let dropdown = pageEl.querySelector('.emoji-dropdown') as HTMLDivElement;
dropdown.classList.add('active'); // need dropdown.classList.add('active'); // need
let lazyLoadQueue = new LazyLoadQueue(); let lazyLoadQueue = new LazyLoadQueue(5);
let container = pageEl.querySelector('.emoji-container .tabs-container') as HTMLDivElement; let container = pageEl.querySelector('.emoji-container .tabs-container') as HTMLDivElement;
let tabs = pageEl.querySelector('.emoji-dropdown .emoji-tabs') as HTMLUListElement; let tabs = pageEl.querySelector('.emoji-dropdown .emoji-tabs') as HTMLUListElement;

105
src/components/lazyLoadQueue.ts

@ -1,10 +1,85 @@
import { isElementInViewport } from "../lib/utils"; import { isElementInViewport } from "../lib/utils";
export default class LazyLoadQueue { export default class LazyLoadQueue {
private lazyLoadMedia: Array<{div: HTMLDivElement, load: () => Promise<void>}> = []; private lazyLoadMedia: Array<{div: HTMLDivElement, load: () => Promise<void>, wasSeen?: boolean}> = [];
private loadingMedia = 0;
private tempID = 0;
constructor(private parallelLimit = 0) {
}
public clear() {
this.tempID--;
this.lazyLoadMedia.length = 0;
this.loadingMedia = 0;
}
public async processQueue(id?: number) {
if(this.parallelLimit > 0 && this.loadingMedia >= this.parallelLimit) return;
let item: {div: HTMLDivElement, load: () => Promise<void>, wasSeen?: boolean};
let index: number;
/* if(id) item = this.lazyLoadMedia.splice(id, 1) as any;
else item = this.lazyLoadMedia.pop(); */
if(id !== undefined) item = this.lazyLoadMedia.splice(id, 1)[0];
else {
index = this.lazyLoadMedia.findIndex(i => isElementInViewport(i.div));
if(index !== -1) {
item = this.lazyLoadMedia.splice(index, 1)[0];
} else {
//index = this.lazyLoadMedia.findIndex(i => i.wasSeen);
//if(index !== -1) {
//item = this.lazyLoadMedia.splice(index, 1)[0];
/*} else {
item = this.lazyLoadMedia.pop();
} */
let length = this.lazyLoadMedia.length;
for(index = length - 1; index >= 0; --index) {
if(this.lazyLoadMedia[index].wasSeen) {
item = this.lazyLoadMedia.splice(index, 1)[0];
break;
}
}
}
}
if(item) {
this.loadingMedia++;
let tempID = this.tempID;
try {
await item.load();
} catch(err) {
console.error('loadMediaQueue error:', err, item, id, index);
}
if(tempID == this.tempID) {
this.loadingMedia--;
}
if(this.lazyLoadMedia.length) {
this.processQueue();
}
}
}
public check(id?: number) { public check(id?: number) {
/* let length = this.lazyLoadMedia.length; /* if(id !== undefined) {
let {div, load} = this.lazyLoadMedia[id];
if(isElementInViewport(div)) {
//console.log('will load div by id:', div, div.getBoundingClientRect());
load();
this.lazyLoadMedia.splice(id, 1);
}
return;
}
let length = this.lazyLoadMedia.length;
for(let i = length - 1; i >= 0; --i) { for(let i = length - 1; i >= 0; --i) {
let {div, load} = this.lazyLoadMedia[i]; let {div, load} = this.lazyLoadMedia[i];
@ -14,18 +89,31 @@ export default class LazyLoadQueue {
this.lazyLoadMedia.splice(i, 1); this.lazyLoadMedia.splice(i, 1);
} }
} */ } */
if(id !== undefined) { if(id !== undefined) {
let {div, load} = this.lazyLoadMedia[id]; let {div} = this.lazyLoadMedia[id];
if(isElementInViewport(div)) { if(isElementInViewport(div)) {
//console.log('will load div by id:', div, div.getBoundingClientRect()); //console.log('will load div by id:', div, div.getBoundingClientRect());
load(); this.lazyLoadMedia[id].wasSeen = true;
this.lazyLoadMedia.splice(id, 1); this.processQueue(id);
} }
return; return;
} }
this.lazyLoadMedia = this.lazyLoadMedia.filter(({div, load}) => { let length = this.lazyLoadMedia.length;
for(let i = length - 1; i >= 0; --i) {
let {div} = this.lazyLoadMedia[i];
if(isElementInViewport(div)) {
console.log('will load div:', div);
this.lazyLoadMedia[i].wasSeen = true;
this.processQueue(i);
//this.lazyLoadMedia.splice(i, 1);
}
}
/* this.lazyLoadMedia = this.lazyLoadMedia.filter(({div, load}) => {
if(isElementInViewport(div)) { if(isElementInViewport(div)) {
//console.log('will load div:', div, div.getBoundingClientRect()); //console.log('will load div:', div, div.getBoundingClientRect());
load(); load();
@ -33,10 +121,11 @@ export default class LazyLoadQueue {
} }
return true; return true;
}); }); */
} }
public push(el: {div: HTMLDivElement, load: () => Promise<void>}) { public push(el: {div: HTMLDivElement, load: () => Promise<void>, wasSeen?: boolean}) {
el.wasSeen = false;
let id = this.lazyLoadMedia.push(el) - 1; let id = this.lazyLoadMedia.push(el) - 1;
this.check(id); this.check(id);

9
src/components/pageAuthCode.ts

@ -1,11 +1,10 @@
import {MTProto} from '../lib/mtproto/mtproto';
import pageSignIn from './pageSignIn'; import pageSignIn from './pageSignIn';
import pageSignUp from './pageSignUp'; import pageSignUp from './pageSignUp';
import pageIm from './pageIm'; import pageIm from './pageIm';
import pagePassword from './pagePassword'; import pagePassword from './pagePassword';
import CryptoWorker from '../lib/crypto/cryptoworker'; import CryptoWorker from '../lib/crypto/cryptoworker';
import LottieLoader from '../lib/lottieLoader'; import LottieLoader from '../lib/lottieLoader';
import apiManager from '../lib/mtproto/apiManager';
let installed = false; let installed = false;
let authCode: { let authCode: {
@ -102,7 +101,7 @@ export default async(_authCode: typeof authCode) => {
codeInput.setAttribute('disabled', 'true'); codeInput.setAttribute('disabled', 'true');
changePhonePromise = MTProto.apiManager.invokeApi('auth.sendCode', { changePhonePromise = apiManager.invokeApi('auth.sendCode', {
/* flags: 0, */ /* flags: 0, */
phone_number: phone_number, phone_number: phone_number,
api_id: Config.App.id, api_id: Config.App.id,
@ -179,13 +178,13 @@ export default async(_authCode: typeof authCode) => {
console.log('invoking auth.signIn with params:', params); console.log('invoking auth.signIn with params:', params);
MTProto.apiManager.invokeApi('auth.signIn', params) apiManager.invokeApi('auth.signIn', params)
.then((response: any) => { .then((response: any) => {
console.log('auth.signIn response:', response); console.log('auth.signIn response:', response);
switch(response._) { switch(response._) {
case 'auth.authorization': case 'auth.authorization':
MTProto.apiManager.setUserAuth({ apiManager.setUserAuth({
id: response.user.id id: response.user.id
}); });

7
src/components/pageIm.ts

@ -7,7 +7,10 @@ import lottieLoader from "../lib/lottieLoader";
import appSidebarLeft from "../lib/appManagers/appSidebarLeft"; import appSidebarLeft from "../lib/appManagers/appSidebarLeft";
/* (window as any).libraryLoaded = function(lol: any) {
// @ts-ignore
console.log('libraryLoaded', lol, this, window.webpMachine);
} */
export default () => import('../lib/services').then(services => { export default () => import('../lib/services').then(services => {
console.log('included services', services); console.log('included services', services);
@ -193,5 +196,7 @@ 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);
appSidebarLeft.loadDialogs(true);
}); });
}); });

10
src/components/pagePassword.ts

@ -1,10 +1,10 @@
import {MTProto} from '../lib/mtproto/mtproto';
import pageIm from './pageIm'; import pageIm from './pageIm';
import CryptoWorker from '../lib/crypto/cryptoworker'; import CryptoWorker from '../lib/crypto/cryptoworker';
import { putPreloader } from './misc'; import { putPreloader } from './misc';
import LottieLoader from '../lib/lottieLoader'; import LottieLoader from '../lib/lottieLoader';
import passwordManager from '../lib/mtproto/passwordManager';
import apiManager from '../lib/mtproto/apiManager';
let installed = false; let installed = false;
@ -97,15 +97,15 @@ export default async() => {
this.textContent = 'PLEASE WAIT...'; this.textContent = 'PLEASE WAIT...';
putPreloader(this); putPreloader(this);
MTProto.passwordManager.getState() passwordManager.getState()
.then(state => { .then(state => {
console.log(state); console.log(state);
MTProto.passwordManager.check(state, value).then((response: any) => { passwordManager.check(state, value).then((response: any) => {
console.log('passwordManager response:', response); console.log('passwordManager response:', response);
switch(response._) { switch(response._) {
case 'auth.authorization': case 'auth.authorization':
MTProto.apiManager.setUserAuth({ apiManager.setUserAuth({
id: response.user.id id: response.user.id
}); });

14
src/components/pageSignUp.ts

@ -1,4 +1,3 @@
import {MTProto} from '../lib/mtproto/mtproto';
import {putPreloader} from './misc'; import {putPreloader} from './misc';
let installed = false; let installed = false;
@ -9,6 +8,7 @@ let authCode: {
import resizeableImage from '../lib/cropper'; import resizeableImage from '../lib/cropper';
import pageIm from './pageIm'; import pageIm from './pageIm';
import apiManager from '../lib/mtproto/apiManager';
export default (_authCode: typeof authCode) => { export default (_authCode: typeof authCode) => {
authCode = _authCode; authCode = _authCode;
@ -75,10 +75,10 @@ export default (_authCode: typeof authCode) => {
/* console.log(file, typeof(file)); */ /* console.log(file, typeof(file)); */
// @ts-ignore // @ts-ignore
/* MTProto.apiFileManager.uploadFile(file).then(function(inputFile) { /* apiFileManager.uploadFile(file).then(function(inputFile) {
console.log('uploaded smthn', inputFile); console.log('uploaded smthn', inputFile);
MTProto.apiManager.invokeApi('photos.uploadProfilePhoto', { apiManager.invokeApi('photos.uploadProfilePhoto', {
file: inputFile file: inputFile
}).then(function (updateResult) { }).then(function (updateResult) {
console.log('updated photo!'); console.log('updated photo!');
@ -137,10 +137,10 @@ export default (_authCode: typeof authCode) => {
console.log('invoking uploadFile...'); console.log('invoking uploadFile...');
// @ts-ignore // @ts-ignore
MTProto.apiFileManager.uploadFile(avatarBlob).then((inputFile: any) => { apiFileManager.uploadFile(avatarBlob).then((inputFile: any) => {
console.log('uploaded smthn', inputFile); console.log('uploaded smthn', inputFile);
MTProto.apiManager.invokeApi('photos.uploadProfilePhoto', { apiManager.invokeApi('photos.uploadProfilePhoto', {
file: inputFile file: inputFile
}).then((updateResult) => { }).then((updateResult) => {
console.log('updated photo!'); console.log('updated photo!');
@ -179,13 +179,13 @@ export default (_authCode: typeof authCode) => {
this.textContent = 'PLEASE WAIT...'; this.textContent = 'PLEASE WAIT...';
putPreloader(this); putPreloader(this);
MTProto.apiManager.invokeApi('auth.signUp', params) apiManager.invokeApi('auth.signUp', params)
.then((response: any) => { .then((response: any) => {
console.log('auth.signUp response:', response); console.log('auth.signUp response:', response);
switch(response._) { switch(response._) {
case 'auth.authorization': // success case 'auth.authorization': // success
MTProto.apiManager.setUserAuth({ // warning apiManager.setUserAuth({ // warning
id: response.user.id id: response.user.id
}); });

4
src/components/scrollable.ts

@ -129,7 +129,7 @@ export default class Scrollable {
//console.log('onresize', thumb.style[type], thumbHeight, height); //console.log('onresize', thumb.style[type], thumbHeight, height);
} }
public setVirtualContainer(el: HTMLElement) { public setVirtualContainer(el?: HTMLElement) {
this.splitUp = el; this.splitUp = el;
this.hiddenElements.up.length = this.hiddenElements.down.length = 0; this.hiddenElements.up.length = this.hiddenElements.down.length = 0;
@ -143,9 +143,11 @@ export default class Scrollable {
//this.topObserver.observe(this.paddingTopDiv); //this.topObserver.observe(this.paddingTopDiv);
//this.bottomObserver.observe(this.paddingBottomDiv); //this.bottomObserver.observe(this.paddingBottomDiv);
if(el) {
el.parentElement.insertBefore(this.paddingTopDiv, el); el.parentElement.insertBefore(this.paddingTopDiv, el);
el.parentNode.insertBefore(this.paddingBottomDiv, el.nextSibling); el.parentNode.insertBefore(this.paddingBottomDiv, el.nextSibling);
} }
}
public onScroll() { public onScroll() {
// @ts-ignore // @ts-ignore

8
src/components/wrappers.ts

@ -8,6 +8,7 @@ import { formatBytes } from "../lib/utils";
import ProgressivePreloader from './preloader'; import ProgressivePreloader from './preloader';
import LazyLoadQueue from './lazyLoadQueue'; import LazyLoadQueue from './lazyLoadQueue';
import apiFileManager from '../lib/mtproto/apiFileManager'; import apiFileManager from '../lib/mtproto/apiFileManager';
import appWebpManager from '../lib/appManagers/appWebpManager';
export type MTDocument = { export type MTDocument = {
_: 'document', _: 'document',
@ -224,7 +225,7 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
console.error('wrong doc for wrapSticker!', doc, div); console.error('wrong doc for wrapSticker!', doc, div);
} }
//console.log('wrap sticker', doc); console.log('wrap sticker', doc);
if(doc.thumbs && !div.firstElementChild) { if(doc.thumbs && !div.firstElementChild) {
let thumb = doc.thumbs[0]; let thumb = doc.thumbs[0];
@ -308,7 +309,10 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
reader.readAsArrayBuffer(blob); reader.readAsArrayBuffer(blob);
} else if(stickerType == 1) { } else if(stickerType == 1) {
let img = new Image(); let img = new Image();
img.src = URL.createObjectURL(blob);
appWebpManager.polyfillImage(img, blob);
//img.src = URL.createObjectURL(blob);
/* div.style.height = doc.h + 'px'; /* div.style.height = doc.h + 'px';
div.style.width = doc.w + 'px'; */ div.style.width = doc.w + 'px'; */

38
src/lib/appManagers/appDialogsManager.ts

@ -25,6 +25,7 @@ export class AppDialogsManager {
public chatListArchived = document.getElementById('dialogs-archived') as HTMLUListElement; public chatListArchived = document.getElementById('dialogs-archived') as HTMLUListElement;
public pinnedDelimiter: HTMLDivElement; public pinnedDelimiter: HTMLDivElement;
public chatsHidden: any; public chatsHidden: any;
public chatsArchivedHidden: any;
public myID = 0; public myID = 0;
public doms: {[peerID: number]: DialogDom} = {}; public doms: {[peerID: number]: DialogDom} = {};
@ -48,6 +49,7 @@ export class AppDialogsManager {
//let chatClosedDiv = document.getElementById('chat-closed'); //let chatClosedDiv = document.getElementById('chat-closed');
this.setListClickListener(this.chatList); this.setListClickListener(this.chatList);
this.setListClickListener(this.chatListArchived);
} }
public setListClickListener(list: HTMLUListElement, onFound?: () => void) { public setListClickListener(list: HTMLUListElement, onFound?: () => void) {
@ -152,12 +154,16 @@ export class AppDialogsManager {
public sortDom(archived = false) { public sortDom(archived = false) {
//return; //return;
let dialogs = appMessagesManager.dialogsStorage.dialogs; let dialogs = appMessagesManager.dialogsStorage.dialogs.slice();
let inUpper: HTMLLIElement[] = []; let inUpper: HTMLLIElement[] = [];
let inBottom: HTMLLIElement[] = []; let inBottom: HTMLLIElement[] = [];
let pinnedDialogs = []; let pinnedDialogs = [];
let sorted = dialogs;
if(!archived) {
for(let i = 0; i < dialogs.length; ++i) { for(let i = 0; i < dialogs.length; ++i) {
let dialog = dialogs[i]; let dialog = dialogs[i];
if(!dialog.pFlags.pinned) break; if(!dialog.pFlags.pinned) break;
@ -175,23 +181,31 @@ export class AppDialogsManager {
} }
} }
let sorted = dialogs sorted = sorted.filter((d: any) => !d.pFlags.pinned && d.folder_id != 1);
.filter((d: any) => !d.pFlags.pinned) } else {
.sort((a: any, b: any) => { sorted = sorted.filter((d: any) => d.folder_id == 1);
}
sorted = sorted.sort((a: any, b: any) => {
let timeA = appMessagesManager.getMessage(a.top_message).date; let timeA = appMessagesManager.getMessage(a.top_message).date;
let timeB = appMessagesManager.getMessage(b.top_message).date; let timeB = appMessagesManager.getMessage(b.top_message).date;
return timeB - timeA; return timeB - timeA;
}); });
if(!archived) {
sorted = pinnedDialogs.concat(sorted); sorted = pinnedDialogs.concat(sorted);
}
//console.log('sortDom', sorted, this.chatsHidden, this.chatsHidden.up, this.chatsHidden.down); //console.log('sortDom', sorted, this.chatsHidden, this.chatsHidden.up, this.chatsHidden.down);
let hiddenLength: number = this.chatsHidden.up.length; let chatList = archived ? this.chatListArchived : this.chatList;
let inViewportLength = this.chatList.childElementCount; let chatsHidden = archived ? this.chatsArchivedHidden : this.chatsHidden;
let hiddenLength: number = chatsHidden.up.length;
let inViewportLength = chatList.childElementCount;
this.chatList.innerHTML = ''; chatList.innerHTML = '';
let inViewportIndex = 0; let inViewportIndex = 0;
sorted.forEach((d: any, idx) => { sorted.forEach((d: any, idx) => {
@ -221,10 +235,10 @@ export class AppDialogsManager {
//this.chatList.append(dom.listEl); //this.chatList.append(dom.listEl);
}); });
console.log('sortDom', sorted.length, inUpper.length, this.chatList.childElementCount, inBottom.length); console.log('sortDom', sorted.length, inUpper.length, chatList.childElementCount, inBottom.length);
this.chatsHidden.up = inUpper; chatsHidden.up = inUpper;
this.chatsHidden.down = inBottom; chatsHidden.down = inBottom;
} }
public setLastMessage(dialog: any, lastMessage?: any, dom?: DialogDom) { public setLastMessage(dialog: any, lastMessage?: any, dom?: DialogDom) {
@ -398,7 +412,7 @@ export class AppDialogsManager {
}, container?: HTMLUListElement, drawStatus = true) { }, container?: HTMLUListElement, drawStatus = true) {
let peerID: number = dialog.peerID; let peerID: number = dialog.peerID;
if((peerID in this.doms) && !container) return; if((this.doms[peerID] || this.domsArchived[peerID]) && !container) return;
let title = appPeersManager.getPeerTitle(peerID); let title = appPeersManager.getPeerTitle(peerID);
@ -497,7 +511,7 @@ export class AppDialogsManager {
}; };
if(!container) { if(!container) {
if(dialog.folder_id) { if(dialog.folder_id && dialog.folder_id == 1) {
this.chatListArchived.append(li); this.chatListArchived.append(li);
this.domsArchived[dialog.peerID] = dom; this.domsArchived[dialog.peerID] = dom;
} else { } else {

5
src/lib/appManagers/appImManager.ts

@ -931,6 +931,8 @@ export class AppImManager {
if(item) { if(item) {
this.loadingMedia++; this.loadingMedia++;
let peerID = this.peerID;
let promise = item(); let promise = item();
try { try {
await promise; await promise;
@ -938,8 +940,10 @@ export class AppImManager {
this.log.error('loadMediaQueue error:', err); this.log.error('loadMediaQueue error:', err);
} }
if(peerID == this.peerID) {
this.loadingMedia--; this.loadingMedia--;
} }
}
if(this.loadMediaQueue.length) return this.loadMediaQueueProcess(); if(this.loadMediaQueue.length) return this.loadMediaQueueProcess();
} }
@ -1179,6 +1183,7 @@ export class AppImManager {
this.unreaded = []; this.unreaded = [];
this.unreadOut = []; this.unreadOut = [];
this.loadMediaQueue = []; this.loadMediaQueue = [];
this.loadingMedia = 0;
lottieLoader.checkAnimations(false, 'chat', true); lottieLoader.checkAnimations(false, 'chat', true);

64
src/lib/appManagers/appMessagesManager.ts

@ -60,7 +60,7 @@ export class AppMessagesManager {
public maxSeenID = 0; public maxSeenID = 0;
public allDialogsLoaded = false; public allDialogsLoaded: {[folder_id: number]: boolean} = {};
public dialogsOffsetDate = 0; public dialogsOffsetDate = 0;
public pinnedIndex = 0; public pinnedIndex = 0;
public dialogsNum = 0; public dialogsNum = 0;
@ -705,11 +705,18 @@ export class AppMessagesManager {
}; };
} }
public getConversations(query?: string, offsetIndex?: number, limit = 20) { public getConversations(query?: string, offsetIndex?: number, limit = 20, folderID = -1) {
var curDialogStorage = this.dialogsStorage; //var curDialogStorage = this.dialogsStorage;
var isSearch = typeof(query) == 'string' && query.length; //var isSearch = typeof(query) == 'string' && query.length;
let curDialogStorage = this.dialogsStorage.dialogs;
if(isSearch) { if(folderID > 0) {
curDialogStorage = curDialogStorage.filter(d => d.folder_id == folderID);
} else {
curDialogStorage = curDialogStorage.filter(d => d.folder_id != 1);
}
/* if(isSearch) {
if(!limit || this.cachedResults.query !== query) { if(!limit || this.cachedResults.query !== query) {
this.cachedResults.query = query; this.cachedResults.query = query;
@ -724,30 +731,38 @@ export class AppMessagesManager {
this.cachedResults.count = this.cachedResults.dialogs.length; this.cachedResults.count = this.cachedResults.dialogs.length;
} }
curDialogStorage = this.cachedResults; curDialogStorage = this.cachedResults;
} else { } else { */
this.cachedResults.query = false; this.cachedResults.query = false;
} //}
var offset = 0 var offset = 0;
if(offsetIndex > 0) { if(offsetIndex > 0) {
for(offset = 0; offset < curDialogStorage.dialogs.length; offset++) { for(; offset < curDialogStorage.length; offset++) {
if(offsetIndex > curDialogStorage.dialogs[offset].index) { if(offsetIndex > curDialogStorage[offset].index) {
break; break;
} }
} }
} }
if(isSearch || this.allDialogsLoaded || curDialogStorage.dialogs.length >= offset + limit) { if(/* isSearch || */this.allDialogsLoaded[folderID] || curDialogStorage.length >= offset + limit) {
return Promise.resolve({ return Promise.resolve({
dialogs: curDialogStorage.dialogs.slice(offset, offset + limit) dialogs: curDialogStorage.slice(offset, offset + limit)
}); });
} }
return this.getTopMessages(limit).then(() => { return this.getTopMessages(limit, folderID).then(() => {
offset = 0 let curDialogStorage = this.dialogsStorage.dialogs;
if(folderID > 0) {
curDialogStorage = curDialogStorage.filter(d => d.folder_id == folderID);
} else {
curDialogStorage = curDialogStorage.filter(d => d.folder_id != 1);
}
offset = 0;
if(offsetIndex > 0) { if(offsetIndex > 0) {
for(offset = 0; offset < curDialogStorage.dialogs.length; offset++) { for(offset = 0; offset < curDialogStorage.length; offset++) {
if(offsetIndex > curDialogStorage.dialogs[offset].index) { if(offsetIndex > curDialogStorage[offset].index) {
break; break;
} }
} }
@ -756,12 +771,12 @@ export class AppMessagesManager {
//console.warn(offset, offset + limit, curDialogStorage.dialogs.length, this.dialogsStorage.dialogs.length); //console.warn(offset, offset + limit, curDialogStorage.dialogs.length, this.dialogsStorage.dialogs.length);
return { return {
dialogs: curDialogStorage.dialogs.slice(offset, offset + limit) dialogs: curDialogStorage.slice(offset, offset + limit)
}; };
}); });
} }
public getTopMessages(limit: number) { public getTopMessages(limit: number, folderID = -1) {
var dialogs = this.dialogsStorage.dialogs; var dialogs = this.dialogsStorage.dialogs;
var offsetDate = 0; var offsetDate = 0;
var offsetID = 0; var offsetID = 0;
@ -775,12 +790,16 @@ export class AppMessagesManager {
flags |= 1; flags |= 1;
} }
if(folderID > 0) {
flags |= 1;
flags |= 2;
}
let hash = 0; let hash = 0;
/* let id = 296814355;
hash = (((hash * 0x4F25) & 0x7FFFFFFF) + id) & 0x7FFFFFFF; */
return apiManager.invokeApi('messages.getDialogs', { return apiManager.invokeApi('messages.getDialogs', {
flags: flags, flags: flags,
folder_id: folderID,
offset_date: offsetDate, offset_date: offsetDate,
offset_id: appMessagesIDsManager.getMessageLocalID(offsetID), offset_id: appMessagesIDsManager.getMessageLocalID(offsetID),
offset_peer: AppPeersManager.getInputPeerByID(offsetPeerID), offset_peer: AppPeersManager.getInputPeerByID(offsetPeerID),
@ -789,6 +808,8 @@ export class AppMessagesManager {
}, { }, {
timeout: 300 timeout: 300
}).then((dialogsResult: any) => { }).then((dialogsResult: any) => {
console.log('messages.getDialogs result:', dialogsResult);
if(!offsetDate) { if(!offsetDate) {
telegramMeWebService.setAuthorized(true); telegramMeWebService.setAuthorized(true);
} }
@ -841,7 +862,7 @@ export class AppMessagesManager {
if(!dialogsResult.dialogs.length || if(!dialogsResult.dialogs.length ||
!dialogsResult.count || !dialogsResult.count ||
dialogs.length >= dialogsResult.count) { dialogs.length >= dialogsResult.count) {
this.allDialogsLoaded = true; this.allDialogsLoaded[folderID] = true;
} }
if(hasPrepend && !this.newDialogsHandlePromise) { if(hasPrepend && !this.newDialogsHandlePromise) {
@ -1313,6 +1334,7 @@ export class AppMessagesManager {
dialog.index = this.generateDialogIndex(topDate); dialog.index = this.generateDialogIndex(topDate);
dialog.peerID = peerID; dialog.peerID = peerID;
if(!dialog.folder_id) dialog.folder_id = 0;
this.pushDialogToStorage(dialog, offsetDate); this.pushDialogToStorage(dialog, offsetDate);

48
src/lib/appManagers/appSidebarLeft.ts

@ -52,6 +52,7 @@ class AppSidebarLeft {
private chatsArchivedContainer = document.getElementById('chats-archived-container') as HTMLDivElement; private chatsArchivedContainer = document.getElementById('chats-archived-container') as HTMLDivElement;
private chatsContainer = document.getElementById('chats-container') as HTMLDivElement; private chatsContainer = document.getElementById('chats-container') as HTMLDivElement;
private chatsArchivedOffsetIndex = 0;
private chatsOffsetIndex = 0; private chatsOffsetIndex = 0;
private chatsPreloader: HTMLDivElement; private chatsPreloader: HTMLDivElement;
private chatsLoadCount = 0; private chatsLoadCount = 0;
@ -71,6 +72,7 @@ class AppSidebarLeft {
private query = ''; private query = '';
public scroll: Scrollable = null; public scroll: Scrollable = null;
public scrollArchived: Scrollable = null;
public searchGroups: {[group: string]: SearchGroup} = { public searchGroups: {[group: string]: SearchGroup} = {
contacts: new SearchGroup('Contacts and Chats', 'contacts'), contacts: new SearchGroup('Contacts and Chats', 'contacts'),
@ -90,11 +92,14 @@ class AppSidebarLeft {
this.scroll = new Scrollable(this.chatsContainer as HTMLDivElement); this.scroll = new Scrollable(this.chatsContainer as HTMLDivElement);
this.scroll.setVirtualContainer(appDialogsManager.chatList); this.scroll.setVirtualContainer(appDialogsManager.chatList);
appDialogsManager.chatsHidden = this.scroll.hiddenElements; appDialogsManager.chatsHidden = this.scroll.hiddenElements;
this.scroll.container.addEventListener('scroll', this.onChatsScroll.bind(this)); this.scroll.container.addEventListener('scroll', this.onChatsScroll.bind(this));
this.listsContainer = new Scrollable(this.searchContainer).container; this.scrollArchived = new Scrollable(this.chatsArchivedContainer as HTMLDivElement);
this.scrollArchived.setVirtualContainer(appDialogsManager.chatListArchived);
appDialogsManager.chatsArchivedHidden = this.scrollArchived.hiddenElements;
this.scroll.container.addEventListener('scroll', this.onChatsArchivedScroll.bind(this));
this.listsContainer = new Scrollable(this.searchContainer).container;
for(let i in this.searchGroups) { for(let i in this.searchGroups) {
this.listsContainer.append(this.searchGroups[i].container); this.listsContainer.append(this.searchGroups[i].container);
} }
@ -200,7 +205,9 @@ class AppSidebarLeft {
setTimeout(() => { setTimeout(() => {
this.onSidebarScroll(); this.onSidebarScroll();
this.scroll.onScroll();
this.onChatsScroll(); this.onChatsScroll();
this.onChatsArchivedScroll();
}, 0); }, 0);
}); });
@ -209,26 +216,34 @@ class AppSidebarLeft {
}); */ }); */
} }
public async loadDialogs() { public async loadDialogs(archived = false) {
if(this.loadDialogsPromise/* || 1 == 1 */) return this.loadDialogsPromise; if(this.loadDialogsPromise/* || 1 == 1 */) return this.loadDialogsPromise;
this.chatsContainer.append(this.chatsPreloader); (archived ? this.chatsArchivedContainer : this.chatsContainer).append(this.chatsPreloader);
//let offset = appMessagesManager.generateDialogIndex();/* appMessagesManager.dialogsNum */; //let offset = appMessagesManager.generateDialogIndex();/* appMessagesManager.dialogsNum */;
let offset = archived ? this.chatsArchivedOffsetIndex : this.chatsOffsetIndex;
//let offset = 0;
try { try {
this.loadDialogsPromise = appMessagesManager.getConversations('', this.chatsOffsetIndex, this.chatsLoadCount); this.loadDialogsPromise = appMessagesManager.getConversations('', offset, this.chatsLoadCount, +archived);
let result = await this.loadDialogsPromise; let result = await this.loadDialogsPromise;
if(result && result.dialogs && result.dialogs.length) { if(result && result.dialogs && result.dialogs.length) {
this.chatsOffsetIndex = result.dialogs[result.dialogs.length - 1].index; let index = result.dialogs[result.dialogs.length - 1].index;
if(archived) this.chatsArchivedOffsetIndex = index;
else this.chatsOffsetIndex = index;
result.dialogs.forEach((dialog: any) => { result.dialogs.forEach((dialog: any) => {
appDialogsManager.addDialog(dialog); appDialogsManager.addDialog(dialog);
}); });
} }
this.log('loaded ' + this.chatsLoadCount + ' dialogs by offset:', this.chatsOffsetIndex, result, this.scroll.hiddenElements); this.log('loaded ' + this.chatsLoadCount + ' dialogs by offset:', offset, result, this.scroll.hiddenElements);
this.scroll.onScroll(); this.scroll.onScroll();
} catch(err) { } catch(err) {
this.log.error(err); this.log.error(err);
@ -239,7 +254,7 @@ class AppSidebarLeft {
} }
public onChatsScroll() { public onChatsScroll() {
//this.log(this.scroll); //this.log(this.scroll.hiddenElements.down.length, this.loadDialogsPromise, appDialogsManager.chatList.childNodes);
if(this.scroll.hiddenElements.down.length > 0/* || 1 == 1 */) return; if(this.scroll.hiddenElements.down.length > 0/* || 1 == 1 */) return;
if(!this.loadDialogsPromise) { if(!this.loadDialogsPromise) {
@ -255,6 +270,23 @@ class AppSidebarLeft {
} }
} }
public onChatsArchivedScroll() {
//this.log(this.scrollArchived.hiddenElements.down.length, this.loadDialogsPromise, appDialogsManager.chatListArchived.childNodes);
if(this.scrollArchived.hiddenElements.down.length > 0/* || 1 == 1 */) return;
if(!this.loadDialogsPromise) {
let d = Array.from(appDialogsManager.chatListArchived.childNodes).slice(-5);
for(let node of d) {
if(isElementInViewport(node)) {
this.loadDialogs(true);
break;
}
}
//console.log('last 5 dialogs:', d);
}
}
public onSidebarScroll() { public onSidebarScroll() {
if(!this.query.trim()) return; if(!this.query.trim()) return;

10
src/lib/appManagers/appSidebarRight.ts

@ -53,7 +53,7 @@ class AppSidebarRight {
public sharedMediaType: string = ''; public sharedMediaType: string = '';
private sharedMediaSelected: HTMLDivElement = null; private sharedMediaSelected: HTMLDivElement = null;
private lazyLoadQueueSidebar = new LazyLoadQueue(); private lazyLoadQueueSidebar = new LazyLoadQueue(5);
/* public minMediaID: { /* public minMediaID: {
[type: string]: number [type: string]: number
} = {}; */ } = {}; */
@ -144,7 +144,10 @@ class AppSidebarRight {
}); });
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
setTimeout(() => this.onSidebarScroll(), 0); setTimeout(() => {
this.sidebarScroll.onScroll();
this.onSidebarScroll();
}, 0);
}); });
} }
@ -334,6 +337,7 @@ class AppSidebarRight {
this.savedVirtualStates = {}; this.savedVirtualStates = {};
this.prevTabID = -1; this.prevTabID = -1;
this.sidebarScroll.setVirtualContainer(null);
(this.profileTabs.children[1] as HTMLLIElement).click(); // set media (this.profileTabs.children[1] as HTMLLIElement).click(); // set media
if(this.sharedMediaSelected) { if(this.sharedMediaSelected) {
@ -348,6 +352,8 @@ class AppSidebarRight {
this.profileElements.notificationsCheckbox.checked = true; this.profileElements.notificationsCheckbox.checked = true;
this.profileElements.notificationsStatus.innerText = 'Enabled'; this.profileElements.notificationsStatus.innerText = 'Enabled';
this.lazyLoadQueueSidebar.clear();
Object.keys(this.sharedMedia).forEach(key => { Object.keys(this.sharedMedia).forEach(key => {
this.sharedMedia[key].innerHTML = ''; this.sharedMedia[key].innerHTML = '';
}); });

72
src/lib/appManagers/appWebpManager.ts

@ -0,0 +1,72 @@
class AppWebpManager {
public webpMachine: any = null;
public loaded: Promise<void>;
public busyPromise: Promise<string>;
public queue: {bytes: Uint8Array, img: HTMLImageElement}[] = [];
constructor() {
this.loaded = new Promise((resolve, reject) => {
(window as any).webpLoaded = () => {
console.log('webpHero loaded');
this.webpMachine = new (window as any).webpHero.WebpMachine();
this.webpMachine.webp.Module.doNotCaptureKeyboard = true;
//this.webpMachine.polyfillDocument();
resolve();
};
let sc = document.createElement('script');
sc.src = 'webp-hero.bundle.js';
sc.async = true;
sc.onload = (window as any).webpLoaded;
/* sc.innerHTML = `
window.webpMachine = new webpHero.WebpMachine();
window.webpMachine.polyfillDocument();
`; */
document.body.appendChild(sc);
});
}
convert(bytes: Uint8Array): Promise<string> {
return this.webpMachine.decode(bytes);
}
async processQueue() {
if(this.busyPromise) return;
let {img, bytes} = this.queue.pop();
await this.loaded;
this.busyPromise = this.convert(bytes);
img.src = await this.busyPromise;
this.busyPromise = null;
if(this.queue.length) {
this.processQueue();
}
}
async polyfillImage(img: HTMLImageElement, blob: Blob) {
/* console.log('polyfillImage', this);
return this.webpMachine.polyfillImage(image); */
if(await this.webpMachine.webpSupport) {
img.src = URL.createObjectURL(blob);
return;
}
const reader = new FileReader();
reader.addEventListener('loadend', async(e) => {
// @ts-ignore
let bytes = new Uint8Array(e.srcElement.result);
this.queue.push({bytes, img});
this.processQueue();
});
reader.readAsArrayBuffer(blob);
}
}
export default new AppWebpManager();

4
src/lib/mtproto/apiFileManager.ts

@ -218,7 +218,9 @@ export class ApiFileManager {
}); });
}, dcID); }, dcID);
var processDownloaded = function(bytes: any) { var processDownloaded = (bytes: Uint8Array) => {
//this.log('processDownloaded', location, bytes);
return Promise.resolve(bytes); return Promise.resolve(bytes);
/* if(!location.sticker || WebpManager.isWebpSupported()) { /* if(!location.sticker || WebpManager.isWebpSupported()) {
return qSync.when(bytes); return qSync.when(bytes);

17
src/scss/partials/_chat.scss

@ -190,12 +190,19 @@
} }
} }
.emoji { img.emoji {
height: 18px; height: 18px;
width: 18px; width: 18px;
margin: 0 .05rem; margin: 0 .05rem;
} }
span.emoji {
height: auto;
width: auto;
overflow: visible;
vertical-align: unset;
}
&.emoji-big { &.emoji-big {
font-size: 0; font-size: 0;
background: none!important; background: none!important;
@ -915,12 +922,16 @@
color: #111; color: #111;
} }
.emoji { img.emoji {
font-size: 16px;
height: 16px; height: 16px;
width: 16px; width: 16px;
vertical-align: top; vertical-align: top;
} }
span.emoji {
font-size: 16px;
vertical-align: unset;
}
} }
/* #chat-closed { /* #chat-closed {

6
src/scss/partials/_chatlist.scss

@ -43,8 +43,10 @@
ul { ul {
margin: 0; margin: 0;
//padding: 0 .5rem; //padding: 0 .5rem;
display: grid; /* display: grid;
grid-auto-columns: 1fr; grid-auto-columns: 1fr; */
display: flex;
flex-direction: column;
/* grid-gap: 4px; */ /* grid-gap: 4px; */
width: 100%; width: 100%;
} }

5
src/scss/style.scss

@ -1249,6 +1249,11 @@ div.scrollable::-webkit-scrollbar-thumb {
opacity: .4; opacity: .4;
} }
[contenteditable] {
-webkit-user-select: text;
user-select: text;
}
.menu-horizontal { .menu-horizontal {
width: 100%; width: 100%;
color: $darkgrey; color: $darkgrey;

1
webp-hero.bundle.js

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