tweb/src/components/chat/sendAs.ts

342 lines
10 KiB
TypeScript

/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import indexOfAndSplice from '../../helpers/array/indexOfAndSplice';
import callbackify from '../../helpers/callbackify';
import ListenerSetter from '../../helpers/listenerSetter';
import liteMode from '../../helpers/liteMode';
import {getMiddleware} from '../../helpers/middleware';
import {modifyAckedPromise} from '../../helpers/modifyAckedResult';
import {ChatFull} from '../../layer';
import {AppManagers} from '../../lib/appManagers/managers';
import getPeerId from '../../lib/appManagers/utils/peers/getPeerId';
import {i18n} from '../../lib/langPack';
import {AckedResult} from '../../lib/mtproto/superMessagePort';
import rootScope from '../../lib/rootScope';
import AvatarElement from '../avatar';
import {ButtonMenuItemOptions, ButtonMenuSync} from '../buttonMenu';
import ButtonMenuToggle from '../buttonMenuToggle';
import PeerTitle from '../peerTitle';
import SetTransition from '../singleTransition';
import getChatMembersString from '../wrappers/getChatMembersString';
const SEND_AS_ANIMATION_DURATION = 300;
export default class ChatSendAs {
private avatar: AvatarElement;
private container: HTMLElement;
private closeBtn: HTMLElement;
private btnMenu: HTMLElement;
private sendAsPeerIds: PeerId[];
private sendAsPeerId: PeerId;
private updatingPromise: ReturnType<ChatSendAs['updateManual']>;
private middleware: ReturnType<typeof getMiddleware>;
private listenerSetter: ListenerSetter;
private peerId: PeerId;
private addedListener: boolean;
private buttons: ButtonMenuItemOptions[];
constructor(
private managers: AppManagers,
private onReady: (container: HTMLElement, skipAnimation?: boolean) => void,
private onChange: (sendAsPeerId: PeerId) => void
) {
this.middleware = getMiddleware();
this.listenerSetter = new ListenerSetter();
this.construct();
}
private construct() {
this.container = document.createElement('div');
this.container.classList.add('new-message-send-as-container');
this.closeBtn = document.createElement('div');
this.closeBtn.classList.add('new-message-send-as-close', 'new-message-send-as-avatar', 'tgico-close');
const sendAsButtons: ButtonMenuItemOptions[] = [{
text: 'SendMessageAsTitle',
onClick: undefined
}];
this.buttons = [];
let previousAvatar: HTMLElement;
const onSendAsMenuToggle = (visible: boolean) => {
if(visible) {
previousAvatar = this.avatar;
}
const isChanged = this.avatar !== previousAvatar;
const useRafs = !visible && isChanged ? 2 : 0;
SetTransition({
element: this.closeBtn,
className: 'is-visible',
forwards: visible,
duration: SEND_AS_ANIMATION_DURATION,
useRafs
});
if(!isChanged) {
SetTransition({
element: previousAvatar,
className: 'is-visible',
forwards: !visible,
duration: SEND_AS_ANIMATION_DURATION,
useRafs
});
}
};
ButtonMenuToggle({
buttonOptions: {noRipple: true},
listenerSetter: this.listenerSetter,
container: this.container,
direction: 'top-right',
buttons: sendAsButtons,
onOpenBefore: () => {
onSendAsMenuToggle(true);
},
onOpen: (e, btnMenu) => {
sendAsButtons[0].element.classList.add('btn-menu-item-header');
this.btnMenu = btnMenu as any;
this.btnMenu.classList.add('scrollable', 'scrollable-y');
this.btnMenu.append(...this.buttons.map((button) => button.element));
},
onClose: () => {
onSendAsMenuToggle(false);
},
onCloseAfter: () => {
this.btnMenu = undefined;
}
});
this.container.append(this.closeBtn);
}
private async updateButtons(peerIds: PeerId[]) {
const promises: Promise<ButtonMenuItemOptions>[] = peerIds.map(async(sendAsPeerId, idx) => {
const textElement = document.createElement('div');
const subtitle = document.createElement('div');
subtitle.classList.add('btn-menu-item-subtitle');
if(sendAsPeerId.isUser()) {
subtitle.append(i18n('Chat.SendAs.PersonalAccount'));
} else if(sendAsPeerId === this.peerId) {
subtitle.append(i18n('VoiceChat.DiscussionGroup'));
} else {
subtitle.append(await getChatMembersString(sendAsPeerId.toChatId()));
}
textElement.append(
new PeerTitle({peerId: sendAsPeerId}).element,
subtitle
);
return {
onClick: idx ? async() => {
const currentPeerId = this.peerId;
this.changeSendAsPeerId(sendAsPeerId);
const middleware = this.middleware.get();
const executeButtonsUpdate = () => {
if(this.sendAsPeerId !== sendAsPeerId || !middleware()) return;
const peerIds = this.sendAsPeerIds.slice();
indexOfAndSplice(peerIds, sendAsPeerId);
peerIds.unshift(sendAsPeerId);
this.updateButtons(peerIds);
};
if(liteMode.isAvailable('animations')) {
setTimeout(executeButtonsUpdate, 250);
} else {
executeButtonsUpdate();
}
// return;
this.managers.appMessagesManager.saveDefaultSendAs(currentPeerId, sendAsPeerId);
} : undefined,
textElement
};
});
const buttons = await Promise.all(promises);
const btnMenu = ButtonMenuSync({buttons}/* , this.listenerSetter */);
buttons.forEach((button, idx) => {
const peerId = peerIds[idx];
const avatar = new AvatarElement();
avatar.classList.add('avatar-26', 'btn-menu-item-icon');
avatar.updateWithOptions({peerId});
if(!idx) {
avatar.classList.add('active');
}
button.element.prepend(avatar);
});
this.buttons = buttons;
// if already opened
this.btnMenu?.append(...this.buttons.map((button) => button.element));
}
private async updateAvatar(sendAsPeerId: PeerId, skipAnimation?: boolean) {
const previousAvatar = this.avatar;
if(previousAvatar) {
if(previousAvatar.peerId === sendAsPeerId) {
return;
}
}
if(!previousAvatar) {
skipAnimation = true;
}
const useRafs = skipAnimation ? 0 : 2;
const duration = skipAnimation ? 0 : SEND_AS_ANIMATION_DURATION;
const avatar = this.avatar = new AvatarElement();
avatar.classList.add('new-message-send-as-avatar', 'avatar-30');
await avatar.updateWithOptions({
isDialog: false,
peerId: sendAsPeerId
});
SetTransition({
element: avatar,
className: 'is-visible',
forwards: true,
duration,
useRafs
});
if(previousAvatar) {
SetTransition({
element: previousAvatar,
className: 'is-visible',
forwards: false,
duration,
onTransitionEnd: () => {
previousAvatar.remove();
},
useRafs
});
}
this.container.append(avatar);
}
private changeSendAsPeerId(sendAsPeerId: PeerId, skipAnimation?: boolean) {
this.sendAsPeerId = sendAsPeerId;
this.onChange(sendAsPeerId);
return this.updateAvatar(sendAsPeerId, skipAnimation);
}
private getDefaultSendAs(): Promise<AckedResult<PeerId>> {
// return rootScope.myId;
return this.managers.acknowledged.appProfileManager.getChannelFull(this.peerId.toChatId()).then((acked) => {
return {
cached: acked.cached,
result: acked.result.then((channelFull) => {
return channelFull.default_send_as ? getPeerId(channelFull.default_send_as) : undefined
})
};
});
}
public async updateManual(skipAnimation?: boolean): Promise<() => void> {
const peerId = this.peerId;
if(this.updatingPromise || !(await this.managers.appPeersManager.isChannel(peerId))) {
return;
}
const middleware = this.middleware.get(() => {
return !this.updatingPromise || this.updatingPromise === updatingPromise;
});
const {container} = this;
const chatId = peerId.toChatId();
const result = (await modifyAckedPromise(this.getDefaultSendAs())).result;
// const result = Promise.resolve(this.getDefaultSendAs());
const wasSkippingAnimation = skipAnimation;
if(result instanceof Promise) {
skipAnimation = undefined;
}
const auto = wasSkippingAnimation && !skipAnimation;
const updatingPromise = this.updatingPromise = callbackify(result, async(sendAsPeerId) => {
if(!middleware() || sendAsPeerId === undefined) return;
await this.changeSendAsPeerId(sendAsPeerId, skipAnimation);
if(!middleware()) return;
this.managers.appChatsManager.getSendAs(chatId).then((sendAsPeers) => {
if(!middleware()) return;
const peers = sendAsPeers.filter((sendAsPeer) => !sendAsPeer.pFlags.premium_required).map((sendAsPeer) => sendAsPeer.peer);
const peerIds = peers.map((peer) => getPeerId(peer));
this.sendAsPeerIds = peerIds.slice();
indexOfAndSplice(peerIds, sendAsPeerId);
peerIds.unshift(sendAsPeerId);
this.updateButtons(peerIds);
});
const callback = () => {
this.onReady(container, skipAnimation);
if(!this.addedListener) {
this.listenerSetter.add(rootScope)('peer_full_update', (peerId) => {
if(this.peerId === peerId) {
this.update();
}
});
this.addedListener = true;
}
};
if(auto) {
callback();
return;
}
return callback;
});
updatingPromise.finally(() => {
if(this.updatingPromise === updatingPromise) {
this.updatingPromise = undefined;
}
});
if(!auto) {
return updatingPromise;
}
}
public update(skipAnimation?: boolean) {
return this.updateManual(skipAnimation).then((callback) => callback?.());
}
public setPeerId(peerId?: PeerId) {
/* if(this.avatar) {
this.avatar.remove();
this.avatar = undefined;
} */
this.middleware.clean();
this.updatingPromise = undefined;
this.peerId = peerId;
}
public destroy() {
this.container.remove();
this.setPeerId();
this.listenerSetter.removeAll();
}
}