|
|
|
/*
|
|
|
|
* 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<PeerId, Set<AvatarElement>> = new Map();
|
|
|
|
const seen: Set<PeerId> = new Set();
|
|
|
|
|
|
|
|
export default class AvatarElement extends HTMLElement {
|
|
|
|
public peerId: PeerId;
|
|
|
|
public isDialog: boolean;
|
|
|
|
public peerTitle: string;
|
|
|
|
public loadPromises: Promise<any>[];
|
|
|
|
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<ArgumentTypes<AvatarElement['updateWithOptions']>[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<any>[]
|
|
|
|
}) {
|
|
|
|
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);
|