Lazy load avatars in channels & members list
This commit is contained in:
parent
0df9aed287
commit
039e238b2f
@ -883,6 +883,7 @@ export default class AppSearchSuper {
|
|||||||
|
|
||||||
if(!this.membersList) {
|
if(!this.membersList) {
|
||||||
this.membersList = new SortedUserList();
|
this.membersList = new SortedUserList();
|
||||||
|
this.membersList.lazyLoadQueue = this.lazyLoadQueue;
|
||||||
this.membersList.list.addEventListener('click', (e) => {
|
this.membersList.list.addEventListener('click', (e) => {
|
||||||
const li = findUpTag(e.target, 'LI');
|
const li = findUpTag(e.target, 'LI');
|
||||||
if(!li) {
|
if(!li) {
|
||||||
|
@ -9,10 +9,10 @@ import appProfileManager from "../lib/appManagers/appProfileManager";
|
|||||||
import rootScope from "../lib/rootScope";
|
import rootScope from "../lib/rootScope";
|
||||||
import { attachClickEvent, cancelEvent } from "../helpers/dom";
|
import { attachClickEvent, cancelEvent } from "../helpers/dom";
|
||||||
import AppMediaViewer, { AppMediaViewerAvatar } from "./appMediaViewer";
|
import AppMediaViewer, { AppMediaViewerAvatar } from "./appMediaViewer";
|
||||||
import { Message, Photo } from "../layer";
|
import { Message } from "../layer";
|
||||||
import appPeersManager from "../lib/appManagers/appPeersManager";
|
import appPeersManager from "../lib/appManagers/appPeersManager";
|
||||||
import appPhotosManager from "../lib/appManagers/appPhotosManager";
|
import appPhotosManager from "../lib/appManagers/appPhotosManager";
|
||||||
//import type { LazyLoadQueueIntersector } from "./lazyLoadQueue";
|
import type { LazyLoadQueueIntersector } from "./lazyLoadQueue";
|
||||||
|
|
||||||
const onAvatarUpdate = (peerId: number) => {
|
const onAvatarUpdate = (peerId: number) => {
|
||||||
appProfileManager.removeFromAvatarsCache(peerId);
|
appProfileManager.removeFromAvatarsCache(peerId);
|
||||||
@ -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 {
|
export default class AvatarElement extends HTMLElement {
|
||||||
private peerId: number;
|
private peerId: number;
|
||||||
private isDialog = false;
|
private isDialog = false;
|
||||||
public peerTitle: string;
|
private peerTitle: string;
|
||||||
public loadPromises: Promise<any>[];
|
public loadPromises: Promise<any>[];
|
||||||
//public lazyLoadQueue: LazyLoadQueueIntersector;
|
public lazyLoadQueue: LazyLoadQueueIntersector;
|
||||||
//private addedToQueue = false;
|
private addedToQueue = false;
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
// элемент создан
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
// браузер вызывает этот метод при добавлении элемента в документ
|
// браузер вызывает этот метод при добавлении элемента в документ
|
||||||
// (может вызываться много раз, если элемент многократно добавляется/удаляется)
|
// (может вызываться много раз, если элемент многократно добавляется/удаляется)
|
||||||
|
|
||||||
this.isDialog = !!this.getAttribute('dialog');
|
this.isDialog = this.getAttribute('dialog') === '1';
|
||||||
if(this.getAttribute('clickable') === '') {
|
if(this.getAttribute('clickable') === '') {
|
||||||
this.setAttribute('clickable', 'set');
|
this.setAttribute('clickable', 'set');
|
||||||
let loading = false;
|
let loading = false;
|
||||||
@ -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) {
|
if(this.lazyLoadQueue) {
|
||||||
this.lazyLoadQueue.unobserve(this);
|
this.lazyLoadQueue.unobserve(this);
|
||||||
}
|
}
|
||||||
} */
|
}
|
||||||
|
|
||||||
static get observedAttributes(): string[] {
|
static get observedAttributes(): string[] {
|
||||||
return ['peer', 'dialog', 'peer-title'/* массив имён атрибутов для отслеживания их изменений */];
|
return ['peer', 'dialog', 'peer-title'/* массив имён атрибутов для отслеживания их изменений */];
|
||||||
@ -152,36 +158,88 @@ export default class AvatarElement extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.peerId = appPeersManager.getPeerMigratedTo(+newValue) || +newValue;
|
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();
|
this.update();
|
||||||
} else if(name === 'peer-title') {
|
} else if(name === 'peer-title') {
|
||||||
this.peerTitle = newValue;
|
this.peerTitle = newValue;
|
||||||
} else if(name === 'dialog') {
|
} else if(name === 'dialog') {
|
||||||
this.isDialog = !!+newValue;
|
this.isDialog = newValue === '1';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public update() {
|
public update() {
|
||||||
/* if(this.lazyLoadQueue) {
|
if(this.lazyLoadQueue) {
|
||||||
if(this.addedToQueue) return;
|
if(!seen.has(this.peerId)) {
|
||||||
this.lazyLoadQueue.push({
|
if(this.addedToQueue) return;
|
||||||
div: this,
|
this.addedToQueue = true;
|
||||||
load: () => {
|
|
||||||
return appProfileManager.putPhoto(this, this.peerId, this.isDialog, this.peerTitle).finally(() => {
|
let set = believeMe.get(this.peerId);
|
||||||
this.addedToQueue = false;
|
if(!set) {
|
||||||
});
|
set = new Set();
|
||||||
|
believeMe.set(this.peerId, set);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
this.addedToQueue = true;
|
set.add(this);
|
||||||
} else { */
|
|
||||||
const res = appProfileManager.putPhoto(this, this.peerId, this.isDialog, this.peerTitle);
|
this.lazyLoadQueue.push({
|
||||||
if(this.loadPromises && res && res.cached) {
|
div: this,
|
||||||
this.loadPromises.push(res.loadPromise);
|
load: () => {
|
||||||
res.loadPromise.finally(() => {
|
seen.add(this.peerId);
|
||||||
this.loadPromises = undefined;
|
return this.update();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else if(this.addedToQueue) {
|
||||||
|
this.lazyLoadQueue.unobserve(this);
|
||||||
}
|
}
|
||||||
//}
|
}
|
||||||
|
|
||||||
|
seen.add(this.peerId);
|
||||||
|
|
||||||
|
const res = appProfileManager.putPhoto(this, this.peerId, this.isDialog, this.peerTitle);
|
||||||
|
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);
|
||||||
|
@ -2560,7 +2560,8 @@ export default class ChatBubbles {
|
|||||||
bubbleContainer,
|
bubbleContainer,
|
||||||
message: messageWithReplies,
|
message: messageWithReplies,
|
||||||
messageDiv,
|
messageDiv,
|
||||||
loadPromises
|
loadPromises,
|
||||||
|
lazyLoadQueue: this.lazyLoadQueue
|
||||||
});
|
});
|
||||||
|
|
||||||
if(isFooter) {
|
if(isFooter) {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
import { getFullDate } from "../../helpers/date";
|
import { getFullDate } from "../../helpers/date";
|
||||||
import { formatNumber } from "../../helpers/number";
|
import { formatNumber } from "../../helpers/number";
|
||||||
import RichTextProcessor from "../../lib/richtextprocessor";
|
import RichTextProcessor from "../../lib/richtextprocessor";
|
||||||
|
import { LazyLoadQueueIntersector } from "../lazyLoadQueue";
|
||||||
import { wrapReply } from "../wrappers";
|
import { wrapReply } from "../wrappers";
|
||||||
import Chat from "./chat";
|
import Chat from "./chat";
|
||||||
import RepliesElement from "./replies";
|
import RepliesElement from "./replies";
|
||||||
@ -65,18 +66,20 @@ export namespace MessageRender {
|
|||||||
return timeSpan;
|
return timeSpan;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const renderReplies = ({bubble, bubbleContainer, message, messageDiv, loadPromises}: {
|
export const renderReplies = ({bubble, bubbleContainer, message, messageDiv, loadPromises, lazyLoadQueue}: {
|
||||||
bubble: HTMLElement,
|
bubble: HTMLElement,
|
||||||
bubbleContainer: HTMLElement,
|
bubbleContainer: HTMLElement,
|
||||||
message: any,
|
message: any,
|
||||||
messageDiv: HTMLElement,
|
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 isFooter = !bubble.classList.contains('sticker') && !bubble.classList.contains('emoji-big') && !bubble.classList.contains('round');
|
||||||
const repliesFooter = new RepliesElement();
|
const repliesFooter = new RepliesElement();
|
||||||
repliesFooter.message = message;
|
repliesFooter.message = message;
|
||||||
repliesFooter.type = isFooter ? 'footer' : 'beside';
|
repliesFooter.type = isFooter ? 'footer' : 'beside';
|
||||||
repliesFooter.loadPromises = loadPromises;
|
repliesFooter.loadPromises = loadPromises;
|
||||||
|
repliesFooter.lazyLoadQueue = lazyLoadQueue;
|
||||||
repliesFooter.init();
|
repliesFooter.init();
|
||||||
bubbleContainer.prepend(repliesFooter);
|
bubbleContainer.prepend(repliesFooter);
|
||||||
return isFooter;
|
return isFooter;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { LazyLoadQueueIntersector } from "../lazyLoadQueue";
|
||||||
import { formatNumber } from "../../helpers/number";
|
import { formatNumber } from "../../helpers/number";
|
||||||
import { Message } from "../../layer";
|
import { Message } from "../../layer";
|
||||||
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
|
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
|
||||||
@ -26,6 +27,7 @@ export default class RepliesElement extends HTMLElement {
|
|||||||
public message: Message.message;
|
public message: Message.message;
|
||||||
public type: 'footer' | 'beside';
|
public type: 'footer' | 'beside';
|
||||||
public loadPromises: Promise<any>[];
|
public loadPromises: Promise<any>[];
|
||||||
|
public lazyLoadQueue: LazyLoadQueueIntersector;
|
||||||
|
|
||||||
private updated = false;
|
private updated = false;
|
||||||
|
|
||||||
@ -69,6 +71,7 @@ export default class RepliesElement extends HTMLElement {
|
|||||||
avatarElem = new AvatarElement();
|
avatarElem = new AvatarElement();
|
||||||
avatarElem.setAttribute('dialog', '0');
|
avatarElem.setAttribute('dialog', '0');
|
||||||
avatarElem.classList.add('avatar-30');
|
avatarElem.classList.add('avatar-30');
|
||||||
|
avatarElem.lazyLoadQueue = this.lazyLoadQueue;
|
||||||
|
|
||||||
if(this.loadPromises) {
|
if(this.loadPromises) {
|
||||||
avatarElem.loadPromises = this.loadPromises;
|
avatarElem.loadPromises = this.loadPromises;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { LazyLoadQueueIntersector } from "./lazyLoadQueue";
|
||||||
import appDialogsManager, { DialogDom } from "../lib/appManagers/appDialogsManager";
|
import appDialogsManager, { DialogDom } from "../lib/appManagers/appDialogsManager";
|
||||||
import { isInDOM, positionElementByIndex, replaceContent } from "../helpers/dom";
|
import { isInDOM, positionElementByIndex, replaceContent } from "../helpers/dom";
|
||||||
import { getHeavyAnimationPromise } from "../hooks/useHeavyAnimationCheck";
|
import { getHeavyAnimationPromise } from "../hooks/useHeavyAnimationCheck";
|
||||||
@ -20,6 +21,7 @@ export default class SortedUserList {
|
|||||||
public list: HTMLUListElement;
|
public list: HTMLUListElement;
|
||||||
public users: Map<number, SortedUser>;
|
public users: Map<number, SortedUser>;
|
||||||
public sorted: Array<SortedUser>;
|
public sorted: Array<SortedUser>;
|
||||||
|
public lazyLoadQueue: LazyLoadQueueIntersector;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.list = appDialogsManager.createChatList();
|
this.list = appDialogsManager.createChatList();
|
||||||
@ -75,7 +77,8 @@ export default class SortedUserList {
|
|||||||
avatarSize: 48,
|
avatarSize: 48,
|
||||||
autonomous: true,
|
autonomous: true,
|
||||||
meAsSaved: false,
|
meAsSaved: false,
|
||||||
rippleEnabled: false
|
rippleEnabled: false,
|
||||||
|
lazyLoadQueue: this.lazyLoadQueue
|
||||||
});
|
});
|
||||||
|
|
||||||
const sortedUser: SortedUser = {
|
const sortedUser: SortedUser = {
|
||||||
|
@ -596,7 +596,7 @@ export class ApiUpdatesManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public saveUpdate(update: Update) {
|
public saveUpdate(update: Update) {
|
||||||
this.debug && this.log('saveUpdate', update);
|
//this.debug && this.log('saveUpdate', update);
|
||||||
rootScope.dispatchEvent(update._, update as any);
|
rootScope.dispatchEvent(update._, update as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ import appNotificationsManager from "./appNotificationsManager";
|
|||||||
import PeerTitle from "../../components/peerTitle";
|
import PeerTitle from "../../components/peerTitle";
|
||||||
import { i18n } from "../langPack";
|
import { i18n } from "../langPack";
|
||||||
import findUpTag from "../../helpers/dom/findUpTag";
|
import findUpTag from "../../helpers/dom/findUpTag";
|
||||||
|
import { LazyLoadQueueIntersector } from "../../components/lazyLoadQueue";
|
||||||
|
|
||||||
export type DialogDom = {
|
export type DialogDom = {
|
||||||
avatarEl: AvatarElement,
|
avatarEl: AvatarElement,
|
||||||
@ -1213,12 +1214,13 @@ export class AppDialogsManager {
|
|||||||
meAsSaved?: boolean,
|
meAsSaved?: boolean,
|
||||||
append?: boolean,
|
append?: boolean,
|
||||||
avatarSize?: number,
|
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;
|
let dialog: Dialog;
|
||||||
|
|
||||||
if(typeof(_dialog) === 'number') {
|
if(typeof(_dialog) === 'number') {
|
||||||
@ -1248,6 +1250,7 @@ export class AppDialogsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const avatarEl = new AvatarElement();
|
const avatarEl = new AvatarElement();
|
||||||
|
avatarEl.lazyLoadQueue = lazyLoadQueue;
|
||||||
avatarEl.setAttribute('dialog', meAsSaved ? '1' : '0');
|
avatarEl.setAttribute('dialog', meAsSaved ? '1' : '0');
|
||||||
avatarEl.setAttribute('peer', '' + peerId);
|
avatarEl.setAttribute('peer', '' + peerId);
|
||||||
avatarEl.classList.add('dialog-avatar', 'avatar-' + avatarSize);
|
avatarEl.classList.add('dialog-avatar', 'avatar-' + avatarSize);
|
||||||
|
Loading…
Reference in New Issue
Block a user