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.
217 lines
6.9 KiB
217 lines
6.9 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 appPeersManager from "../../lib/appManagers/appPeersManager"; |
|
import appReactionsManager from "../../lib/appManagers/appReactionsManager"; |
|
import RLottiePlayer from "../../lib/rlottie/rlottiePlayer"; |
|
import SetTransition from "../singleTransition"; |
|
import StackedAvatars from "../stackedAvatars"; |
|
import { wrapSticker, wrapStickerAnimation } from "../wrappers"; |
|
|
|
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: ReturnType<typeof wrapSticker>; |
|
|
|
constructor() { |
|
super(); |
|
this.classList.add(CLASS_NAME); |
|
} |
|
|
|
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 = 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 |
|
}).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 => appPeersManager.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(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 |
|
}) as Promise<RLottiePlayer>, |
|
|
|
wrapStickerAnimation({ |
|
doc: availableReaction.around_animation, |
|
size: 80, |
|
target: this.stickerContainer, |
|
side: 'center', |
|
skipRatio: 1, |
|
play: false |
|
}).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);
|
|
|