Ability to edit message when input is locked
Fix unneeded focus on hidden input Throttle emoji interactive animation Remove link to Webogram
This commit is contained in:
parent
18737324e6
commit
988ed5f306
|
@ -252,6 +252,7 @@ export default class ChatInput {
|
|||
public sendAsPeerId: PeerId;
|
||||
|
||||
private replyInTopicOverlay: HTMLDivElement;
|
||||
private restoreInputLock: () => void;
|
||||
|
||||
constructor(
|
||||
private chat: Chat,
|
||||
|
@ -368,12 +369,18 @@ export default class ChatInput {
|
|||
this.inputContainer.append(c);
|
||||
}
|
||||
|
||||
private createButtonIcon(...args: Parameters<typeof ButtonIcon>) {
|
||||
const button = ButtonIcon(...args);
|
||||
button.tabIndex = -1;
|
||||
return button;
|
||||
}
|
||||
|
||||
public constructPeerHelpers() {
|
||||
this.replyElements.container = document.createElement('div');
|
||||
this.replyElements.container.classList.add('reply-wrapper');
|
||||
|
||||
this.replyElements.iconBtn = ButtonIcon('');
|
||||
this.replyElements.cancelBtn = ButtonIcon('close reply-cancel', {noRipple: true});
|
||||
this.replyElements.iconBtn = this.createButtonIcon('');
|
||||
this.replyElements.cancelBtn = this.createButtonIcon('close reply-cancel', {noRipple: true});
|
||||
|
||||
this.replyElements.container.append(this.replyElements.iconBtn, this.replyElements.cancelBtn);
|
||||
|
||||
|
@ -501,7 +508,7 @@ export default class ChatInput {
|
|||
this.replyInTopicOverlay.classList.add('reply-in-topic-overlay', 'hide');
|
||||
this.replyInTopicOverlay.append(i18n('Chat.Input.ReplyToAnswer'));
|
||||
|
||||
this.btnToggleEmoticons = ButtonIcon('none toggle-emoticons', {noRipple: true});
|
||||
this.btnToggleEmoticons = this.createButtonIcon('none toggle-emoticons', {noRipple: true});
|
||||
|
||||
this.inputMessageContainer = document.createElement('div');
|
||||
this.inputMessageContainer.classList.add('input-message-container');
|
||||
|
@ -530,7 +537,7 @@ export default class ChatInput {
|
|||
});
|
||||
}, {listenerSetter: this.listenerSetter});
|
||||
|
||||
this.btnScheduled = ButtonIcon('scheduled btn-scheduled float hide', {noRipple: true});
|
||||
this.btnScheduled = this.createButtonIcon('scheduled btn-scheduled float hide', {noRipple: true});
|
||||
|
||||
attachClickEvent(this.btnScheduled, (e) => {
|
||||
this.appImManager.openScheduled(this.chat.peerId);
|
||||
|
@ -554,7 +561,7 @@ export default class ChatInput {
|
|||
});
|
||||
});
|
||||
|
||||
this.btnToggleReplyMarkup = ButtonIcon('botcom toggle-reply-markup float hide', {noRipple: true});
|
||||
this.btnToggleReplyMarkup = this.createButtonIcon('botcom toggle-reply-markup float hide', {noRipple: true});
|
||||
this.replyKeyboard = new ReplyKeyboard({
|
||||
appendTo: this.rowsWrapper,
|
||||
listenerSetter: this.listenerSetter,
|
||||
|
@ -780,7 +787,7 @@ export default class ChatInput {
|
|||
this.inlineHelper = new InlineHelper(this.rowsWrapper, this.autocompleteHelperController, this.chat, this.managers);
|
||||
this.rowsWrapper.append(this.newMessageWrapper);
|
||||
|
||||
this.btnCancelRecord = ButtonIcon('binfilled btn-circle btn-record-cancel chat-secondary-button');
|
||||
this.btnCancelRecord = this.createButtonIcon('binfilled btn-circle btn-record-cancel chat-secondary-button');
|
||||
|
||||
this.btnSendContainer = document.createElement('div');
|
||||
this.btnSendContainer.classList.add('btn-send-container');
|
||||
|
@ -788,7 +795,7 @@ export default class ChatInput {
|
|||
this.recordRippleEl = document.createElement('div');
|
||||
this.recordRippleEl.classList.add('record-ripple');
|
||||
|
||||
this.btnSend = ButtonIcon('none btn-circle btn-send animated-button-icon');
|
||||
this.btnSend = this.createButtonIcon('none btn-circle btn-send animated-button-icon');
|
||||
this.btnSend.insertAdjacentHTML('afterbegin', `
|
||||
<span class="tgico tgico-send"></span>
|
||||
<span class="tgico tgico-schedule"></span>
|
||||
|
@ -1591,7 +1598,10 @@ export default class ChatInput {
|
|||
return;
|
||||
}
|
||||
|
||||
const oldKey = i.key;
|
||||
i.compareAndUpdate({key});
|
||||
|
||||
return {oldKey, newKey: key};
|
||||
}
|
||||
|
||||
private filterAttachMenuButtons() {
|
||||
|
@ -1617,15 +1627,23 @@ export default class ChatInput {
|
|||
chatInput.classList.remove('no-transition');
|
||||
}
|
||||
|
||||
this.updateMessageInputPlaceholder(placeholderKey);
|
||||
const isEditingAndLocked = canSend && !canSendPlain && this.restoreInputLock;
|
||||
|
||||
if(!canSend || !canSendPlain) {
|
||||
!isEditingAndLocked && this.updateMessageInputPlaceholder(placeholderKey);
|
||||
|
||||
if(isEditingAndLocked) {
|
||||
this.restoreInputLock = () => {
|
||||
this.updateMessageInputPlaceholder(placeholderKey);
|
||||
this.messageInput.contentEditable = 'false';
|
||||
};
|
||||
} else if(!canSend || !canSendPlain) {
|
||||
messageInput.contentEditable = 'false';
|
||||
|
||||
if(!canSendPlain) {
|
||||
this.messageInputField.onFakeInput(undefined, true);
|
||||
}
|
||||
} else {
|
||||
this.restoreInputLock = undefined;
|
||||
messageInput.contentEditable = 'true';
|
||||
this.setDraft(undefined, false);
|
||||
|
||||
|
@ -1650,6 +1668,7 @@ export default class ChatInput {
|
|||
withLinebreaks: true
|
||||
});
|
||||
|
||||
this.messageInputField.input.tabIndex = -1;
|
||||
this.messageInputField.input.classList.replace('input-field-input', 'input-message-input');
|
||||
this.messageInputField.inputFake.classList.replace('input-field-input', 'input-message-input');
|
||||
this.messageInput = this.messageInputField.input;
|
||||
|
@ -2354,7 +2373,7 @@ export default class ChatInput {
|
|||
foundHelper = this.inlineHelper;
|
||||
|
||||
if(!this.btnPreloader) {
|
||||
this.btnPreloader = ButtonIcon('none btn-preloader float show disable-hover', {noRipple: true});
|
||||
this.btnPreloader = this.createButtonIcon('none btn-preloader float show disable-hover', {noRipple: true});
|
||||
putPreloader(this.btnPreloader, true);
|
||||
this.inputMessageContainer.parentElement.insertBefore(this.btnPreloader, this.inputMessageContainer.nextSibling);
|
||||
} else {
|
||||
|
@ -2901,12 +2920,27 @@ export default class ChatInput {
|
|||
|
||||
let input = wrapDraftText(message.message, {entities: message.totalEntities, wrappingForPeerId: this.chat.peerId});
|
||||
const f = async() => {
|
||||
let restoreInputLock: () => void;
|
||||
if(!this.messageInput.isContentEditable) {
|
||||
const placeholderKey = await this.getPlaceholderKey(true);
|
||||
const {contentEditable} = this.messageInput;
|
||||
this.messageInput.contentEditable = 'true';
|
||||
const {oldKey} = this.updateMessageInputPlaceholder(placeholderKey);
|
||||
|
||||
restoreInputLock = () => {
|
||||
this.messageInput.contentEditable = contentEditable;
|
||||
this.updateMessageInputPlaceholder(oldKey);
|
||||
};
|
||||
}
|
||||
|
||||
const replyFragment = await wrapMessageForReply({message, usingMids: [message.mid]});
|
||||
this.setTopInfo('edit', f, i18n('AccDescrEditing'), replyFragment, input, message);
|
||||
|
||||
this.editMsgId = mid;
|
||||
this.editMessage = message;
|
||||
input = undefined;
|
||||
|
||||
this.restoreInputLock = restoreInputLock;
|
||||
};
|
||||
f();
|
||||
}
|
||||
|
@ -3084,6 +3118,11 @@ export default class ChatInput {
|
|||
this.editMsgId = this.editMessage = undefined;
|
||||
this.helperType = this.helperFunc = undefined;
|
||||
|
||||
if(this.restoreInputLock) {
|
||||
this.restoreInputLock?.();
|
||||
this.restoreInputLock = undefined;
|
||||
}
|
||||
|
||||
if(this.chat.container.classList.contains('is-helper-active')) {
|
||||
appNavigationController.removeByType('input-helper');
|
||||
this.chat.container.classList.remove('is-helper-active');
|
||||
|
@ -3139,7 +3178,7 @@ export default class ChatInput {
|
|||
const oldReply = replyParent.lastElementChild.previousElementSibling;
|
||||
const haveReply = oldReply.classList.contains('reply');
|
||||
|
||||
this.replyElements.iconBtn.replaceWith(this.replyElements.iconBtn = ButtonIcon((type === 'webpage' ? 'link' : type) + ' active reply-icon', {noRipple: true}));
|
||||
this.replyElements.iconBtn.replaceWith(this.replyElements.iconBtn = this.createButtonIcon((type === 'webpage' ? 'link' : type) + ' active reply-icon', {noRipple: true}));
|
||||
const {container} = wrapReply(title, subtitle, this.chat.animationGroup, message);
|
||||
if(haveReply) {
|
||||
oldReply.replaceWith(container);
|
||||
|
|
|
@ -209,7 +209,7 @@ export class AppSidebarLeft extends SidebarSlider {
|
|||
});
|
||||
},
|
||||
verify: () => App.isMainDomain
|
||||
}, {
|
||||
}, /* {
|
||||
icon: 'char w',
|
||||
text: 'ChatList.Menu.SwitchTo.Webogram',
|
||||
onClick: () => {
|
||||
|
@ -218,7 +218,7 @@ export class AppSidebarLeft extends SidebarSlider {
|
|||
});
|
||||
},
|
||||
verify: () => App.isMainDomain
|
||||
}, {
|
||||
}, */ {
|
||||
icon: 'download',
|
||||
text: 'PWA.Install',
|
||||
onClick: () => {
|
||||
|
|
|
@ -45,6 +45,7 @@ import PopupStickers from '../popups/stickers';
|
|||
import {hideToast, toastNew} from '../toast';
|
||||
import wrapStickerAnimation from './stickerAnimation';
|
||||
import framesCache from '../../helpers/framesCache';
|
||||
import {IS_MOBILE} from '../../environment/userAgent';
|
||||
|
||||
// https://github.com/telegramdesktop/tdesktop/blob/master/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp#L40
|
||||
export const STICKER_EFFECT_MULTIPLIER = 1 + 0.245 * 2;
|
||||
|
@ -753,6 +754,11 @@ export async function onEmojiStickerClick({event, container, managers, peerId, m
|
|||
return;
|
||||
}
|
||||
|
||||
const activeAnimations: Set<{}> = (container as any).activeAnimations ??= new Set();
|
||||
if(activeAnimations.size >= (IS_MOBILE ? 3 : 5)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const doc = await managers.appStickersManager.getAnimatedEmojiSticker(emoji, true);
|
||||
if(!doc) {
|
||||
return;
|
||||
|
@ -763,7 +769,7 @@ export async function onEmojiStickerClick({event, container, managers, peerId, m
|
|||
v: 1
|
||||
};
|
||||
|
||||
const sendInteractionThrottled: () => void = (container as any).sendInteractionThrottled = throttle(() => {
|
||||
const sendInteractionThrottled: () => void = (container as any).sendInteractionThrottled ??= throttle(() => {
|
||||
const length = data.a.length;
|
||||
if(!length) {
|
||||
return;
|
||||
|
@ -789,6 +795,9 @@ export async function onEmojiStickerClick({event, container, managers, peerId, m
|
|||
data.a.length = 0;
|
||||
}, 1000, false);
|
||||
|
||||
const o = {};
|
||||
activeAnimations.add(o);
|
||||
|
||||
const isOut = bubble ? bubble.classList.contains('is-out') : undefined;
|
||||
const {animationDiv} = wrapStickerAnimation({
|
||||
doc,
|
||||
|
@ -797,7 +806,10 @@ export async function onEmojiStickerClick({event, container, managers, peerId, m
|
|||
size: 360,
|
||||
target: container,
|
||||
play: true,
|
||||
withRandomOffset: true
|
||||
withRandomOffset: true,
|
||||
onUnmount: () => {
|
||||
activeAnimations.delete(o);
|
||||
}
|
||||
});
|
||||
|
||||
if(isOut !== undefined && !isOut) {
|
||||
|
|
|
@ -30,7 +30,8 @@ export default function wrapStickerAnimation({
|
|||
fullThumb,
|
||||
withRandomOffset,
|
||||
relativeEffect,
|
||||
loopEffect
|
||||
loopEffect,
|
||||
onUnmount
|
||||
}: {
|
||||
size: number,
|
||||
doc: MyDocument,
|
||||
|
@ -43,7 +44,8 @@ export default function wrapStickerAnimation({
|
|||
fullThumb?: PhotoSize | Extract<VideoSize, VideoSize.videoSize>,
|
||||
withRandomOffset?: boolean,
|
||||
relativeEffect?: boolean,
|
||||
loopEffect?: boolean
|
||||
loopEffect?: boolean,
|
||||
onUnmount?: () => void
|
||||
}) {
|
||||
const animationDiv = document.createElement('div');
|
||||
animationDiv.classList.add('emoji-animation');
|
||||
|
@ -58,6 +60,7 @@ export default function wrapStickerAnimation({
|
|||
animation?.remove();
|
||||
animationDiv.remove();
|
||||
appImManager.chat.bubbles.scrollable.container.removeEventListener('scroll', onScroll);
|
||||
onUnmount?.();
|
||||
};
|
||||
|
||||
const middlewareHelper = middleware?.create() ?? getMiddleware();
|
||||
|
|
|
@ -1061,13 +1061,14 @@ export class AppImManager extends EventListenerBase<{
|
|||
|
||||
if(
|
||||
chat?.input?.messageInput &&
|
||||
e.target !== chat.input.messageInput &&
|
||||
target !== chat.input.messageInput &&
|
||||
target.tagName !== 'INPUT' &&
|
||||
!target.isContentEditable &&
|
||||
!IS_TOUCH_SUPPORTED &&
|
||||
(!mediaSizes.isMobile || this.tabId === APP_TABS.CHAT) &&
|
||||
!chat.selection.isSelecting &&
|
||||
!chat.input.recording
|
||||
!chat.input.recording &&
|
||||
chat.input.messageInput.isContentEditable
|
||||
) {
|
||||
chat.input.messageInput.focus();
|
||||
placeCaretAtEnd(chat.input.messageInput);
|
||||
|
|
|
@ -3440,9 +3440,15 @@ export class AppMessagesManager extends AppManager {
|
|||
return true;
|
||||
}
|
||||
|
||||
const canEditMessageInPeer = this.appPeersManager.isBroadcast(message.peerId) ?
|
||||
this.appChatsManager.hasRights(message.peerId.toChatId(), 'edit_messages') :
|
||||
message.pFlags.out;
|
||||
const {peerId} = message;
|
||||
|
||||
const canEditMessageInPeer = this.appPeersManager.isBroadcast(peerId) ?
|
||||
this.appChatsManager.hasRights(peerId.toChatId(), 'edit_messages') :
|
||||
(
|
||||
peerId.isAnyChat() && kind === 'text' ?
|
||||
this.appChatsManager.hasRights(peerId.toChatId(), 'send_plain') || this.appChatsManager.hasRights(peerId.toChatId(), 'send_media') :
|
||||
true
|
||||
) && message.pFlags.out;
|
||||
|
||||
if(
|
||||
!canEditMessageInPeer || (
|
||||
|
|
Loading…
Reference in New Issue