import appChatsManager from "../../lib/appManagers/appChatsManager"; import appImManager from "../../lib/appManagers/appImManager"; import appMessagesManager from "../../lib/appManagers/appMessagesManager"; import appPeersManager from "../../lib/appManagers/appPeersManager"; import appPollsManager, { Poll } from "../../lib/appManagers/appPollsManager"; import $rootScope from "../../lib/rootScope"; import { findUpClassName } from "../../lib/utils"; import ButtonMenu, { ButtonMenuItemOptions } from "../buttonMenu"; import { attachContextMenuListener, openBtnMenu, positionMenu } from "../misc"; import { PopupButton, PopupPeer } from "../popup"; import appSidebarRight from "../sidebarRight"; export class ChatContextMenu { private buttons: (ButtonMenuItemOptions & {verify: (peerID: number, msgID: number, target: HTMLElement) => boolean})[]; private element: HTMLElement; private target: HTMLElement; public peerID: number; public msgID: number; constructor(private attachTo: HTMLElement) { attachContextMenuListener(attachTo, (e) => { if(this.init) { this.init(); this.init = null; } let bubble: HTMLElement = null; try { bubble = findUpClassName(e.target, 'bubble__container'); } catch(e) {} if(!bubble) return; if(e instanceof MouseEvent) e.preventDefault(); if(this.element.classList.contains('active')) { return false; } if(e instanceof MouseEvent) e.cancelBubble = true; bubble = bubble.parentElement as HTMLDivElement; // bc container const msgID = +bubble.dataset.mid; if(!msgID) return; this.peerID = $rootScope.selectedPeerID; this.msgID = msgID; this.target = e.target as HTMLElement; this.buttons.forEach(button => { const good = button.verify(this.peerID, this.msgID, this.target); button.element.classList.toggle('hide', !good); }); const side: 'left' | 'right' = bubble.classList.contains('is-in') ? 'left' : 'right'; positionMenu(e, this.element, side); openBtnMenu(this.element, () => { this.peerID = this.msgID = 0; this.target = null; }); /////this.log('contextmenu', e, bubble, msgID, side); }); } private init = () => { this.buttons = [{ icon: 'reply', text: 'Reply', onClick: this.onReplyClick, verify: (peerID: number) => peerID > 0 || appChatsManager.hasRights(-peerID, 'send') }, { icon: 'edit', text: 'Edit', onClick: this.onEditClick, verify: (peerID: number, msgID: number) => appMessagesManager.canEditMessage(msgID, 'text') }, { icon: 'copy', text: 'Copy', onClick: this.onCopyClick, verify: (peerID: number, msgID: number) => !!appMessagesManager.getMessage(msgID).message }, { icon: 'pin', text: 'Pin', onClick: this.onPinClick, verify: (peerID: number, msgID: number) => { const message = appMessagesManager.getMessage(msgID); return message._ != 'messageService' && appImManager.pinnedMsgID != msgID && (peerID == $rootScope.myID || (peerID < 0 && appChatsManager.hasRights(-peerID, 'pin'))); } }, { icon: 'unpin', text: 'Unpin', onClick: this.onUnpinClick, verify: (peerID: number, msgID: number) => appImManager.pinnedMsgID == msgID && (peerID == $rootScope.myID || (peerID < 0 && appChatsManager.hasRights(-peerID, 'pin'))) }, { icon: 'revote', text: 'Revote', onClick: this.onRetractVote, verify: (peerID: number, msgID) => { const message = appMessagesManager.getMessage(msgID); const poll = message.media?.poll as Poll; return poll && poll.chosenIndexes.length && !poll.pFlags.closed && !poll.pFlags.quiz; } }, { icon: 'lock', text: 'Stop poll', onClick: this.onStopPoll, verify: (peerID: number, msgID) => { const message = appMessagesManager.getMessage(msgID); const poll = message.media?.poll; return appMessagesManager.canEditMessage(msgID, 'poll') && poll && !poll.pFlags.closed; } }, { icon: 'forward', text: 'Forward', onClick: this.onForwardClick, verify: () => true }, { icon: 'delete danger', text: 'Delete', onClick: this.onDeleteClick, verify: (peerID: number, msgID: number) => peerID > 0 || appMessagesManager.getMessage(msgID).fromID == $rootScope.myID || appChatsManager.hasRights(-peerID, 'deleteRevoke') }]; this.element = ButtonMenu(this.buttons); this.element.id = 'bubble-contextmenu'; appImManager.chatInput.parentElement.insertBefore(this.element, appImManager.chatInput); }; private onReplyClick = () => { const message = appMessagesManager.getMessage(this.msgID); const chatInputC = appImManager.chatInputC; chatInputC.setTopInfo(appPeersManager.getPeerTitle(message.fromID, true), message.message, undefined, message); chatInputC.replyToMsgID = this.msgID; chatInputC.editMsgID = 0; }; private onEditClick = () => { const message = appMessagesManager.getMessage(this.msgID); const chatInputC = appImManager.chatInputC; chatInputC.setTopInfo('Editing', message.message, message.message, message); chatInputC.replyToMsgID = 0; chatInputC.editMsgID = this.msgID; }; private onCopyClick = () => { let message = appMessagesManager.getMessage(this.msgID); let str = message ? message.message : ''; var textArea = document.createElement("textarea"); textArea.value = str; textArea.style.position = "fixed"; //avoid scrolling to bottom document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { document.execCommand('copy'); } catch (err) { console.error('Oops, unable to copy', err); } document.body.removeChild(textArea); }; private onPinClick = () => { appMessagesManager.updatePinnedMessage($rootScope.selectedPeerID, this.msgID); }; private onUnpinClick = () => { appMessagesManager.updatePinnedMessage($rootScope.selectedPeerID, 0); }; private onRetractVote = () => { appPollsManager.sendVote(this.msgID, []); }; private onStopPoll = () => { appPollsManager.stopPoll(this.msgID); }; private onForwardClick = () => { let msgID: number; const albumItem = findUpClassName(this.target, 'album-item'); if(albumItem) { msgID = +albumItem.dataset.mid; } appSidebarRight.forwardTab.open([msgID]); }; private onDeleteClick = () => { let peerID = $rootScope.selectedPeerID; let firstName = appPeersManager.getPeerTitle(peerID, false, true); let callback = (revoke: boolean) => { appMessagesManager.deleteMessages([this.msgID], revoke); }; let title: string, description: string, buttons: PopupButton[]; title = 'Delete Message?'; description = `Are you sure you want to delete this message?`; if(peerID == $rootScope.myID) { buttons = [{ text: 'DELETE', isDanger: true, callback: () => callback(false) }]; } else { buttons = [{ text: 'DELETE JUST FOR ME', isDanger: true, callback: () => callback(false) }]; if(peerID > 0) { buttons.push({ text: 'DELETE FOR ME AND ' + firstName, isDanger: true, callback: () => callback(true) }); } else if(appChatsManager.hasRights(-peerID, 'deleteRevoke')) { buttons.push({ text: 'DELETE FOR ALL', isDanger: true, callback: () => callback(true) }); } } buttons.push({ text: 'CANCEL', isCancel: true }); let popup = new PopupPeer('popup-delete-chat', { peerID: peerID, title: title, description: description, buttons: buttons }); popup.show(); }; }