tweb/src/components/chat/contextMenu.ts

306 lines
11 KiB
TypeScript
Raw Normal View History

import { isTouchSupported } from "../../helpers/touchSupport";
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";
2020-11-15 04:33:47 +01:00
import rootScope from "../../lib/rootScope";
import { attachClickEvent, cancelEvent, cancelSelection, findUpClassName } from "../../helpers/dom";
import ButtonMenu, { ButtonMenuItemOptions } from "../buttonMenu";
import { attachContextMenuListener, openBtnMenu, positionMenu } from "../misc";
import PopupDeleteMessages from "../popupDeleteMessages";
import PopupForward from "../popupForward";
import PopupPinMessage from "../popupUnpinMessage";
import { copyTextToClipboard } from "../../helpers/clipboard";
export default class ChatContextMenu {
2020-11-12 03:46:54 +01:00
private buttons: (ButtonMenuItemOptions & {verify: () => boolean, notDirect?: () => boolean, withSelection?: true})[];
private element: HTMLElement;
private target: HTMLElement;
private isTargetAnAlbumItem: boolean;
public peerID: number;
public msgID: number;
constructor(private attachTo: HTMLElement) {
const onContextMenu = (e: MouseEvent | Touch) => {
if(this.init) {
this.init();
this.init = null;
}
let bubble: HTMLElement, bubbleContainer: HTMLElement;
try {
bubbleContainer = findUpClassName(e.target, 'bubble__container');
bubble = bubbleContainer ? bubbleContainer.parentElement : findUpClassName(e.target, 'bubble');
} catch(e) {}
// ! context menu click by date bubble (there is no pointer-events)
if(!bubble) return;
if(e instanceof MouseEvent) e.preventDefault();
if(this.element.classList.contains('active')) {
return false;
}
if(e instanceof MouseEvent) e.cancelBubble = true;
2020-11-12 22:09:55 +01:00
let mid = +bubble.dataset.mid;
if(!mid) return;
// * если открыть контекстное меню для альбома не по бабблу, и последний элемент не выбран, чтобы показать остальные пункты
if(appImManager.chatSelection.isSelecting && !bubbleContainer) {
const mids = appMessagesManager.getMidsByMid(mid);
if(mids.length > 1) {
const selectedMid = appImManager.chatSelection.selectedMids.has(mid) ? mid : mids.find(mid => appImManager.chatSelection.selectedMids.has(mid));
if(selectedMid) {
mid = selectedMid;
}
}
}
2020-11-15 04:33:47 +01:00
this.peerID = rootScope.selectedPeerID;
//this.msgID = msgID;
this.target = e.target as HTMLElement;
const albumItem = findUpClassName(this.target, 'album-item');
this.isTargetAnAlbumItem = !!albumItem;
if(albumItem) {
this.msgID = +albumItem.dataset.mid;
} else {
2020-11-12 22:09:55 +01:00
this.msgID = mid;
}
this.buttons.forEach(button => {
2020-11-12 03:46:54 +01:00
let good: boolean;
2020-11-12 22:09:55 +01:00
//if((appImManager.chatSelection.isSelecting && !button.withSelection) || (button.withSelection && !appImManager.chatSelection.isSelecting)) {
if(appImManager.chatSelection.isSelecting && !button.withSelection) {
2020-11-12 03:46:54 +01:00
good = false;
} else {
good = bubbleContainer || isTouchSupported ?
button.verify() :
2020-11-12 22:09:55 +01:00
button.notDirect && button.verify() && button.notDirect();
2020-11-12 03:46:54 +01:00
}
button.element.classList.toggle('hide', !good);
});
const side: 'left' | 'right' = bubble.classList.contains('is-in') ? 'left' : 'right';
//bubble.parentElement.append(this.element);
//appImManager.log('contextmenu', e, bubble, side);
positionMenu(e, this.element, side);
openBtnMenu(this.element, () => {
this.peerID = this.msgID = 0;
this.target = null;
});
};
if(isTouchSupported) {
attachClickEvent(attachTo, (e) => {
if(appImManager.chatSelection.isSelecting) {
return;
}
const className = (e.target as HTMLElement).className;
if(!className || !className.includes) return;
appImManager.log('touchend', e);
const good = ['bubble', 'bubble__container', 'message', 'time', 'inner'].find(c => className.match(new RegExp(c + '($|\\s)')));
if(good) {
cancelEvent(e);
onContextMenu((e as TouchEvent).changedTouches[0]);
}
});
attachContextMenuListener(attachTo, (e) => {
if(appImManager.chatSelection.isSelecting) return;
// * these two lines will fix instant text selection on iOS Safari
attachTo.classList.add('no-select');
attachTo.addEventListener('touchend', () => {
attachTo.classList.remove('no-select');
}, {once: true});
cancelSelection();
//cancelEvent(e as any);
const bubble = findUpClassName(e.target, 'album-item') || findUpClassName(e.target, 'bubble');
if(bubble) {
appImManager.chatSelection.toggleByBubble(bubble);
}
});
} else attachContextMenuListener(attachTo, onContextMenu);
}
private init = () => {
this.buttons = [{
icon: 'reply',
text: 'Reply',
onClick: this.onReplyClick,
verify: () => (this.peerID > 0 || appChatsManager.hasRights(-this.peerID, 'send')) && this.msgID > 0/* ,
cancelEvent: true */
}, {
icon: 'edit',
text: 'Edit',
onClick: this.onEditClick,
verify: () => appMessagesManager.canEditMessage(this.msgID, 'text')
}, {
icon: 'copy',
text: 'Copy',
onClick: this.onCopyClick,
verify: () => !!appMessagesManager.getMessage(this.msgID).message
2020-11-12 03:46:54 +01:00
}, {
icon: 'copy',
text: 'Copy selected',
onClick: this.onCopyClick,
2020-11-12 22:09:55 +01:00
verify: () => appImManager.chatSelection.selectedMids.has(this.msgID) && !![...appImManager.chatSelection.selectedMids].find(mid => !!appMessagesManager.getMessage(mid).message),
notDirect: () => true,
2020-11-12 03:46:54 +01:00
withSelection: true
}, {
icon: 'pin',
text: 'Pin',
onClick: this.onPinClick,
verify: () => {
const message = appMessagesManager.getMessage(this.msgID);
// for new layer
// return this.msgID > 0 && message._ != 'messageService' && appImManager.pinnedMsgID != this.msgID && (this.peerID > 0 || appChatsManager.hasRights(-this.peerID, 'pin'));
return this.msgID > 0 && message._ != 'messageService' && /* appImManager.pinnedMsgID != this.msgID && */ (this.peerID == rootScope.myID || (this.peerID < 0 && appChatsManager.hasRights(-this.peerID, 'pin')));
}
}, {
icon: 'unpin',
text: 'Unpin',
onClick: this.onUnpinClick,
verify: () => /* appImManager.pinnedMsgID == this.msgID && */ appPeersManager.canPinMessage(this.peerID)
}, {
icon: 'revote',
text: 'Revote',
onClick: this.onRetractVote,
verify: () => {
const message = appMessagesManager.getMessage(this.msgID);
const poll = message.media?.poll as Poll;
return poll && poll.chosenIndexes.length && !poll.pFlags.closed && !poll.pFlags.quiz;
}/* ,
cancelEvent: true */
}, {
icon: 'stop',
text: 'Stop poll',
onClick: this.onStopPoll,
verify: () => {
const message = appMessagesManager.getMessage(this.msgID);
const poll = message.media?.poll;
return appMessagesManager.canEditMessage(this.msgID, 'poll') && poll && !poll.pFlags.closed && this.msgID > 0;
}/* ,
cancelEvent: true */
}, {
icon: 'forward',
text: 'Forward',
onClick: this.onForwardClick,
verify: () => this.msgID > 0
2020-11-12 03:46:54 +01:00
}, {
icon: 'forward',
text: 'Forward selected',
onClick: this.onForwardClick,
2020-11-12 22:09:55 +01:00
verify: () => appImManager.chatSelection.selectedMids.has(this.msgID) && !appImManager.chatSelection.selectionForwardBtn.hasAttribute('disabled'),
notDirect: () => true,
2020-11-12 03:46:54 +01:00
withSelection: true
}, {
icon: 'select',
text: 'Select',
onClick: this.onSelectClick,
verify: () => {
const message = appMessagesManager.getMessage(this.msgID);
return !message.action && !appImManager.chatSelection.selectedMids.has(this.msgID);
},
2020-11-12 03:46:54 +01:00
notDirect: () => true,
withSelection: true
}, {
icon: 'select',
text: 'Clear selection',
onClick: this.onClearSelectionClick,
2020-11-12 03:46:54 +01:00
verify: () => appImManager.chatSelection.selectedMids.has(this.msgID),
2020-11-12 22:09:55 +01:00
notDirect: () => true,
2020-11-12 03:46:54 +01:00
withSelection: true
}, {
icon: 'delete danger',
text: 'Delete',
onClick: this.onDeleteClick,
verify: () => appMessagesManager.canDeleteMessage(this.msgID)
2020-11-12 03:46:54 +01:00
}, {
icon: 'delete danger',
text: 'Delete selected',
onClick: this.onDeleteClick,
2020-11-12 22:09:55 +01:00
verify: () => appImManager.chatSelection.selectedMids.has(this.msgID) && !appImManager.chatSelection.selectionDeleteBtn.hasAttribute('disabled'),
notDirect: () => true,
2020-11-12 03:46:54 +01:00
withSelection: true
}];
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;
const f = () => {
chatInputC.setTopInfo('reply', f, appPeersManager.getPeerTitle(message.fromID, true), message.message, undefined, message);
chatInputC.replyToMsgID = this.msgID;
};
f();
};
private onEditClick = () => {
appImManager.chatInputC.initMessageEditing(this.msgID);
};
private onCopyClick = () => {
2020-11-12 03:46:54 +01:00
const mids = appImManager.chatSelection.isSelecting ? [...appImManager.chatSelection.selectedMids] : [this.msgID];
const str = mids.reduce((acc, mid) => {
const message = appMessagesManager.getMessage(mid);
return acc + (message?.message ? message.message + '\n' : '');
}, '').trim();
copyTextToClipboard(str);
};
private onPinClick = () => {
2020-11-15 04:33:47 +01:00
new PopupPinMessage(rootScope.selectedPeerID, this.msgID);
};
private onUnpinClick = () => {
new PopupPinMessage(rootScope.selectedPeerID, this.msgID, true);
};
private onRetractVote = () => {
appPollsManager.sendVote(this.msgID, []);
};
private onStopPoll = () => {
appPollsManager.stopPoll(this.msgID);
};
private onForwardClick = () => {
2020-11-12 03:46:54 +01:00
if(appImManager.chatSelection.isSelecting) {
appImManager.chatSelection.selectionForwardBtn.click();
} else {
new PopupForward(this.isTargetAnAlbumItem ? [this.msgID] : appMessagesManager.getMidsByMid(this.msgID));
}
};
private onSelectClick = () => {
2020-11-09 20:57:06 +01:00
appImManager.chatSelection.toggleByBubble(findUpClassName(this.target, 'album-item') || findUpClassName(this.target, 'bubble'));
};
private onClearSelectionClick = () => {
appImManager.chatSelection.cancelSelection();
};
private onDeleteClick = () => {
2020-11-12 03:46:54 +01:00
if(appImManager.chatSelection.isSelecting) {
appImManager.chatSelection.selectionDeleteBtn.click();
} else {
new PopupDeleteMessages(this.isTargetAnAlbumItem ? [this.msgID] : appMessagesManager.getMidsByMid(this.msgID));
2020-11-12 03:46:54 +01:00
}
};
}