Browse Source

Lazy load avatars in channels & members list

master
Eduard Kuzmenko 4 years ago
parent
commit
039e238b2f
  1. 1
      src/components/appSearchSuper..ts
  2. 108
      src/components/avatar.ts
  3. 3
      src/components/chat/bubbles.ts
  4. 7
      src/components/chat/messageRender.ts
  5. 3
      src/components/chat/replies.ts
  6. 5
      src/components/sortedUserList.ts
  7. 2
      src/lib/appManagers/apiUpdatesManager.ts
  8. 9
      src/lib/appManagers/appDialogsManager.ts

1
src/components/appSearchSuper..ts

@ -883,6 +883,7 @@ export default class AppSearchSuper { @@ -883,6 +883,7 @@ export default class AppSearchSuper {
if(!this.membersList) {
this.membersList = new SortedUserList();
this.membersList.lazyLoadQueue = this.lazyLoadQueue;
this.membersList.list.addEventListener('click', (e) => {
const li = findUpTag(e.target, 'LI');
if(!li) {

108
src/components/avatar.ts

@ -9,10 +9,10 @@ import appProfileManager from "../lib/appManagers/appProfileManager"; @@ -9,10 +9,10 @@ import appProfileManager from "../lib/appManagers/appProfileManager";
import rootScope from "../lib/rootScope";
import { attachClickEvent, cancelEvent } from "../helpers/dom";
import AppMediaViewer, { AppMediaViewerAvatar } from "./appMediaViewer";
import { Message, Photo } from "../layer";
import { Message } from "../layer";
import appPeersManager from "../lib/appManagers/appPeersManager";
import appPhotosManager from "../lib/appManagers/appPhotosManager";
//import type { LazyLoadQueueIntersector } from "./lazyLoadQueue";
import type { LazyLoadQueueIntersector } from "./lazyLoadQueue";
const onAvatarUpdate = (peerId: number) => {
appProfileManager.removeFromAvatarsCache(peerId);
@ -98,24 +98,22 @@ export async function openAvatarViewer(target: HTMLElement, peerId: number, midd @@ -98,24 +98,22 @@ export async function openAvatarViewer(target: HTMLElement, peerId: number, midd
}
}
const believeMe: Map<number, Set<AvatarElement>> = new Map();
const seen: Set<number> = new Set();
export default class AvatarElement extends HTMLElement {
private peerId: number;
private isDialog = false;
public peerTitle: string;
private peerTitle: string;
public loadPromises: Promise<any>[];
//public lazyLoadQueue: LazyLoadQueueIntersector;
//private addedToQueue = false;
constructor() {
super();
// элемент создан
}
public lazyLoadQueue: LazyLoadQueueIntersector;
private addedToQueue = false;
connectedCallback() {
// браузер вызывает этот метод при добавлении элемента в документ
// (может вызываться много раз, если элемент многократно добавляется/удаляется)
this.isDialog = !!this.getAttribute('dialog');
this.isDialog = this.getAttribute('dialog') === '1';
if(this.getAttribute('clickable') === '') {
this.setAttribute('clickable', 'set');
let loading = false;
@ -131,13 +129,21 @@ export default class AvatarElement extends HTMLElement { @@ -131,13 +129,21 @@ export default class AvatarElement extends HTMLElement {
}
}
/* disconnectedCallback() {
disconnectedCallback() {
// браузер вызывает этот метод при удалении элемента из документа
// (может вызываться много раз, если элемент многократно добавляется/удаляется)
const set = believeMe.get(this.peerId);
if(set && set.has(this)) {
set.delete(this);
if(!set.size) {
believeMe.delete(this.peerId);
}
}
if(this.lazyLoadQueue) {
this.lazyLoadQueue.unobserve(this);
}
} */
}
static get observedAttributes(): string[] {
return ['peer', 'dialog', 'peer-title'/* массив имён атрибутов для отслеживания их изменений */];
@ -152,36 +158,88 @@ export default class AvatarElement extends HTMLElement { @@ -152,36 +158,88 @@ export default class AvatarElement extends HTMLElement {
}
this.peerId = appPeersManager.getPeerMigratedTo(+newValue) || +newValue;
const wasPeerId = +oldValue;
if(wasPeerId) {
const set = believeMe.get(wasPeerId);
if(set) {
set.delete(this);
if(!set.size) {
believeMe.delete(wasPeerId);
}
}
}
this.update();
} else if(name === 'peer-title') {
this.peerTitle = newValue;
} else if(name === 'dialog') {
this.isDialog = !!+newValue;
this.isDialog = newValue === '1';
}
}
public update() {
/* if(this.lazyLoadQueue) {
if(this.lazyLoadQueue) {
if(!seen.has(this.peerId)) {
if(this.addedToQueue) return;
this.addedToQueue = true;
let set = believeMe.get(this.peerId);
if(!set) {
set = new Set();
believeMe.set(this.peerId, set);
}
set.add(this);
this.lazyLoadQueue.push({
div: this,
load: () => {
return appProfileManager.putPhoto(this, this.peerId, this.isDialog, this.peerTitle).finally(() => {
this.addedToQueue = false;
});
seen.add(this.peerId);
return this.update();
}
});
this.addedToQueue = true;
} else { */
return;
} else if(this.addedToQueue) {
this.lazyLoadQueue.unobserve(this);
}
}
seen.add(this.peerId);
const res = appProfileManager.putPhoto(this, this.peerId, this.isDialog, this.peerTitle);
if(this.loadPromises && res && res.cached) {
this.loadPromises.push(res.loadPromise);
res.loadPromise.finally(() => {
const promise = res ? res.loadPromise : Promise.resolve();
if(this.loadPromises) {
if(res && res.cached) {
this.loadPromises.push(promise);
}
promise.finally(() => {
this.loadPromises = undefined;
});
}
//}
if(this.addedToQueue) {
promise.finally(() => {
this.addedToQueue = false;
});
}
const set = believeMe.get(this.peerId);
if(set) {
set.delete(this);
const arr = Array.from(set);
believeMe.delete(this.peerId);
for(let i = 0, length = arr.length; i < length; ++i) {
arr[i].update();
}
}
return promise;
}
}
customElements.define("avatar-element", AvatarElement);
customElements.define('avatar-element', AvatarElement);

3
src/components/chat/bubbles.ts

@ -2560,7 +2560,8 @@ export default class ChatBubbles { @@ -2560,7 +2560,8 @@ export default class ChatBubbles {
bubbleContainer,
message: messageWithReplies,
messageDiv,
loadPromises
loadPromises,
lazyLoadQueue: this.lazyLoadQueue
});
if(isFooter) {

7
src/components/chat/messageRender.ts

@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
import { getFullDate } from "../../helpers/date";
import { formatNumber } from "../../helpers/number";
import RichTextProcessor from "../../lib/richtextprocessor";
import { LazyLoadQueueIntersector } from "../lazyLoadQueue";
import { wrapReply } from "../wrappers";
import Chat from "./chat";
import RepliesElement from "./replies";
@ -65,18 +66,20 @@ export namespace MessageRender { @@ -65,18 +66,20 @@ export namespace MessageRender {
return timeSpan;
};
export const renderReplies = ({bubble, bubbleContainer, message, messageDiv, loadPromises}: {
export const renderReplies = ({bubble, bubbleContainer, message, messageDiv, loadPromises, lazyLoadQueue}: {
bubble: HTMLElement,
bubbleContainer: HTMLElement,
message: any,
messageDiv: HTMLElement,
loadPromises?: Promise<any>[]
loadPromises?: Promise<any>[],
lazyLoadQueue?: LazyLoadQueueIntersector
}) => {
const isFooter = !bubble.classList.contains('sticker') && !bubble.classList.contains('emoji-big') && !bubble.classList.contains('round');
const repliesFooter = new RepliesElement();
repliesFooter.message = message;
repliesFooter.type = isFooter ? 'footer' : 'beside';
repliesFooter.loadPromises = loadPromises;
repliesFooter.lazyLoadQueue = lazyLoadQueue;
repliesFooter.init();
bubbleContainer.prepend(repliesFooter);
return isFooter;

3
src/components/chat/replies.ts

@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import type { LazyLoadQueueIntersector } from "../lazyLoadQueue";
import { formatNumber } from "../../helpers/number";
import { Message } from "../../layer";
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
@ -26,6 +27,7 @@ export default class RepliesElement extends HTMLElement { @@ -26,6 +27,7 @@ export default class RepliesElement extends HTMLElement {
public message: Message.message;
public type: 'footer' | 'beside';
public loadPromises: Promise<any>[];
public lazyLoadQueue: LazyLoadQueueIntersector;
private updated = false;
@ -69,6 +71,7 @@ export default class RepliesElement extends HTMLElement { @@ -69,6 +71,7 @@ export default class RepliesElement extends HTMLElement {
avatarElem = new AvatarElement();
avatarElem.setAttribute('dialog', '0');
avatarElem.classList.add('avatar-30');
avatarElem.lazyLoadQueue = this.lazyLoadQueue;
if(this.loadPromises) {
avatarElem.loadPromises = this.loadPromises;

5
src/components/sortedUserList.ts

@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import type { LazyLoadQueueIntersector } from "./lazyLoadQueue";
import appDialogsManager, { DialogDom } from "../lib/appManagers/appDialogsManager";
import { isInDOM, positionElementByIndex, replaceContent } from "../helpers/dom";
import { getHeavyAnimationPromise } from "../hooks/useHeavyAnimationCheck";
@ -20,6 +21,7 @@ export default class SortedUserList { @@ -20,6 +21,7 @@ export default class SortedUserList {
public list: HTMLUListElement;
public users: Map<number, SortedUser>;
public sorted: Array<SortedUser>;
public lazyLoadQueue: LazyLoadQueueIntersector;
constructor() {
this.list = appDialogsManager.createChatList();
@ -75,7 +77,8 @@ export default class SortedUserList { @@ -75,7 +77,8 @@ export default class SortedUserList {
avatarSize: 48,
autonomous: true,
meAsSaved: false,
rippleEnabled: false
rippleEnabled: false,
lazyLoadQueue: this.lazyLoadQueue
});
const sortedUser: SortedUser = {

2
src/lib/appManagers/apiUpdatesManager.ts

@ -596,7 +596,7 @@ export class ApiUpdatesManager { @@ -596,7 +596,7 @@ export class ApiUpdatesManager {
}
public saveUpdate(update: Update) {
this.debug && this.log('saveUpdate', update);
//this.debug && this.log('saveUpdate', update);
rootScope.dispatchEvent(update._, update as any);
}

9
src/lib/appManagers/appDialogsManager.ts

@ -35,6 +35,7 @@ import appNotificationsManager from "./appNotificationsManager"; @@ -35,6 +35,7 @@ import appNotificationsManager from "./appNotificationsManager";
import PeerTitle from "../../components/peerTitle";
import { i18n } from "../langPack";
import findUpTag from "../../helpers/dom/findUpTag";
import { LazyLoadQueueIntersector } from "../../components/lazyLoadQueue";
export type DialogDom = {
avatarEl: AvatarElement,
@ -1213,12 +1214,13 @@ export class AppDialogsManager { @@ -1213,12 +1214,13 @@ export class AppDialogsManager {
meAsSaved?: boolean,
append?: boolean,
avatarSize?: number,
autonomous?: boolean
autonomous?: boolean,
lazyLoadQueue?: LazyLoadQueueIntersector,
}) {
return this.addDialog(options.dialog, options.container, options.drawStatus, options.rippleEnabled, options.onlyFirstName, options.meAsSaved, options.append, options.avatarSize, options.autonomous);
return this.addDialog(options.dialog, options.container, options.drawStatus, options.rippleEnabled, options.onlyFirstName, options.meAsSaved, options.append, options.avatarSize, options.autonomous, options.lazyLoadQueue);
}
public addDialog(_dialog: Dialog | number, container?: HTMLUListElement | Scrollable | false, drawStatus = true, rippleEnabled = true, onlyFirstName = false, meAsSaved = true, append = true, avatarSize = 54, autonomous = !!container) {
public addDialog(_dialog: Dialog | number, container?: HTMLUListElement | Scrollable | false, drawStatus = true, rippleEnabled = true, onlyFirstName = false, meAsSaved = true, append = true, avatarSize = 54, autonomous = !!container, lazyLoadQueue?: LazyLoadQueueIntersector) {
let dialog: Dialog;
if(typeof(_dialog) === 'number') {
@ -1248,6 +1250,7 @@ export class AppDialogsManager { @@ -1248,6 +1250,7 @@ export class AppDialogsManager {
}
const avatarEl = new AvatarElement();
avatarEl.lazyLoadQueue = lazyLoadQueue;
avatarEl.setAttribute('dialog', meAsSaved ? '1' : '0');
avatarEl.setAttribute('peer', '' + peerId);
avatarEl.classList.add('dialog-avatar', 'avatar-' + avatarSize);

Loading…
Cancel
Save