Browse Source

Fix sidebar load & photo single objectURL & no raf message render

master
morethanwords 5 years ago
parent
commit
da6a4d2d2e
  1. 8
      src/components/lazyLoadQueue.ts
  2. 10
      src/components/misc.ts
  3. 48
      src/components/wrappers.ts
  4. 7
      src/lib/appManagers/appDialogsManager.ts
  5. 24
      src/lib/appManagers/appDocsManager.ts
  6. 42
      src/lib/appManagers/appImManager.ts
  7. 6
      src/lib/appManagers/appMessagesManager.ts
  8. 30
      src/lib/appManagers/appPhotosManager.ts
  9. 532
      src/lib/appManagers/appSidebarRight.ts
  10. 3
      src/lib/appManagers/appWebPagesManager.ts
  11. 6
      src/lib/appManagers/appWebpManager.ts
  12. 5
      src/lib/lottieLoader.ts
  13. 13
      src/lib/mtproto/apiFileManager.ts
  14. 25
      webpack.common.js

8
src/components/lazyLoadQueue.ts

@ -14,6 +14,8 @@ export default class LazyLoadQueue { @@ -14,6 +14,8 @@ export default class LazyLoadQueue {
private lockPromise: Promise<void> = null;
private unlockResolve: () => void = null;
private log = console.log.bind(console, '[LL]:');
constructor(private parallelLimit = 5) {
}
@ -67,13 +69,13 @@ export default class LazyLoadQueue { @@ -67,13 +69,13 @@ export default class LazyLoadQueue {
let tempID = this.tempID;
console.log('lazyLoadQueue: will load media', this.lockPromise);
this.log('will load media', this.lockPromise, item);
try {
if(this.lockPromise) {
let perf = performance.now();
await this.lockPromise;
console.log('lazyLoadQueue: waited lock:', performance.now() - perf);
this.log('waited lock:', performance.now() - perf);
}
await new Promise((resolve, reject) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve)));
@ -86,7 +88,7 @@ export default class LazyLoadQueue { @@ -86,7 +88,7 @@ export default class LazyLoadQueue {
this.loadingMedia--;
}
console.log('lazyLoadQueue: loaded media');
this.log('loaded media');
if(this.lazyLoadMedia.length) {
this.processQueue();

10
src/components/misc.ts

@ -92,6 +92,16 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool @@ -92,6 +92,16 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool
});
}
export function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGImageElement, url: string) {
let img = new Image();
img.src = url;
img.onload = () => {
if(elem instanceof HTMLImageElement) elem.src = url;
else if(elem instanceof SVGImageElement) elem.setAttributeNS(null, 'href', url);
else elem.style.backgroundImage = 'url(' + url + ')';
};
}
export function putPreloader(elem: Element, returnDiv = false) {
const html = `
<svg xmlns="http://www.w3.org/2000/svg" class="preloader-circular" viewBox="25 25 50 50">

48
src/components/wrappers.ts

@ -12,9 +12,10 @@ import appWebpManager from '../lib/appManagers/appWebpManager'; @@ -12,9 +12,10 @@ import appWebpManager from '../lib/appManagers/appWebpManager';
import {wrapPlayer} from '../lib/ckin';
import { RichTextProcessor } from '../lib/richtextprocessor';
import { CancellablePromise } from '../lib/polyfill';
import { renderImageFromUrl } from './misc';
export type MTDocument = {
_: 'document',
_: 'document' | 'documentEmpty',
pFlags: any,
flags: number,
id: string,
@ -27,12 +28,25 @@ export type MTDocument = { @@ -27,12 +28,25 @@ export type MTDocument = {
dc_id: number,
attributes: any[],
thumb?: MTPhotoSize,
type?: string,
h?: number,
w?: number,
file_name?: string,
file?: File,
duration?: number
duration?: number,
downloaded?: boolean,
version?: any,
audioTitle?: string,
audioPerformer?: string,
sticker?: boolean,
stickerEmoji?: string,
stickerEmojiRaw?: string,
stickerSetInput?: any,
animated?: boolean
};
export type MTPhotoSize = {
@ -497,17 +511,13 @@ export async function wrapPhoto(this: AppImManager, photoID: string, message: an @@ -497,17 +511,13 @@ export async function wrapPhoto(this: AppImManager, photoID: string, message: an
preloader.attach(container, true, promise);
}
return promise.then((blob) => {
return promise.then(() => {
if(this.peerID != peerID) {
this.log.warn('peer changed');
return;
}
if(withTail) {
image.setAttributeNS(null, 'href', URL.createObjectURL(blob));
} else if(image instanceof Image) {
image.src = URL.createObjectURL(blob);
}
renderImageFromUrl(image, photo.url);
});
};
@ -525,14 +535,15 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: ( @@ -525,14 +535,15 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
if(!stickerType) {
console.error('wrong doc for wrapSticker!', doc, div);
return Promise.resolve();
}
//////console.log('wrap sticker', doc, onlyThumb);
console.log('wrap sticker', doc, div, onlyThumb);
if(doc.thumbs && !div.firstElementChild) {
let thumb = doc.thumbs[0];
///////console.log('wrap sticker', thumb);
console.log('wrap sticker', thumb, div);
if(thumb.bytes) {
apiFileManager.saveSmallFile(thumb.location, thumb.bytes);
@ -566,14 +577,7 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: ( @@ -566,14 +577,7 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
return lazyLoadQueue ? (lazyLoadQueue.push({div, load}), Promise.resolve()) : load();
}
let load = () => apiFileManager.downloadSmallFile({
_: 'inputDocumentFileLocation',
access_hash: doc.access_hash,
file_reference: doc.file_reference,
thumb_size: ''/* document.thumbs[0].type */,
id: doc.id,
stickerType: stickerType
}, {mimeType: doc.mime_type, dcID: doc.dc_id}).then(blob => {
let load = () => appDocsManager.downloadDoc(doc.id).then(blob => {
//console.log('loaded sticker:', blob, div);
if(middleware && !middleware()) return;
@ -666,7 +670,7 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: ( @@ -666,7 +670,7 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
appStickersManager.saveSticker(doc);
});
return lazyLoadQueue ? (lazyLoadQueue.push({div, load}), Promise.resolve()) : load();
return lazyLoadQueue && (!doc.downloaded || stickerType == 2) ? (lazyLoadQueue.push({div, load, wasSeen: group == 'chat'}), Promise.resolve()) : load();
}
export function wrapReply(title: string, subtitle: string, media?: any) {
@ -710,8 +714,8 @@ export function wrapReply(title: string, subtitle: string, media?: any) { @@ -710,8 +714,8 @@ export function wrapReply(title: string, subtitle: string, media?: any) {
}
appPhotosManager.preloadPhoto(photo, appPhotosManager.choosePhotoSize(photo, 32, 32))
.then((blob) => {
replyMedia.style.backgroundImage = 'url(' + URL.createObjectURL(blob) + ')';
.then(blob => {
renderImageFromUrl(replyMedia, photo.url || URL.createObjectURL(blob));
});
replyContent.append(replyMedia);

7
src/lib/appManagers/appDialogsManager.ts

@ -6,7 +6,7 @@ import appPeersManager from './appPeersManager'; @@ -6,7 +6,7 @@ import appPeersManager from './appPeersManager';
import appMessagesManager from "./appMessagesManager";
import appUsersManager from "./appUsersManager";
import { RichTextProcessor } from "../richtextprocessor";
import { ripple } from "../../components/misc";
import { ripple, renderImageFromUrl } from "../../components/misc";
import appSidebarLeft from "./appSidebarLeft";
import Scrollable from "../../components/scrollable";
@ -219,13 +219,14 @@ export class AppDialogsManager { @@ -219,13 +219,14 @@ export class AppDialogsManager {
let img = new Image();
img.src = this.savedAvatarURLs[peerID];
//renderImageFromUrl(img, this.savedAvatarURLs[peerID]);
div.innerHTML = '';
//div.style.fontSize = '0'; // need
//div.style.backgroundColor = '';
window.requestAnimationFrame(() => {
//window.requestAnimationFrame(() => {
div.append(img);
});
//});
return true;
}

24
src/lib/appManagers/appDocsManager.ts

@ -2,12 +2,12 @@ import apiFileManager from '../mtproto/apiFileManager'; @@ -2,12 +2,12 @@ import apiFileManager from '../mtproto/apiFileManager';
import FileManager from '../filemanager';
import {RichTextProcessor} from '../richtextprocessor';
import { CancellablePromise } from '../polyfill';
//import { MTDocument } from '../../components/misc';
import { MTDocument } from '../../components/wrappers';
class AppDocsManager {
private docs: any = {};
public saveDoc(apiDoc: /* MTDocument */any, context?: any) {
public saveDoc(apiDoc: MTDocument/* any */, context?: any) {
console.log('saveDoc', apiDoc, this.docs[apiDoc.id]);
if(this.docs[apiDoc.id]) return this.docs[apiDoc.id];
@ -136,15 +136,17 @@ class AppDocsManager { @@ -136,15 +136,17 @@ class AppDocsManager {
return this.docs[docID] !== undefined;
}
public getFileName(doc: any) {
if (doc.file_name) {
return doc.file_name
public getFileName(doc: MTDocument) {
if(doc.file_name) {
return doc.file_name;
}
var fileExt = '.' + doc.mime_type.split('/')[1]
if (fileExt == '.octet-stream') {
fileExt = ''
var fileExt = '.' + doc.mime_type.split('/')[1];
if(fileExt == '.octet-stream') {
fileExt = '';
}
return 't_' + (doc.type || 'file') + doc.id + fileExt
return 't_' + (doc.type || 'file') + doc.id + fileExt;
}
public updateDocDownloaded(docID: string) {
@ -166,8 +168,8 @@ class AppDocsManager { @@ -166,8 +168,8 @@ class AppDocsManager {
}
}
public downloadDoc(docID: any, toFileEntry?: any): CancellablePromise<Blob> {
let doc: any;
public downloadDoc(docID: string | MTDocument, toFileEntry?: any): CancellablePromise<Blob> {
let doc: MTDocument;
if(typeof(docID) === 'string') {
doc = this.docs[docID];
} else {

42
src/lib/appManagers/appImManager.ts

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import apiManager from '../mtproto/apiManager';
import { $rootScope, isElementInViewport, numberWithCommas, findUpClassName, formatNumber, placeCaretAtEnd, calcImageInBox, findUpTag, langPack } from "../utils";
import appUsersManager from "./appUsersManager";
import appMessagesManager, { HistoryResult } from "./appMessagesManager";
import appMessagesManager from "./appMessagesManager";
import appPeersManager from "./appPeersManager";
import appProfileManager from "./appProfileManager";
import appDialogsManager from "./appDialogsManager";
@ -993,8 +993,11 @@ export class AppImManager { @@ -993,8 +993,11 @@ export class AppImManager {
this.firstTopMsgID = dialog ? dialog.top_message : 0;
this.chatInner.style.visibility = 'hidden';
this.chatInput.style.display = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID) ? 'none' : '';
this.topbar.style.display = '';
window.requestAnimationFrame(() => {
this.chatInner.style.visibility = 'hidden';
//this.chatInner.style.visibility = 'hidden';
let title = '';
if(this.peerID == this.myID) title = 'Saved Messages';
@ -1002,9 +1005,10 @@ export class AppImManager { @@ -1002,9 +1005,10 @@ export class AppImManager {
//this.titleEl.innerHTML = appSidebarRight.profileElements.name.innerHTML = dom.titleSpan.innerHTML;
this.titleEl.innerHTML = appSidebarRight.profileElements.name.innerHTML = title;
this.pinnedMessageContainer.style.display = 'none';
this.topbar.style.display = this.goDownBtn.style.display = '';
this.goDownBtn.style.display = '';
//this.topbar.style.display = this.goDownBtn.style.display = '';
//this.chatInput.style.display = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID) ? 'none' : '';
//appSidebarRight.toggleSidebar(true);
this.chatInput.style.display = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID) ? 'none' : '';
if(appPeersManager.isAnyGroup(peerID)) this.chatInner.classList.add('is-chat');
else this.chatInner.classList.remove('is-chat');
@ -1454,21 +1458,15 @@ export class AppImManager { @@ -1454,21 +1458,15 @@ export class AppImManager {
bubbleContainer.style.height = attachmentDiv.style.height;
bubbleContainer.style.width = attachmentDiv.style.width;
//appPhotosManager.setAttachmentSize(doc, bubble);
let load = () => wrapSticker(doc, attachmentDiv, () => {
wrapSticker(doc, attachmentDiv, () => {
if(this.peerID != peerID) {
this.log.warn('peer changed, canceling sticker attach');
return false;
}
return true;
}, null, 'chat', false, !!message.pending || !multipleRender).then(() => {
//preloader.detach();
/* attachmentDiv.style.width = '';
attachmentDiv.style.height = ''; */
});
this.lazyLoadQueue.push({div: bubble, load: load, wasSeen: true});
}, this.lazyLoadQueue, 'chat', false, !!message.pending || !multipleRender);
break;
} else if(doc.mime_type == 'video/mp4' && doc.size <= 20e6) {
this.log('never get free 2', doc);
@ -1703,6 +1701,8 @@ export class AppImManager { @@ -1703,6 +1701,8 @@ export class AppImManager {
}
}
//history = history.slice(); // need
if(additionMsgID) {
history.unshift(additionMsgID);
}
@ -1719,6 +1719,8 @@ export class AppImManager { @@ -1719,6 +1719,8 @@ export class AppImManager {
this.scrollPosition.prepareFor(reverse ? 'up' : 'down');
}
let firstLoad = !!this.setPeerPromise && false;
let peerID = this.peerID;
return new Promise<boolean>((resolve, reject) => {
let resolved = false;
@ -1733,6 +1735,8 @@ export class AppImManager { @@ -1733,6 +1735,8 @@ export class AppImManager {
//console.timeEnd('appImManager: pre render start');
//this.log('start performHistoryResult, scrollTop:', this.scrollable.scrollTop, this.scrollable.scrollHeight, this.scrollable.innerHeight);
let renderedFirstScreen = false;
let r = () => {
//let bubble = bubbles.shift();
@ -1771,7 +1775,7 @@ export class AppImManager { @@ -1771,7 +1775,7 @@ export class AppImManager {
if(reverse) {
this.scrollable.prepend(bubble);
//console.log('innerHeight', innerHeight, this.scrollable.scrollTop);
//this.log('performHistoryResult scrollTop', this.scrollable.scrollTop, bubble.scrollHeight);
/* if(innerHeight >= 0) {
let height = bubble.scrollHeight;
@ -1816,10 +1820,10 @@ export class AppImManager { @@ -1816,10 +1820,10 @@ export class AppImManager {
this.scrollable.append(bubble);
} */
window.requestAnimationFrame(r);
firstLoad ? window.requestAnimationFrame(r) : r();
};
window.requestAnimationFrame(r);
firstLoad ? window.requestAnimationFrame(r) : r();
//r();
/* method((msgID) => {
let message = appMessagesManager.getMessage(msgID);
@ -1854,7 +1858,7 @@ export class AppImManager { @@ -1854,7 +1858,7 @@ export class AppImManager {
maxID = dialog.top_message/* + 1 */;
}
let loadCount = Object.keys(this.bubbles).length > 0 ? 20 : this.scrollable.container.parentElement.scrollHeight / 30/* * 1.25 */ | 0;
let loadCount = Object.keys(this.bubbles).length > 0 ? 20 : this.scrollable.innerHeight / 38/* * 1.25 */ | 0;
/* if(testScroll) {
loadCount = 5;
@ -1901,7 +1905,9 @@ export class AppImManager { @@ -1901,7 +1905,9 @@ export class AppImManager {
return (reverse ? this.getHistoryTopPromise = promise : this.getHistoryBottomPromise = promise);
} else {
return this.performHistoryResult(result.history || [], reverse, isBackLimit, additionMsgID, true);
let promise = this.performHistoryResult(result.history || [], reverse, isBackLimit, additionMsgID, true);
return (reverse ? this.getHistoryTopPromise = promise : this.getHistoryBottomPromise = promise);
//return this.performHistoryResult(result.history || [], reverse, isBackLimit, additionMsgID, true);
}
}

6
src/lib/appManagers/appMessagesManager.ts

@ -1137,7 +1137,8 @@ export class AppMessagesManager { @@ -1137,7 +1137,8 @@ export class AppMessagesManager {
if(apiMessage.media.ttl_seconds) {
apiMessage.media = {_: 'messageMediaUnsupportedWeb'};
} else {
appPhotosManager.savePhoto(apiMessage.media.photo, mediaContext);
apiMessage.media.photo = appPhotosManager.savePhoto(apiMessage.media.photo, mediaContext);
//appPhotosManager.savePhoto(apiMessage.media.photo, mediaContext);
}
break
case 'messageMediaDocument':
@ -1171,7 +1172,8 @@ export class AppMessagesManager { @@ -1171,7 +1172,8 @@ export class AppMessagesManager {
var migrateTo;
switch(apiMessage.action._) {
case 'messageActionChatEditPhoto':
appPhotosManager.savePhoto(apiMessage.action.photo, mediaContext);
apiMessage.action.photo = appPhotosManager.savePhoto(apiMessage.action.photo, mediaContext);
//appPhotosManager.savePhoto(apiMessage.action.photo, mediaContext);
if(isBroadcast) {
apiMessage.action._ = 'messageActionChannelEditPhoto';
}

30
src/lib/appManagers/appPhotosManager.ts

@ -7,7 +7,7 @@ import apiFileManager from "../mtproto/apiFileManager"; @@ -7,7 +7,7 @@ import apiFileManager from "../mtproto/apiFileManager";
import apiManager from "../mtproto/apiManager";
type MTPhoto = {
_: 'photo',
_: 'photo' | 'photoEmpty' | string,
pFlags: any,
flags: number,
id: string,
@ -18,7 +18,8 @@ type MTPhoto = { @@ -18,7 +18,8 @@ type MTPhoto = {
dc_id: number,
user_id: number,
downloaded?: boolean
downloaded?: boolean | number,
url?: string
};
export class AppPhotosManager {
@ -52,7 +53,7 @@ export class AppPhotosManager { @@ -52,7 +53,7 @@ export class AppPhotosManager {
console.warn('no apiPhoto.id', photo);
} else this.photos[photo.id] = photo;
if(!('sizes' in photo)) return;
/* if(!('sizes' in photo)) return;
photo.sizes.forEach((photoSize: any) => {
if(photoSize._ == 'photoCachedSize') {
@ -65,7 +66,7 @@ export class AppPhotosManager { @@ -65,7 +66,7 @@ export class AppPhotosManager {
delete photoSize.bytes;
photoSize._ = 'photoSize';
}
});
}); */
/* if(!photo.downloaded) {
photo.downloaded = apiFileManager.isFileExists({
@ -141,7 +142,8 @@ export class AppPhotosManager { @@ -141,7 +142,8 @@ export class AppPhotosManager {
var photoIDs = [];
var context = {user_id: userID};
for(var i = 0; i < photosResult.photos.length; i++) {
this.savePhoto(photosResult.photos[i], context);
//this.savePhoto(photosResult.photos[i], context);
photosResult.photos[i] = this.savePhoto(photosResult.photos[i], context);
photoIDs.push(photosResult.photos[i].id);
}
@ -237,8 +239,8 @@ export class AppPhotosManager { @@ -237,8 +239,8 @@ export class AppPhotosManager {
return photoSize;
}
public preloadPhoto(photoID: any, photoSize?: MTPhotoSize): Promise<Blob> {
let photo: any = null;
public preloadPhoto(photoID: any, photoSize?: MTPhotoSize): Promise<Blob | string> {
let photo: MTPhoto = null;
if(typeof(photoID) === 'string') {
photo = this.photos[photoID];
@ -267,6 +269,10 @@ export class AppPhotosManager { @@ -267,6 +269,10 @@ export class AppPhotosManager {
let promise: Promise<Blob>;
/* if(photo.downloaded == photoSize.size && photo.url) {
return Promise.resolve(photo.url);
} */
if(isPhoto/* && photoSize.size >= 1e6 */) {
//console.log('Photos downloadFile exec', photo);
promise = apiFileManager.downloadFile(photo.dc_id, location, photoSize.size);
@ -276,8 +282,14 @@ export class AppPhotosManager { @@ -276,8 +282,14 @@ export class AppPhotosManager {
}
if(typeof(photoID) === 'string') {
promise.then(() => {
this.photos[photoID].downloaded = true;
let photo = this.photos[photoID];
promise.then(blob => {
if(!photo.downloaded || photo.downloaded < blob.size) {
photo.downloaded = blob.size;
photo.url = URL.createObjectURL(blob);
console.log('wrote photo:', photo, photoSize, blob);
}
});
}

532
src/lib/appManagers/appSidebarRight.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { horizontalMenu, formatPhoneNumber, putPreloader } from "../../components/misc";
import { horizontalMenu, formatPhoneNumber, putPreloader, renderImageFromUrl } from "../../components/misc";
import Scrollable from '../../components/scrollable';
import { $rootScope } from "../utils";
import appMessagesManager from "./appMessagesManager";
@ -67,6 +67,9 @@ class AppSidebarRight { @@ -67,6 +67,9 @@ class AppSidebarRight {
[type: string]: number[]
}
} = {};
public usedFromHistory: {
[type: string]: number
} = {};
private log = logger('SR');
@ -83,7 +86,7 @@ class AppSidebarRight { @@ -83,7 +86,7 @@ class AppSidebarRight {
private mediaDivsByIDs: {
[mid: number]: HTMLDivElement
} = {};
public urlsToRevoke: string[] = [];
constructor() {
@ -93,8 +96,8 @@ class AppSidebarRight { @@ -93,8 +96,8 @@ class AppSidebarRight {
this.scroll = new Scrollable(this.sidebarEl, 'y', 1200, 'SR');
this.scroll.container.addEventListener('scroll', this.onSidebarScroll.bind(this));
this.scroll.onScrolledBottom = () => {
if(this.sharedMediaSelected && !this.scroll.hiddenElements.down.length
&& this.sharedMediaSelected.childElementCount/* && false */) {
if(this.sharedMediaSelected && !this.scroll.hiddenElements.down.length && this.sharedMediaSelected.childElementCount/* && false */) {
this.log('onScrolledBottom will load media');
this.loadSidebarMedia(true);
}
};
@ -104,7 +107,7 @@ class AppSidebarRight { @@ -104,7 +107,7 @@ class AppSidebarRight {
this.sharedMediaType = this.sharedMediaTypes[id];
this.sharedMediaSelected = tabContent.firstElementChild as HTMLDivElement;
if(this.prevTabID != -1 && !this.sharedMediaSelected.childElementCount) { // quick brown fix
this.loadSidebarMedia(true);
}
@ -142,11 +145,10 @@ class AppSidebarRight { @@ -142,11 +145,10 @@ class AppSidebarRight {
let ids = Object.keys(this.mediaDivsByIDs).map(k => +k).sort();
let idx = ids.findIndex(i => i == messageID);
let targets = ids.map(id => ({element: this.mediaDivsByIDs[id], mid: id}));
appMediaViewer.openMedia(message, target, false, this.sidebarEl,
targets.slice(idx + 1).reverse(), targets.slice(0, idx).reverse(), () => this.loadSidebarMedia(true));
appMediaViewer.openMedia(message, target, false, this.sidebarEl, targets.slice(idx + 1).reverse(), targets.slice(0, idx).reverse(), () => this.loadSidebarMedia(true));
});
this.profileElements.notificationsCheckbox.addEventListener('change', () => {
@ -154,12 +156,7 @@ class AppSidebarRight { @@ -154,12 +156,7 @@ class AppSidebarRight {
appImManager.mutePeer();
});
window.addEventListener('resize', () => {
setTimeout(() => {
this.scroll.onScroll();
this.onSidebarScroll();
}, 0);
});
window.addEventListener('resize', this.onSidebarScroll.bind(this));
if(testScroll) {
let div = document.createElement('div');
@ -191,6 +188,7 @@ class AppSidebarRight { @@ -191,6 +188,7 @@ class AppSidebarRight {
setTimeout(() => this.lazyLoadQueueSidebar.check(), 200);
this.sidebarEl.classList.add('active');
} else this.sidebarEl.classList.remove('active');
return;
}
@ -201,12 +199,237 @@ class AppSidebarRight { @@ -201,12 +199,237 @@ class AppSidebarRight {
}
}
public performSearchResult(ids: number[], type: string) {
let peerID = this.peerID;
let sharedMediaDiv: HTMLDivElement;
let messages: any[] = [];
for(let mid of ids) {
let message = appMessagesManager.getMessage(mid);
if(message.media) messages.push(message);
}
let elemsToAppend: HTMLElement[] = [];
/*'inputMessagesFilterContacts',
'inputMessagesFilterPhotoVideo',
'inputMessagesFilterDocument',
'inputMessagesFilterUrl',
'inputMessagesFilterVoice'*/
switch(type) {
case 'inputMessagesFilterPhotoVideo': {
sharedMediaDiv = this.sharedMedia.contentMedia;
for(let message of messages) {
let media = message.media.photo || message.media.document || (message.media.webpage && message.media.webpage.document);
if(!media) {
//this.log('no media!', message);
continue;
}
if(media._ == 'document' && media.type != 'video'/* && media.type != 'gif' */) {
//this.log('broken video', media);
continue;
}
let div = document.createElement('div');
//console.log(message, photo);
let isPhoto = media._ == 'photo';
let photo = isPhoto ? appPhotosManager.getPhoto(media.id) : null;
if(!photo || !photo.downloaded) {
//this.log('inputMessagesFilterPhotoVideo', message, media, photo, div);
let sizes = media.sizes || media.thumbs;
if(sizes && sizes[0].bytes) {
appPhotosManager.setAttachmentPreview(sizes[0].bytes, div, false, true);
} /* else {
this.log('no stripped size', message, media);
} */
}
//this.log('inputMessagesFilterPhotoVideo', message, media);
let load = () => appPhotosManager.preloadPhoto(isPhoto ? media.id : media, appPhotosManager.choosePhotoSize(media, 200, 200))
.then((blob) => {
if($rootScope.selectedPeerID != peerID) {
this.log.warn('peer changed');
return;
}
if(photo && photo.url) {
renderImageFromUrl(div, photo.url);
} else {
let url = URL.createObjectURL(blob);
this.urlsToRevoke.push(url);
let img = new Image();
img.src = url;
img.onload = () => {
div.style.backgroundImage = 'url(' + url + ')';
};
}
//div.style.backgroundImage = 'url(' + url + ')';
});
div.dataset.mid = '' + message.mid;
if(photo && photo.downloaded) load();
else this.lazyLoadQueueSidebar.push({div, load});
this.lastSharedMediaDiv.append(div);
if(this.lastSharedMediaDiv.childElementCount == 3) {
if(!this.scroll.contains(this.lastSharedMediaDiv)) {
elemsToAppend.push(this.lastSharedMediaDiv);
}
this.lastSharedMediaDiv = document.createElement('div');
}
this.mediaDivsByIDs[message.mid] = div;
//sharedMediaDiv.append(div);
}
break;
}
case 'inputMessagesFilterDocument': {
sharedMediaDiv = this.sharedMedia.contentDocuments;
for(let message of messages) {
if(!message.media.document || message.media.document.type == 'voice') {
continue;
}
let doc = message.media.document;
if(doc.attributes) {
if(doc.attributes.find((a: any) => a._ == "documentAttributeSticker")) {
continue;
}
}
//this.log('come back down to my knees', message);
let div = wrapDocument(message.media.document, true);
elemsToAppend.push(div);
}
break;
}
case 'inputMessagesFilterUrl': {
sharedMediaDiv = this.sharedMedia.contentLinks;
for(let message of messages) {
if(!message.media.webpage || message.media.webpage._ == 'webPageEmpty') {
continue;
}
let webpage = message.media.webpage;
let div = document.createElement('div');
let previewDiv = document.createElement('div');
previewDiv.classList.add('preview');
//this.log('wrapping webpage', webpage);
if(webpage.photo) {
let load = () => appPhotosManager.preloadPhoto(webpage.photo.id, appPhotosManager.choosePhotoSize(webpage.photo, 380, 0))
.then((blob) => {
if($rootScope.selectedPeerID != peerID) {
this.log.warn('peer changed');
return;
}
renderImageFromUrl(previewDiv, webpage.photo.url);
/* let url = URL.createObjectURL(blob);
this.urlsToRevoke.push(url);
previewDiv.style.backgroundImage = 'url(' + url + ')'; */
});
this.lazyLoadQueueSidebar.push({div: previewDiv, load});
} else {
previewDiv.innerText = (webpage.title || webpage.description || webpage.url || webpage.display_url).slice(0, 1);
previewDiv.classList.add('empty');
}
let title = webpage.rTitle || '';
let subtitle = webpage.rDescription || '';
let url = RichTextProcessor.wrapRichText(webpage.url || '');
if(!title) {
//title = new URL(webpage.url).hostname;
title = webpage.display_url.split('/', 1)[0];
}
div.append(previewDiv);
div.insertAdjacentHTML('beforeend', `
<div class="title">${title}</div>
<div class="subtitle">${subtitle}</div>
<div class="url">${url}</div>
`);
if(div.innerText.trim().length) {
elemsToAppend.push(div);
}
}
break;
}
/* case 'inputMessagesFilterVoice': {
//this.log('wrapping audio', message.media);
if(!message.media || !message.media.document || message.media.document.type != 'voice') {
break;
}
let doc = message.media.document;
this.log('wrapping audio', doc);
let audioDiv = wrapAudio(doc);
this.sharedMedia.contentAudio.append(audioDiv);
break;
} */
default:
//console.warn('death is my friend', message);
break;
}
if(this.lastSharedMediaDiv.childElementCount && !this.scroll.contains(this.lastSharedMediaDiv)) {
elemsToAppend.push(this.lastSharedMediaDiv);
}
if(elemsToAppend.length) {
//window.requestAnimationFrame(() => {
elemsToAppend.forEach(el => this.scroll.append(el));
//});
}
if(sharedMediaDiv) {
let parent = sharedMediaDiv.parentElement;
if(parent.lastElementChild.classList.contains('preloader')) {
parent.lastElementChild.remove();
}
}
this.onSidebarScroll();
}
public loadSidebarMedia(single = false) {
if(testScroll/* || 1 == 1 */) {
return;
}
//this.log('loadSidebarMedia', single, this.peerID);
this.log('loadSidebarMedia', single, this.peerID);
let peerID = this.peerID;
@ -214,16 +437,25 @@ class AppSidebarRight { @@ -214,16 +437,25 @@ class AppSidebarRight {
typesToLoad = typesToLoad.filter(type => !this.loadedAllMedia[type]);
if(!typesToLoad.length) return;
if(!this.historiesStorage[peerID]) this.historiesStorage[peerID] = {};
let historyStorage = this.historiesStorage[peerID];
let historyStorage = this.historiesStorage[peerID] ?? (this.historiesStorage[peerID] = {});
this.scroll.lock();
let promises = typesToLoad.map(type => {
if(this.loadSidebarMediaPromises[type]) return this.loadSidebarMediaPromises[type];
if(!historyStorage[type]) historyStorage[type] = [];
let history = historyStorage[type];
let history = historyStorage[type] ?? (historyStorage[type] = []);
let loadCount = (appPhotosManager.windowH / 130 | 0) * 3;
// render from cache
if(history.length && this.usedFromHistory[type] < history.length && this.cleared[type]) {
let ids = history.slice(this.usedFromHistory[type], this.usedFromHistory[type] + loadCount);
this.log('will render from cache', this.usedFromHistory[type], history, ids, loadCount);
this.usedFromHistory[type] += ids.length;
this.performSearchResult(ids, type);
return;
}
// заливать новую картинку сюда только после полной отправки!
//let maxID = this.minMediaID[type] || 0;
@ -233,9 +465,9 @@ class AppSidebarRight { @@ -233,9 +465,9 @@ class AppSidebarRight {
? appMessagesManager.historiesStorage[peerID].history.slice() : [];
maxID = !maxID && ids.length ? ids[ids.length - 1] : maxID;
//this.log('search house of glass pre', type, ids, maxID);
this.log('search house of glass pre', type, ids, maxID);
let loadCount = history.length ? 50 : 15;
//let loadCount = history.length ? 50 : 15;
return this.loadSidebarMediaPromises[type] = appMessagesManager.getSearch(peerID, '', {_: type}, maxID, loadCount)
.then(value => {
ids = ids.concat(value.history);
@ -243,238 +475,26 @@ class AppSidebarRight { @@ -243,238 +475,26 @@ class AppSidebarRight {
this.log('search house of glass', type, value, ids, this.cleared);
if(value.history.length < loadCount) {
this.loadedAllMedia[type] = true;
}
if($rootScope.selectedPeerID != peerID) {
this.log.warn('peer changed');
return;
}
if(this.cleared[type]) {
ids = history;
delete this.cleared[type];
}
let sharedMediaDiv: HTMLDivElement;
let messages: any[] = [];
for(let mid of ids) {
let message = appMessagesManager.getMessage(mid);
if(message.media) messages.push(message);
if(value.history.length < loadCount) {
this.loadedAllMedia[type] = true;
}
let elemsToAppend: HTMLElement[] = [];
/*'inputMessagesFilterContacts',
'inputMessagesFilterPhotoVideo',
'inputMessagesFilterDocument',
'inputMessagesFilterUrl',
'inputMessagesFilterVoice'*/
switch(type) {
case 'inputMessagesFilterPhotoVideo': {
sharedMediaDiv = this.sharedMedia.contentMedia;
for(let message of messages) {
let media = message.media.photo || message.media.document || (message.media.webpage && message.media.webpage.document);
if(!media) {
//this.log('no media!', message);
continue;
}
if(media._ == 'document' && media.type != 'video'/* && media.type != 'gif' */) {
//this.log('broken video', media);
continue;
}
let div = document.createElement('div');
//console.log(message, photo);
let isPhoto = media._ == 'photo';
let photo = isPhoto ? appPhotosManager.getPhoto(media.id) : null;
if(!photo || !photo.downloaded) {
//this.log('inputMessagesFilterPhotoVideo', message, media, photo, div);
let sizes = media.sizes || media.thumbs;
if(sizes && sizes[0].bytes) {
appPhotosManager.setAttachmentPreview(sizes[0].bytes, div, false, true);
} /* else {
this.log('no stripped size', message, media);
} */
}
//this.log('inputMessagesFilterPhotoVideo', message, media);
let load = () => appPhotosManager.preloadPhoto(isPhoto ? media.id : media, appPhotosManager.choosePhotoSize(media, 200, 200))
.then((blob) => {
if($rootScope.selectedPeerID != peerID) {
this.log.warn('peer changed');
return;
}
let url = URL.createObjectURL(blob);
this.urlsToRevoke.push(url);
let img = new Image();
img.src = url;
img.onload = () => {
div.style.backgroundImage = 'url(' + url + ')';
};
//div.style.backgroundImage = 'url(' + url + ')';
});
div.dataset.mid = '' + message.mid;
if(photo && photo.downloaded) load();
else this.lazyLoadQueueSidebar.push({div, load});
this.lastSharedMediaDiv.append(div);
if(this.lastSharedMediaDiv.childElementCount == 3) {
if(!this.scroll.contains(this.lastSharedMediaDiv)) {
elemsToAppend.push(this.lastSharedMediaDiv);
}
this.lastSharedMediaDiv = document.createElement('div');
}
this.mediaDivsByIDs[message.mid] = div;
//sharedMediaDiv.append(div);
}
break;
}
case 'inputMessagesFilterDocument': {
sharedMediaDiv = this.sharedMedia.contentDocuments;
for(let message of messages) {
if(!message.media.document || message.media.document.type == 'voice') {
continue;
}
let doc = message.media.document;
if(doc.attributes) {
if(doc.attributes.find((a: any) => a._ == "documentAttributeSticker")) {
continue;
}
}
//this.log('come back down to my knees', message);
let div = wrapDocument(message.media.document, true);
elemsToAppend.push(div);
}
break;
}
case 'inputMessagesFilterUrl': {
sharedMediaDiv = this.sharedMedia.contentLinks;
for(let message of messages) {
if(!message.media.webpage || message.media.webpage._ == 'webPageEmpty') {
continue;
}
let webpage = message.media.webpage;
let div = document.createElement('div');
let previewDiv = document.createElement('div');
previewDiv.classList.add('preview');
//this.log('wrapping webpage', webpage);
if(webpage.photo) {
let load = () => appPhotosManager.preloadPhoto(webpage.photo.id, appPhotosManager.choosePhotoSize(webpage.photo, 380, 0))
.then((blob) => {
if($rootScope.selectedPeerID != peerID) {
this.log.warn('peer changed');
return;
}
let url = URL.createObjectURL(blob);
this.urlsToRevoke.push(url);
previewDiv.style.backgroundImage = 'url(' + url + ')';
});
this.lazyLoadQueueSidebar.push({div: previewDiv, load});
} else {
previewDiv.innerText = (webpage.title || webpage.description || webpage.url || webpage.display_url).slice(0, 1);
previewDiv.classList.add('empty');
}
let title = webpage.rTitle || '';
let subtitle = webpage.rDescription || '';
let url = RichTextProcessor.wrapRichText(webpage.url || '');
if(!title) {
//title = new URL(webpage.url).hostname;
title = webpage.display_url.split('/', 1)[0];
}
div.append(previewDiv);
div.insertAdjacentHTML('beforeend', `
<div class="title">${title}</div>
<div class="subtitle">${subtitle}</div>
<div class="url">${url}</div>
`);
if(div.innerText.trim().length) {
elemsToAppend.push(div);
}
}
break;
}
/* case 'inputMessagesFilterVoice': {
//this.log('wrapping audio', message.media);
if(!message.media || !message.media.document || message.media.document.type != 'voice') {
break;
}
let doc = message.media.document;
this.log('wrapping audio', doc);
let audioDiv = wrapAudio(doc);
this.sharedMedia.contentAudio.append(audioDiv);
break;
} */
default:
//console.warn('death is my friend', message);
break;
}
if(this.lastSharedMediaDiv.childElementCount && !this.scroll.contains(this.lastSharedMediaDiv)) {
elemsToAppend.push(this.lastSharedMediaDiv);
if(this.cleared[type]) {
//ids = history;
delete this.cleared[type];
}
if(elemsToAppend.length) {
//window.requestAnimationFrame(() => {
elemsToAppend.forEach(el => this.scroll.append(el));
//});
if(ids.length) {
this.performSearchResult(ids, type);
}
if(sharedMediaDiv) {
let parent = sharedMediaDiv.parentElement;
if(parent.lastElementChild.classList.contains('preloader')) {
parent.lastElementChild.remove();
}
}
this.onSidebarScroll();
}).then(() => {
this.loadSidebarMediaPromises[type] = null;
}, (err) => {
this.log.error('load error:', err);
}).then(() => {
this.loadSidebarMediaPromises[type] = null;
});
});
@ -491,7 +511,7 @@ class AppSidebarRight { @@ -491,7 +511,7 @@ class AppSidebarRight {
this.lastSharedMediaDiv = document.createElement('div');
//this.log('fillProfileElements');
window.requestAnimationFrame(() => {
this.profileContentEl.parentElement.scrollTop = 0;
this.profileElements.bio.style.display = 'none';
@ -500,7 +520,7 @@ class AppSidebarRight { @@ -500,7 +520,7 @@ class AppSidebarRight {
this.profileElements.notificationsRow.style.display = '';
this.profileElements.notificationsCheckbox.checked = true;
this.profileElements.notificationsStatus.innerText = 'Enabled';
Object.keys(this.sharedMedia).forEach(key => {
this.sharedMedia[key].innerHTML = '';
@ -509,12 +529,14 @@ class AppSidebarRight { @@ -509,12 +529,14 @@ class AppSidebarRight {
putPreloader(parent, true);
}
});
this.savedVirtualStates = {};
this.prevTabID = -1;
this.scroll.setVirtualContainer(null);
(this.profileTabs.children[1] as HTMLLIElement).click(); // set media
//this.scroll.getScrollTopOffset();
this.loadSidebarMedia(true);
});
@ -530,6 +552,7 @@ class AppSidebarRight { @@ -530,6 +552,7 @@ class AppSidebarRight {
this.sharedMediaTypes.forEach(type => {
//this.minMediaID[type] = 0;
this.cleared[type] = true;
this.usedFromHistory[type] = 0;
});
let setText = (text: string, el: HTMLDivElement) => {
@ -537,12 +560,14 @@ class AppSidebarRight { @@ -537,12 +560,14 @@ class AppSidebarRight {
if(el.childElementCount > 1) {
el.firstElementChild.remove();
}
let p = document.createElement('p');
p.innerHTML = text;
el.prepend(p);
el.style.display = '';
//this.scroll.getScrollTopOffset();
});
};
@ -564,7 +589,10 @@ class AppSidebarRight { @@ -564,7 +589,10 @@ class AppSidebarRight {
appImManager.setMutedState(muted);
}
} else {
this.profileElements.notificationsRow.style.display = 'none';
window.requestAnimationFrame(() => {
this.profileElements.notificationsRow.style.display = 'none';
//this.scroll.getScrollTopOffset();
})
}
if(peerID > 0) {
@ -590,7 +618,7 @@ class AppSidebarRight { @@ -590,7 +618,7 @@ class AppSidebarRight {
appMessagesManager.wrapSingleMessage(userFull.pinned_msg_id);
}
this.scroll.getScrollTopOffset();
//this.scroll.getScrollTopOffset();
});
} else {
let chat = appPeersManager.getPeer(peerID);
@ -607,11 +635,11 @@ class AppSidebarRight { @@ -607,11 +635,11 @@ class AppSidebarRight {
setText(RichTextProcessor.wrapRichText(chatFull.about), this.profileElements.bio);
}
this.scroll.getScrollTopOffset();
//this.scroll.getScrollTopOffset();
});
}
this.scroll.getScrollTopOffset();
//this.scroll.getScrollTopOffset();
//this.loadSidebarMedia();
}
}

3
src/lib/appManagers/appWebPagesManager.ts

@ -21,7 +21,8 @@ class AppWebPagesManager { @@ -21,7 +21,8 @@ class AppWebPagesManager {
saveWebPage(apiWebPage: any, messageID?: number, mediaContext?: any) {
if(apiWebPage.photo && apiWebPage.photo._ === 'photo') {
appPhotosManager.savePhoto(apiWebPage.photo, mediaContext);
//appPhotosManager.savePhoto(apiWebPage.photo, mediaContext);
apiWebPage.photo = appPhotosManager.savePhoto(apiWebPage.photo, mediaContext);
} else {
delete apiWebPage.photo;
}

6
src/lib/appManagers/appWebpManager.ts

@ -65,7 +65,11 @@ class AppWebpManager { @@ -65,7 +65,11 @@ class AppWebpManager {
await this.loaded;
this.busyPromise = this.convert(bytes);
img.src = await this.busyPromise;
let imgTemp = new Image();
imgTemp.src = await this.busyPromise;
imgTemp.onload = () => {
img.src = imgTemp.src;
};
callback();
this.busyPromise = null;

5
src/lib/lottieLoader.ts

@ -61,6 +61,11 @@ class LottieLoader { @@ -61,6 +61,11 @@ class LottieLoader {
if(canvas) {
let c = container.firstElementChild as HTMLCanvasElement;
if(!c) {
console.warn('no canvas element for check!', container, animations[i]);
continue;
}
if(!c.height && !c.width && isElementInViewport(container)) {
//console.log('lottie need resize');
animation.resize();

13
src/lib/mtproto/apiFileManager.ts

@ -94,12 +94,6 @@ export class ApiFileManager { @@ -94,12 +94,6 @@ export class ApiFileManager {
var fileName = (location.file_name as string || '').split('.');
var ext: string = fileName[fileName.length - 1] || '';
if(location.stickerType == 1) {
ext += 'webp';
} else if(location.stickerType == 2) {
ext += 'tgs';
}
var versionPart = location.version ? ('v' + location.version) : '';
return (fileName[0] ? fileName[0] + '_' : '') + location.id + versionPart + (ext ? '.' + ext : ext);
@ -108,13 +102,6 @@ export class ApiFileManager { @@ -108,13 +102,6 @@ export class ApiFileManager {
this.log.trace('Empty location', location);
}
var ext: string = 'jpg';
if(location.stickerType == 1) {
ext = 'webp';
} else if(location.stickerType == 2) {
ext += 'tgs';
}
if(location.volume_id) {
return location.volume_id + '_' + location.local_id + '.' + ext;
} else {

25
webpack.common.js

@ -1,6 +1,8 @@ @@ -1,6 +1,8 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
let allowedIPs = ['195.66.140.39', '192.168.31.144', '127.0.0.1', '192.168.31.1', '192.168.31.192'];
module.exports = {
module: {
rules: [
@ -74,7 +76,28 @@ module.exports = { @@ -74,7 +76,28 @@ module.exports = {
watchContentBase: true,
compress: true,
http2: true,
port: 9000
host: '0.0.0.0',
port: 9000,
overlay: true,
useLocalIp: true,
before: function(app, server, compiler) {
app.use((req, res, next) => {
let IP = '';
if(req.headers['cf-connecting-ip']) {
IP = req.headers['cf-connecting-ip'];
} else {
IP = req.connection.remoteAddress.split(':').pop();
}
if(!allowedIPs.includes(IP)) {
console.log('Bad IP connecting: ' + IP, req.url);
res.status(404).send('Nothing interesting here.');
} else {
console.log(req.url, IP);
next();
}
});
}
},
plugins: [

Loading…
Cancel
Save