Telegram Web K with changes to work inside I2P https://web.telegram.i2p/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

224 lines
7.2 KiB

/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import callbackify from '../../helpers/callbackify';
import formatNumber from '../../helpers/number/formatNumber';
import {fastRaf} from '../../helpers/schedulers';
import {MessagePeerReaction, ReactionCount} from '../../layer';
import {AppManagers} from '../../lib/appManagers/managers';
import getPeerId from '../../lib/appManagers/utils/peers/getPeerId';
import RLottiePlayer from '../../lib/rlottie/rlottiePlayer';
import rootScope from '../../lib/rootScope';
import SetTransition from '../singleTransition';
import StackedAvatars from '../stackedAvatars';
import {wrapSticker, wrapStickerAnimation} from '../wrappers';
import {Awaited} from '../../types';
const CLASS_NAME = 'reaction';
const TAG_NAME = CLASS_NAME + '-element';
const REACTION_INLINE_SIZE = 14;
const REACTION_BLOCK_SIZE = 22;
export const REACTION_DISPLAY_INLINE_COUNTER_AT = 2;
export const REACTION_DISPLAY_BLOCK_COUNTER_AT = 4;
export type ReactionLayoutType = 'block' | 'inline';
export default class ReactionElement extends HTMLElement {
private type: ReactionLayoutType;
private counter: HTMLElement;
private stickerContainer: HTMLElement;
private stackedAvatars: StackedAvatars;
private canRenderAvatars: boolean;
private _reactionCount: ReactionCount;
private wrapStickerPromise: Awaited<ReturnType<typeof wrapSticker>>['render'];
private managers: AppManagers;
constructor() {
super();
this.classList.add(CLASS_NAME);
this.managers = rootScope.managers;
}
public get reactionCount() {
return this._reactionCount;
}
public set reactionCount(reactionCount: ReactionCount) {
this._reactionCount = reactionCount;
}
public get count() {
return this.reactionCount.count;
}
public init(type: ReactionLayoutType) {
this.type = type;
this.classList.add(CLASS_NAME + '-' + type);
}
public setCanRenderAvatars(canRenderAvatars: boolean) {
this.canRenderAvatars = canRenderAvatars;
}
public render(doNotRenderSticker?: boolean) {
const hadStickerContainer = !!this.stickerContainer;
if(!hadStickerContainer) {
this.stickerContainer = document.createElement('div');
this.stickerContainer.classList.add(CLASS_NAME + '-sticker');
this.append(this.stickerContainer);
}
const reactionCount = this.reactionCount;
if(!doNotRenderSticker && !hadStickerContainer) {
const availableReaction = this.managers.appReactionsManager.getReaction(reactionCount.reaction);
callbackify(availableReaction, (availableReaction) => {
if(!availableReaction.center_icon) {
this.stickerContainer.classList.add('is-static');
}
if(availableReaction.pFlags.inactive) {
this.classList.add('is-inactive');
}
const size = this.type === 'inline' ? REACTION_INLINE_SIZE : REACTION_BLOCK_SIZE;
const wrapPromise = this.wrapStickerPromise = wrapSticker({
div: this.stickerContainer,
doc: availableReaction.center_icon ?? availableReaction.static_icon,
width: size,
height: size,
static: true,
managers: this.managers
}).then(({render}) => render).finally(() => {
if(this.wrapStickerPromise === wrapPromise) {
this.wrapStickerPromise = undefined;
}
});
});
}
}
public renderCounter() {
const reactionCount = this.reactionCount;
const displayOn = this.type === 'inline' ? REACTION_DISPLAY_INLINE_COUNTER_AT : REACTION_DISPLAY_BLOCK_COUNTER_AT;
if(reactionCount.count >= displayOn || (this.type === 'block' && !this.canRenderAvatars)) {
if(!this.counter) {
this.counter = document.createElement(this.type === 'inline' ? 'i' : 'span');
this.counter.classList.add(CLASS_NAME + '-counter');
}
const formatted = formatNumber(reactionCount.count);
if(this.counter.textContent !== formatted) {
this.counter.textContent = formatted;
}
if(!this.counter.parentElement) {
this.append(this.counter);
}
} else if(this.counter?.parentElement) {
this.counter.remove();
this.counter = undefined;
}
}
public renderAvatars(recentReactions: MessagePeerReaction[]) {
if(this.type === 'inline') {
return;
}
if(this.reactionCount.count >= REACTION_DISPLAY_BLOCK_COUNTER_AT || !this.canRenderAvatars) {
if(this.stackedAvatars) {
this.stackedAvatars.container.remove();
this.stackedAvatars = undefined;
}
return;
}
if(!this.stackedAvatars) {
this.stackedAvatars = new StackedAvatars({
avatarSize: 24
});
this.append(this.stackedAvatars.container);
}
this.stackedAvatars.render(recentReactions.map((reaction) => getPeerId(reaction.peer_id)));
}
public setIsChosen(isChosen = !!this.reactionCount.pFlags.chosen) {
if(this.type === 'inline') return;
const wasChosen = this.classList.contains('is-chosen') && !this.classList.contains('backwards');
if(wasChosen !== isChosen) {
SetTransition(this, 'is-chosen', isChosen, this.isConnected ? 300 : 0);
}
}
public fireAroundAnimation() {
callbackify(this.managers.appReactionsManager.getReaction(this.reactionCount.reaction), (availableReaction) => {
const size = this.type === 'inline' ? REACTION_INLINE_SIZE + 14 : REACTION_BLOCK_SIZE + 18;
const div = document.createElement('div');
div.classList.add(CLASS_NAME + '-sticker-activate');
Promise.all([
wrapSticker({
div: div,
doc: availableReaction.center_icon,
width: size,
height: size,
withThumb: false,
needUpscale: true,
play: false,
skipRatio: 1,
group: 'none',
needFadeIn: false,
managers: this.managers
}).then(({render}) => render as Promise<RLottiePlayer>),
wrapStickerAnimation({
doc: availableReaction.around_animation,
size: 80,
target: this.stickerContainer,
side: 'center',
skipRatio: 1,
play: false,
managers: this.managers
}).stickerPromise
]).then(([iconPlayer, aroundPlayer]) => {
const remove = () => {
// if(!isInDOM(div)) return;
fastRaf(() => {
// if(!isInDOM(div)) return;
iconPlayer.remove();
div.remove();
this.stickerContainer.classList.remove('has-animation');
});
};
iconPlayer.addEventListener('enterFrame', (frameNo) => {
if(frameNo === iconPlayer.maxFrame) {
if(this.wrapStickerPromise) { // wait for fade in animation
this.wrapStickerPromise.then(() => {
setTimeout(remove, 1e3);
});
} else {
remove();
}
}
});
iconPlayer.addEventListener('firstFrame', () => {
this.stickerContainer.append(div);
this.stickerContainer.classList.add('has-animation');
iconPlayer.play();
aroundPlayer.play();
}, {once: true});
});
});
}
}
customElements.define(TAG_NAME, ReactionElement);