Avatar context menu

This commit is contained in:
Eduard Kuzmenko 2023-03-13 20:13:21 +04:00
parent 5af49d55f1
commit f14a235b81
6 changed files with 83 additions and 37 deletions

View File

@ -49,6 +49,7 @@ import getMediaFromMessage from '../../lib/appManagers/utils/messages/getMediaFr
import canSaveMessageMedia from '../../lib/appManagers/utils/messages/canSaveMessageMedia';
import getAlbumText from '../../lib/appManagers/utils/messages/getAlbumText';
import PopupElement from '../popups';
import AvatarElement from '../avatar';
type ChatContextMenuButton = ButtonMenuItemOptions & {
verify: () => boolean | Promise<boolean>,
@ -90,6 +91,7 @@ export default class ChatContextMenu {
private linkToMessage: Awaited<ReturnType<ChatContextMenu['getUrlToMessage']>>;
private selectedMessagesText: string;
private selectedMessages: MyMessage[];
private avatarPeerId: number;
constructor(
private chat: Chat,
@ -117,7 +119,7 @@ export default class ChatContextMenu {
'.reply',
'.document',
'audio-element',
'avatar-element',
// 'avatar-element',
'a',
'.bubble-beside-button',
'replies-element',
@ -142,15 +144,16 @@ export default class ChatContextMenu {
}
private onContextMenu = (e: MouseEvent | Touch | TouchEvent) => {
let bubble: HTMLElement, contentWrapper: HTMLElement;
let bubble: HTMLElement, contentWrapper: HTMLElement, avatar: AvatarElement;
try {
contentWrapper = findUpClassName(e.target, 'bubble-content-wrapper');
bubble = contentWrapper ? contentWrapper.parentElement : findUpClassName(e.target, 'bubble');
avatar = findUpClassName(e.target, 'bubbles-group-avatar') as AvatarElement;
} catch(e) {}
// ! context menu click by date bubble (there is no pointer-events)
if(!bubble || bubble.classList.contains('bubble-first')) return;
if((!bubble || bubble.classList.contains('bubble-first')) && !avatar) return;
let element = this.element;
if(e instanceof MouseEvent || e.hasOwnProperty('preventDefault')) (e as any).preventDefault();
@ -159,8 +162,10 @@ export default class ChatContextMenu {
}
if(e instanceof MouseEvent || e.hasOwnProperty('cancelBubble')) (e as any).cancelBubble = true;
let mid = +bubble.dataset.mid;
if(!mid) return;
let mid = avatar ? 0 : +bubble.dataset.mid;
if(!mid && mid !== 0) {
return;
}
const r = async() => {
const isSponsored = this.isSponsored = mid < 0;
@ -177,9 +182,9 @@ export default class ChatContextMenu {
this.sponsoredMessage = isSponsored ? (bubble as any).message.sponsoredMessage : undefined;
const mids = await this.chat.getMidsByMid(mid);
const mids = avatar ? [] : await this.chat.getMidsByMid(mid);
// * если открыть контекстное меню для альбома не по бабблу, и последний элемент не выбран, чтобы показать остальные пункты
if(this.chat.selection.isSelecting && !contentWrapper) {
if(this.chat.selection.isSelecting && !contentWrapper && mid) {
if(isSponsored) {
return;
}
@ -196,6 +201,8 @@ export default class ChatContextMenu {
this.isOverBubble = !!contentWrapper;
this.avatarPeerId = (avatar as AvatarElement)?.peerId;
const groupedItem = findUpClassName(this.target, 'grouped-item');
this.isTargetAGroupedItem = !!groupedItem;
if(groupedItem) {
@ -205,14 +212,14 @@ export default class ChatContextMenu {
}
this.isSelected = this.chat.selection.isMidSelected(this.peerId, this.mid);
this.message = (bubble as any).message || await this.chat.getMessage(this.mid);
this.albumMessages = (this.message as Message.message).grouped_id ? await this.managers.appMessagesManager.getMessagesByAlbum((this.message as Message.message).grouped_id) : undefined;
this.noForwards = !isSponsored && !(await this.managers.appMessagesManager.canForward(this.message));
this.message = avatar ? undefined : (bubble as any).message || await this.chat.getMessage(this.mid);
this.albumMessages = (this.message as Message.message)?.grouped_id ? await this.managers.appMessagesManager.getMessagesByAlbum((this.message as Message.message).grouped_id) : undefined;
this.noForwards = this.message && !isSponsored && !(await this.managers.appMessagesManager.canForward(this.message));
this.viewerPeerId = undefined;
this.canOpenReactedList = undefined;
this.linkToMessage = await this.getUrlToMessage();
this.selectedMessagesText = await this.getSelectedMessagesText();
this.selectedMessages = this.chat.selection.isSelecting ? await this.chat.selection.getSelectedMessages() : undefined;
this.selectedMessages = this.chat.selection.isSelecting && !avatar ? await this.chat.selection.getSelectedMessages() : undefined;
const initResult = await this.init();
if(!initResult) {
@ -242,7 +249,7 @@ export default class ChatContextMenu {
}
}
const side: 'left' | 'right' = bubble.classList.contains('is-in') ? 'left' : 'right';
const side: 'left' | 'right' = !bubble || bubble.classList.contains('is-in') ? 'left' : 'right';
// bubble.parentElement.append(element);
// appImManager.log('contextmenu', e, bubble, side);
positionMenu((e as TouchEvent).touches ? (e as TouchEvent).touches[0] : e as MouseEvent, element, side, menuPadding);
@ -313,6 +320,36 @@ export default class ChatContextMenu {
}
private setButtons() {
if(this.avatarPeerId !== undefined) {
const openPeer = () => {
this.chat.appImManager.setInnerPeer({peerId: this.avatarPeerId});
};
this.buttons = [{
icon: 'message',
text: 'SendMessage',
onClick: openPeer,
verify: () => this.chat.peerId !== this.avatarPeerId && this.avatarPeerId.isUser()
}, {
icon: 'newgroup',
text: 'OpenGroup2',
onClick: openPeer,
verify: () => this.chat.peerId !== this.avatarPeerId && this.managers.appPeersManager.isAnyGroup(this.avatarPeerId)
}, {
icon: 'newchannel',
text: 'OpenChannel2',
onClick: openPeer,
verify: () => this.chat.peerId !== this.avatarPeerId && this.managers.appPeersManager.isBroadcast(this.avatarPeerId)
}, {
icon: 'mention',
text: 'Mention',
onClick: () => {
this.chat.input.mentionUser(this.avatarPeerId.toUserId(), false);
},
verify: () => /* this.avatarPeerId.isUser() && */this.chat.canSend('send_plain')
}];
return;
}
const verifyFavoriteSticker = async(toAdd: boolean) => {
const doc = ((this.message as Message.message).media as MessageMedia.messageMediaDocument)?.document;
if(!(doc as MyDocument)?.sticker) {
@ -770,7 +807,7 @@ export default class ChatContextMenu {
let reactionsMenu: ChatReactionsMenu;
let reactionsMenuPosition: 'horizontal' | 'vertical';
if(
this.message._ === 'message' &&
this.message?._ === 'message' &&
!this.chat.selection.isSelecting &&
!this.message.pFlags.is_outgoing &&
!this.message.pFlags.is_scheduled &&
@ -868,7 +905,7 @@ export default class ChatContextMenu {
}
private async getUrlToMessage() {
if(this.peerId.isUser()) {
if(!this.message || this.peerId.isUser()) {
return;
}
@ -898,7 +935,7 @@ export default class ChatContextMenu {
}
private async getSelectedMessagesText() {
if(!isSelectionEmpty()) {
if(this.avatarPeerId || !isSelectionEmpty()) {
return '';
}

View File

@ -111,6 +111,7 @@ import {MARKDOWN_ENTITIES} from '../../lib/richTextProcessor';
import IMAGE_MIME_TYPES_SUPPORTED from '../../environment/imageMimeTypesSupport';
import VIDEO_MIME_TYPES_SUPPORTED from '../../environment/videoMimeTypesSupport';
import {ChatRights} from '../../lib/appManagers/appChatsManager';
import getPeerActiveUsernames from '../../lib/appManagers/utils/peers/getPeerActiveUsernames';
const RECORD_MIN_TIME = 500;
@ -1296,6 +1297,27 @@ export default class ChatInput {
this.managers.appDraftsManager.syncDraft(this.chat.peerId, this.chat.threadId, draft);
}
public mentionUser(userId: UserId, isHelper?: boolean) {
Promise.resolve(this.managers.appUsersManager.getUser(userId)).then((user) => {
let str = '', entity: MessageEntity;
const usernames = getPeerActiveUsernames(user);
if(usernames[0]) {
str = '@' + usernames[0];
} else {
str = user.first_name || user.last_name;
entity = {
_: 'messageEntityMentionName',
length: str.length,
offset: 0,
user_id: user.id
};
}
str += ' ';
this.insertAtCaret(str, entity, isHelper);
});
}
public destroy() {
// this.chat.log.error('Input destroying');
@ -2185,7 +2207,7 @@ export default class ChatInput {
const newValue = newPrefix + insertText + suffix;
if(isHelper && caretPos !== -1) {
const match = matches[2];
const match = matches ? matches[2] : fullValue;
// const {node, selection} = getCaretPosNew(this.messageInput);
const selection = document.getSelection();

View File

@ -5,7 +5,6 @@
*/
import type ChatInput from './input';
import type {MessageEntity} from '../../layer';
import AutocompleteHelperController from './autocompleteHelperController';
import AutocompletePeerHelper from './autocompletePeerHelper';
import {AppManagers} from '../../lib/appManagers/managers';
@ -25,24 +24,7 @@ export default class MentionsHelper extends AutocompletePeerHelper {
'mentions-helper',
(target) => {
const userId = (target as HTMLElement).dataset.peerId.toUserId();
const user = Promise.resolve(managers.appUsersManager.getUser(userId)).then((user) => {
let str = '', entity: MessageEntity;
const usernames = getPeerActiveUsernames(user);
if(usernames[0]) {
str = '@' + usernames[0];
} else {
str = user.first_name || user.last_name;
entity = {
_: 'messageEntityMentionName',
length: str.length,
offset: 0,
user_id: user.id
};
}
str += ' ';
chatInput.insertAtCaret(str, entity);
});
chatInput.mentionUser(userId, true);
}
);
}

View File

@ -890,7 +890,8 @@ export default class ChatSelection extends AppSelection {
}
public canSelectBubble(bubble: HTMLElement) {
return !bubble.classList.contains('service') &&
return bubble &&
!bubble.classList.contains('service') &&
!bubble.classList.contains('is-outgoing') &&
!bubble.classList.contains('is-error') &&
!bubble.classList.contains('bubble-first') &&

View File

@ -21,7 +21,7 @@ const App = {
version: process.env.VERSION,
versionFull: process.env.VERSION_FULL,
build: +process.env.BUILD,
langPackVersion: '1.0.7',
langPackVersion: '1.0.8',
langPack: 'webk',
langPackCode: 'en',
domains: MAIN_DOMAINS,

View File

@ -955,6 +955,10 @@ const lang = {
'BotAlreadyAddedToAttachMenu': 'This bot is already added to your attachment menu.',
'AddBot': 'Add Bot',
'ActionAttachMenuBotAllowed': 'You allowed this bot to message you when you added it to your attachment menu.',
'SendMessage': 'Send Message',
'Mention': 'Mention',
'OpenChannel2': 'Open Channel',
'OpenGroup2': 'Open Group',
// * macos
'AccountSettings.Filters': 'Chat Folders',