/* * https://github.com/morethanwords/tweb * Copyright (C) 2019-2021 Eduard Kuzmenko * https://github.com/morethanwords/tweb/blob/master/LICENSE */ import rootScope from '../lib/rootScope'; import {Message, Photo} from '../layer'; import type LazyLoadQueue from './lazyLoadQueue'; import {attachClickEvent} from '../helpers/dom/clickEvent'; import cancelEvent from '../helpers/dom/cancelEvent'; import AppMediaViewer from './appMediaViewer'; import AppMediaViewerAvatar from './appMediaViewerAvatar'; import isObject from '../helpers/object/isObject'; import {ArgumentTypes} from '../types'; import putPhoto from './putPhoto'; import {recordPromise} from '../helpers/recordPromise'; const onAvatarUpdate = (peerId: PeerId) => { (Array.from(document.querySelectorAll('avatar-element[data-peer-id="' + peerId + '"]')) as AvatarElement[]).forEach((elem) => { // console.log('updating avatar:', elem); elem.update(); }); }; rootScope.addEventListener('avatar_update', onAvatarUpdate); rootScope.addEventListener('peer_title_edit', async(peerId) => { if(!(await rootScope.managers.appAvatarsManager.isAvatarCached(peerId))) { onAvatarUpdate(peerId); } }); export async function openAvatarViewer( target: HTMLElement, peerId: PeerId, middleware: () => boolean, message?: any, prevTargets?: {element: HTMLElement, item: Photo.photo['id'] | Message.messageService}[], nextTargets?: typeof prevTargets ) { let photo = await rootScope.managers.appProfileManager.getFullPhoto(peerId); if(!middleware() || !photo) { return; } const getTarget = () => { const good = Array.from(target.querySelectorAll('img')).find((img) => !img.classList.contains('emoji')); return good ? target : null; }; if(peerId.isAnyChat()) { const hadMessage = !!message; const inputFilter = 'inputMessagesFilterChatPhotos'; if(!message) { message = await rootScope.managers.appMessagesManager.getSearch({ peerId, inputFilter: {_: inputFilter}, maxId: 0, limit: 1 }).then((value) => { // console.log(lol); // ! by descend return value.history[0]; }); if(!middleware()) { return; } } if(message) { // ! гений в деле, костылируем (но это гениально) const messagePhoto = message.action.photo; if(messagePhoto.id !== photo.id) { if(!hadMessage) { message = rootScope.managers.appMessagesManager.generateFakeAvatarMessage(peerId, photo); } else { } } const f = (arr: typeof prevTargets) => arr.map((el) => ({ element: el.element, mid: (el.item as Message.messageService).mid, peerId: (el.item as Message.messageService).peerId })); new AppMediaViewer() .setSearchContext({ peerId, inputFilter: {_: inputFilter} }) .openMedia(message, getTarget(), undefined, undefined, prevTargets ? f(prevTargets) : undefined, nextTargets ? f(nextTargets) : undefined); return; } } if(photo) { if(!isObject(message) && message) { photo = await rootScope.managers.appPhotosManager.getPhoto(message); } const f = (arr: typeof prevTargets) => arr.map((el) => ({ element: el.element, photoId: el.item as string })); new AppMediaViewerAvatar(peerId).openMedia( photo.id, getTarget(), undefined, prevTargets ? f(prevTargets) : undefined, nextTargets ? f(nextTargets) : undefined ); } } const believeMe: Map> = new Map(); const seen: Set = new Set(); export default class AvatarElement extends HTMLElement { public peerId: PeerId; public isDialog: boolean; public peerTitle: string; public loadPromises: Promise[]; public lazyLoadQueue: LazyLoadQueue; public isBig: boolean; private addedToQueue = false; 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); } } public attachClickEvent() { let loading = false; attachClickEvent(this, async(e) => { cancelEvent(e); if(loading) return; // console.log('avatar clicked'); const peerId = this.peerId; loading = true; await openAvatarViewer(this, this.peerId, () => this.peerId === peerId); loading = false; }); } public updateOptions(options: Partial[0]>) { for(const i in options) { // @ts-ignore this[i] = options[i]; } } public updateWithOptions(options: { peerId: PeerId, isDialog?: boolean, isBig?: boolean, peerTitle?: string, lazyLoadQueue?: LazyLoadQueue, loadPromises?: Promise[] }) { const wasPeerId = this.peerId; this.updateOptions(options); const newPeerId = this.peerId; if(wasPeerId === newPeerId) { return; } this.peerId = /* rootScope.managers.appPeersManager.getPeerMigratedTo(newPeerId) || */newPeerId; this.dataset.peerId = '' + newPeerId; if(wasPeerId) { const set = believeMe.get(wasPeerId); if(set) { set.delete(this); if(!set.size) { believeMe.delete(wasPeerId); } } } return this.update(); } private r(onlyThumb = false) { const promise = putPhoto(this, this.peerId, this.isDialog, this.peerTitle, onlyThumb, this.isBig); // recordPromise(promise, 'avatar putPhoto-' + this.peerId); if(this.loadPromises) { this.loadPromises.push(promise); promise.finally(() => { this.loadPromises = undefined; }); } return promise; } public update() { 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: () => { seen.add(this.peerId); return this.update(); } }); return this.r(true); } else if(this.addedToQueue) { this.lazyLoadQueue.unobserve(this); } } seen.add(this.peerId); const promise = this.r(); 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);