Markup tooltip changes:
Support multiple format types Fix link editor on small devices
This commit is contained in:
parent
c5570cd7af
commit
bf58a3d147
@ -11,7 +11,7 @@ import apiManager from "../../lib/mtproto/mtprotoworker";
|
|||||||
//import Recorder from '../opus-recorder/dist/recorder.min';
|
//import Recorder from '../opus-recorder/dist/recorder.min';
|
||||||
import opusDecodeController from "../../lib/opusDecodeController";
|
import opusDecodeController from "../../lib/opusDecodeController";
|
||||||
import RichTextProcessor from "../../lib/richtextprocessor";
|
import RichTextProcessor from "../../lib/richtextprocessor";
|
||||||
import { attachClickEvent, blurActiveElement, cancelEvent, cancelSelection, findUpClassName, getSelectedNodes, isInputEmpty, markdownTags, MarkdownType, placeCaretAtEnd, serializeNodes } from "../../helpers/dom";
|
import { attachClickEvent, blurActiveElement, cancelEvent, cancelSelection, findUpClassName, getRichValue, getSelectedNodes, isInputEmpty, markdownTags, MarkdownType, placeCaretAtEnd, serializeNodes } from "../../helpers/dom";
|
||||||
import { ButtonMenuItemOptions } from '../buttonMenu';
|
import { ButtonMenuItemOptions } from '../buttonMenu';
|
||||||
import emoticonsDropdown from "../emoticonsDropdown";
|
import emoticonsDropdown from "../emoticonsDropdown";
|
||||||
import PopupCreatePoll from "../popups/createPoll";
|
import PopupCreatePoll from "../popups/createPoll";
|
||||||
@ -585,7 +585,7 @@ export default class ChatInput {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.listenerSetter.add(this.messageInput, 'beforeinput', (e: Event) => {
|
/* this.listenerSetter.add(this.messageInput, 'beforeinput', (e: Event) => {
|
||||||
// * validate due to manual formatting through browser's context menu
|
// * validate due to manual formatting through browser's context menu
|
||||||
const inputType = (e as InputEvent).inputType;
|
const inputType = (e as InputEvent).inputType;
|
||||||
//console.log('message beforeinput event', e);
|
//console.log('message beforeinput event', e);
|
||||||
@ -597,7 +597,7 @@ export default class ChatInput {
|
|||||||
cancelEvent(e); // * cancel legacy markdown event
|
cancelEvent(e); // * cancel legacy markdown event
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}); */
|
||||||
this.listenerSetter.add(this.messageInput, 'input', this.onMessageInput);
|
this.listenerSetter.add(this.messageInput, 'input', this.onMessageInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -655,7 +655,7 @@ export default class ChatInput {
|
|||||||
/**
|
/**
|
||||||
* * clear previous formatting, due to Telegram's inability to handle several entities
|
* * clear previous formatting, due to Telegram's inability to handle several entities
|
||||||
*/
|
*/
|
||||||
const checkForSingle = () => {
|
/* const checkForSingle = () => {
|
||||||
const nodes = getSelectedNodes();
|
const nodes = getSelectedNodes();
|
||||||
//console.log('Using formatting:', commandsMap[type], nodes, this.executedHistory);
|
//console.log('Using formatting:', commandsMap[type], nodes, this.executedHistory);
|
||||||
|
|
||||||
@ -686,12 +686,14 @@ export default class ChatInput {
|
|||||||
executed.push(document.execCommand('styleWithCSS', false, 'false'));
|
executed.push(document.execCommand('styleWithCSS', false, 'false'));
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
};
|
}; */
|
||||||
|
|
||||||
//if(type === 'monospace') {
|
|
||||||
let haveThisType = false;
|
|
||||||
executed.push(document.execCommand('styleWithCSS', false, 'true'));
|
executed.push(document.execCommand('styleWithCSS', false, 'true'));
|
||||||
|
|
||||||
|
if(type === 'monospace') {
|
||||||
|
let haveThisType = false;
|
||||||
|
//executed.push(document.execCommand('styleWithCSS', false, 'true'));
|
||||||
|
|
||||||
const selection = window.getSelection();
|
const selection = window.getSelection();
|
||||||
if(!selection.isCollapsed) {
|
if(!selection.isCollapsed) {
|
||||||
const range = selection.getRangeAt(0);
|
const range = selection.getRangeAt(0);
|
||||||
@ -703,18 +705,20 @@ export default class ChatInput {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
executed.push(document.execCommand('removeFormat', false, null));
|
//executed.push(document.execCommand('removeFormat', false, null));
|
||||||
|
|
||||||
if(!haveThisType) {
|
if(haveThisType) {
|
||||||
|
executed.push(document.execCommand('fontName', false, 'Roboto'));
|
||||||
|
} else {
|
||||||
|
executed.push(typeof(command) === 'function' ? command() : document.execCommand(command, false, null));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
executed.push(typeof(command) === 'function' ? command() : document.execCommand(command, false, null));
|
executed.push(typeof(command) === 'function' ? command() : document.execCommand(command, false, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
executed.push(document.execCommand('styleWithCSS', false, 'false'));
|
executed.push(document.execCommand('styleWithCSS', false, 'false'));
|
||||||
/* } else {
|
|
||||||
executed.push(typeof(command) === 'function' ? command() : document.execCommand(command, false, null));
|
|
||||||
} */
|
|
||||||
|
|
||||||
checkForSingle();
|
//checkForSingle();
|
||||||
saveExecuted();
|
saveExecuted();
|
||||||
if(this.appImManager.markupTooltip) {
|
if(this.appImManager.markupTooltip) {
|
||||||
this.appImManager.markupTooltip.setActiveMarkupButton();
|
this.appImManager.markupTooltip.setActiveMarkupButton();
|
||||||
@ -794,10 +798,10 @@ export default class ChatInput {
|
|||||||
|
|
||||||
//console.log('messageInput input', this.messageInput.innerText, this.serializeNodes(Array.from(this.messageInput.childNodes)));
|
//console.log('messageInput input', this.messageInput.innerText, this.serializeNodes(Array.from(this.messageInput.childNodes)));
|
||||||
//const value = this.messageInput.innerText;
|
//const value = this.messageInput.innerText;
|
||||||
const richValue = this.messageInputField.value;
|
const markdownEntities: MessageEntity[] = [];
|
||||||
|
const richValue = getRichValue(this.messageInputField.input, markdownEntities);
|
||||||
|
|
||||||
//const entities = RichTextProcessor.parseEntities(value);
|
//const entities = RichTextProcessor.parseEntities(value);
|
||||||
const markdownEntities: MessageEntity[] = [];
|
|
||||||
const value = RichTextProcessor.parseMarkdown(richValue, markdownEntities);
|
const value = RichTextProcessor.parseMarkdown(richValue, markdownEntities);
|
||||||
const entities = RichTextProcessor.mergeEntities(markdownEntities, RichTextProcessor.parseEntities(value));
|
const entities = RichTextProcessor.mergeEntities(markdownEntities, RichTextProcessor.parseEntities(value));
|
||||||
|
|
||||||
@ -815,6 +819,10 @@ export default class ChatInput {
|
|||||||
this.stickersHelper.checkEmoticon(emoticon);
|
this.stickersHelper.checkEmoticon(emoticon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!richValue.trim()) {
|
||||||
|
this.appImManager.markupTooltip.hide();
|
||||||
|
}
|
||||||
|
|
||||||
const html = this.messageInput.innerHTML;
|
const html = this.messageInput.innerHTML;
|
||||||
if(this.canRedoFromHTML && html != this.canRedoFromHTML && !this.lockRedo) {
|
if(this.canRedoFromHTML && html != this.canRedoFromHTML && !this.lockRedo) {
|
||||||
this.canRedoFromHTML = '';
|
this.canRedoFromHTML = '';
|
||||||
@ -1095,19 +1103,18 @@ export default class ChatInput {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//let str = this.serializeNodes(Array.from(this.messageInput.childNodes));
|
const entities: MessageEntity[] = [];
|
||||||
let str = this.messageInputField.value;
|
const str = getRichValue(this.messageInputField.input, entities);
|
||||||
|
|
||||||
//console.log('childnode str after:', str/* , getRichValue(this.messageInput) */);
|
|
||||||
|
|
||||||
//return;
|
//return;
|
||||||
|
|
||||||
if(this.editMsgId) {
|
if(this.editMsgId) {
|
||||||
this.appMessagesManager.editMessage(this.chat.getMessage(this.editMsgId), str, {
|
this.appMessagesManager.editMessage(this.chat.getMessage(this.editMsgId), str, {
|
||||||
|
entities,
|
||||||
noWebPage: this.noWebPage
|
noWebPage: this.noWebPage
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.appMessagesManager.sendText(this.chat.peerId, str, {
|
this.appMessagesManager.sendText(this.chat.peerId, str, {
|
||||||
|
entities,
|
||||||
replyToMsgId: this.replyToMsgId,
|
replyToMsgId: this.replyToMsgId,
|
||||||
threadId: this.chat.threadId,
|
threadId: this.chat.threadId,
|
||||||
noWebPage: this.noWebPage,
|
noWebPage: this.noWebPage,
|
||||||
|
@ -5,6 +5,7 @@ import ButtonIcon from "../buttonIcon";
|
|||||||
import { clamp } from "../../helpers/number";
|
import { clamp } from "../../helpers/number";
|
||||||
import { isTouchSupported } from "../../helpers/touchSupport";
|
import { isTouchSupported } from "../../helpers/touchSupport";
|
||||||
import { isApple } from "../../helpers/userAgent";
|
import { isApple } from "../../helpers/userAgent";
|
||||||
|
//import { logger } from "../../lib/logger";
|
||||||
|
|
||||||
export default class MarkupTooltip {
|
export default class MarkupTooltip {
|
||||||
public container: HTMLElement;
|
public container: HTMLElement;
|
||||||
@ -17,10 +18,11 @@ export default class MarkupTooltip {
|
|||||||
private waitingForMouseUp = false;
|
private waitingForMouseUp = false;
|
||||||
private linkInput: HTMLInputElement;
|
private linkInput: HTMLInputElement;
|
||||||
private savedRange: Range;
|
private savedRange: Range;
|
||||||
mouseUpCounter: number = 0;
|
private mouseUpCounter: number = 0;
|
||||||
|
//private log: ReturnType<typeof logger>;
|
||||||
|
|
||||||
constructor(private appImManager: AppImManager) {
|
constructor(private appImManager: AppImManager) {
|
||||||
|
//this.log = logger('MARKUP');
|
||||||
}
|
}
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
@ -41,14 +43,20 @@ export default class MarkupTooltip {
|
|||||||
tools1.append(this.buttons[c] = button);
|
tools1.append(this.buttons[c] = button);
|
||||||
|
|
||||||
if(c !== 'link') {
|
if(c !== 'link') {
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('mousedown', (e) => {
|
||||||
|
cancelEvent(e);
|
||||||
this.appImManager.chat.input.applyMarkdown(c);
|
this.appImManager.chat.input.applyMarkdown(c);
|
||||||
this.hide();
|
this.cancelClosening();
|
||||||
|
|
||||||
|
/* this.mouseUpCounter = 0;
|
||||||
|
this.setMouseUpEvent(); */
|
||||||
|
//this.hide();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
attachClickEvent(button, (e) => {
|
attachClickEvent(button, (e) => {
|
||||||
cancelEvent(e);
|
cancelEvent(e);
|
||||||
this.showLinkEditor();
|
this.showLinkEditor();
|
||||||
|
this.cancelClosening();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -81,17 +89,19 @@ export default class MarkupTooltip {
|
|||||||
this.linkInput.classList.remove('error');
|
this.linkInput.classList.remove('error');
|
||||||
});
|
});
|
||||||
|
|
||||||
attachClickEvent(this.linkBackButton, (e) => {
|
this.linkBackButton.addEventListener('mousedown', (e) => {
|
||||||
|
//this.log('linkBackButton click');
|
||||||
cancelEvent(e);
|
cancelEvent(e);
|
||||||
this.container.classList.remove('is-link');
|
this.container.classList.remove('is-link');
|
||||||
//input.value = '';
|
//input.value = '';
|
||||||
this.resetSelection();
|
this.resetSelection();
|
||||||
this.setTooltipPosition();
|
this.setTooltipPosition();
|
||||||
|
this.cancelClosening();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.linkApplyButton = ButtonIcon('check markup-tooltip-link-apply', {noRipple: true});
|
this.linkApplyButton = ButtonIcon('check markup-tooltip-link-apply', {noRipple: true});
|
||||||
attachClickEvent(this.linkApplyButton, (e) => {
|
this.linkApplyButton.addEventListener('mousedown', (e) => {
|
||||||
cancelEvent(e);
|
//this.log('linkApplyButton click');
|
||||||
this.applyLink(e);
|
this.applyLink(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -145,7 +155,9 @@ export default class MarkupTooltip {
|
|||||||
cancelEvent(e);
|
cancelEvent(e);
|
||||||
this.resetSelection();
|
this.resetSelection();
|
||||||
this.appImManager.chat.input.applyMarkdown('link', this.linkInput.value);
|
this.appImManager.chat.input.applyMarkdown('link', this.linkInput.value);
|
||||||
|
setTimeout(() => {
|
||||||
this.hide();
|
this.hide();
|
||||||
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private isLinkValid() {
|
private isLinkValid() {
|
||||||
@ -160,10 +172,12 @@ export default class MarkupTooltip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public hide() {
|
public hide() {
|
||||||
|
//return;
|
||||||
|
|
||||||
if(this.init) return;
|
if(this.init) return;
|
||||||
|
|
||||||
this.container.classList.remove('is-visible');
|
this.container.classList.remove('is-visible');
|
||||||
document.removeEventListener('mouseup', this.onMouseUp);
|
//document.removeEventListener('mouseup', this.onMouseUp);
|
||||||
document.removeEventListener('mouseup', this.onMouseUpSingle);
|
document.removeEventListener('mouseup', this.onMouseUpSingle);
|
||||||
this.waitingForMouseUp = false;
|
this.waitingForMouseUp = false;
|
||||||
|
|
||||||
@ -178,39 +192,33 @@ export default class MarkupTooltip {
|
|||||||
public getActiveMarkupButton() {
|
public getActiveMarkupButton() {
|
||||||
const nodes = getSelectedNodes();
|
const nodes = getSelectedNodes();
|
||||||
const parents = [...new Set(nodes.map(node => node.parentNode))];
|
const parents = [...new Set(nodes.map(node => node.parentNode))];
|
||||||
if(parents.length > 1) return undefined;
|
//if(parents.length > 1 && parents) return [];
|
||||||
|
|
||||||
const node = parents[0] as HTMLElement;
|
const currentMarkups: Set<HTMLElement> = new Set();
|
||||||
let currentMarkup: HTMLElement;
|
(parents as HTMLElement[]).forEach(node => {
|
||||||
for(const type in markdownTags) {
|
for(const type in markdownTags) {
|
||||||
const tag = markdownTags[type as MarkdownType];
|
const tag = markdownTags[type as MarkdownType];
|
||||||
if(node.matches(tag.match)) {
|
const closest = node.closest(tag.match + ', [contenteditable]');
|
||||||
currentMarkup = this.buttons[type as MarkdownType];
|
if(closest !== this.appImManager.chat.input.messageInput) {
|
||||||
break;
|
currentMarkups.add(this.buttons[type as MarkdownType]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return currentMarkup;
|
|
||||||
|
return [...currentMarkups];
|
||||||
}
|
}
|
||||||
|
|
||||||
public setActiveMarkupButton() {
|
public setActiveMarkupButton() {
|
||||||
const activeButton = this.getActiveMarkupButton();
|
const activeButtons = this.getActiveMarkupButton();
|
||||||
|
|
||||||
for(const i in this.buttons) {
|
for(const i in this.buttons) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const button = this.buttons[i];
|
const button = this.buttons[i];
|
||||||
if(button != activeButton) {
|
button.classList.toggle('active', activeButtons.includes(button));
|
||||||
button.classList.remove('active');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(activeButton) {
|
|
||||||
activeButton.classList.add('active');
|
|
||||||
}
|
|
||||||
|
|
||||||
return activeButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
private setTooltipPosition(isLinkToggle = false) {
|
private setTooltipPosition(isLinkToggle = false) {
|
||||||
const selection = document.getSelection();
|
const selection = document.getSelection();
|
||||||
const range = selection.getRangeAt(0);
|
const range = selection.getRangeAt(0);
|
||||||
@ -253,7 +261,6 @@ export default class MarkupTooltip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const selection = document.getSelection();
|
const selection = document.getSelection();
|
||||||
|
|
||||||
if(!selection.toString().trim().length) {
|
if(!selection.toString().trim().length) {
|
||||||
this.hide();
|
this.hide();
|
||||||
return;
|
return;
|
||||||
@ -285,21 +292,19 @@ export default class MarkupTooltip {
|
|||||||
|
|
||||||
this.container.classList.add('is-visible');
|
this.container.classList.add('is-visible');
|
||||||
|
|
||||||
//console.log('selection', selectionRect, activeButton);
|
//this.log('selection', selectionRect, activeButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onMouseUp = (e: Event) => {
|
/* private onMouseUp = (e: Event) => {
|
||||||
|
this.log('onMouseUp');
|
||||||
if(findUpClassName(e.target, 'markup-tooltip')) return;
|
if(findUpClassName(e.target, 'markup-tooltip')) return;
|
||||||
/* if(isTouchSupported) {
|
|
||||||
this.appImManager.chat.input.messageInput.focus();
|
|
||||||
cancelEvent(e);
|
|
||||||
} */
|
|
||||||
|
|
||||||
this.hide();
|
this.hide();
|
||||||
document.removeEventListener('mouseup', this.onMouseUp);
|
//document.removeEventListener('mouseup', this.onMouseUp);
|
||||||
};
|
}; */
|
||||||
|
|
||||||
private onMouseUpSingle = (e: Event) => {
|
private onMouseUpSingle = (e: Event) => {
|
||||||
|
//this.log('onMouseUpSingle');
|
||||||
this.waitingForMouseUp = false;
|
this.waitingForMouseUp = false;
|
||||||
|
|
||||||
if(isTouchSupported) {
|
if(isTouchSupported) {
|
||||||
@ -314,39 +319,51 @@ export default class MarkupTooltip {
|
|||||||
|
|
||||||
this.show();
|
this.show();
|
||||||
|
|
||||||
!isTouchSupported && document.addEventListener('mouseup', this.onMouseUp);
|
//!isTouchSupported && document.addEventListener('mouseup', this.onMouseUp);
|
||||||
};
|
};
|
||||||
|
|
||||||
public setMouseUpEvent() {
|
public setMouseUpEvent() {
|
||||||
if(this.waitingForMouseUp) return;
|
if(this.waitingForMouseUp) return;
|
||||||
this.waitingForMouseUp = true;
|
this.waitingForMouseUp = true;
|
||||||
|
|
||||||
console.log('[MARKUP]: setMouseUpEvent');
|
//this.log('setMouseUpEvent');
|
||||||
|
|
||||||
document.addEventListener('mouseup', this.onMouseUpSingle, {once: true});
|
document.addEventListener('mouseup', this.onMouseUpSingle, {once: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public cancelClosening() {
|
||||||
|
if(isTouchSupported && !isApple) {
|
||||||
|
document.removeEventListener('mouseup', this.onMouseUpSingle);
|
||||||
|
document.addEventListener('mouseup', (e) => {
|
||||||
|
cancelEvent(e);
|
||||||
|
this.mouseUpCounter = 1;
|
||||||
|
this.waitingForMouseUp = false;
|
||||||
|
this.setMouseUpEvent();
|
||||||
|
}, {once: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public handleSelection() {
|
public handleSelection() {
|
||||||
if(this.addedListener) return;
|
if(this.addedListener) return;
|
||||||
this.addedListener = true;
|
this.addedListener = true;
|
||||||
document.addEventListener('selectionchange', (e) => {
|
document.addEventListener('selectionchange', (e) => {
|
||||||
if(document.activeElement == this.linkInput) {
|
//this.log('selectionchange');
|
||||||
|
|
||||||
|
if(document.activeElement === this.linkInput) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(document.activeElement != this.appImManager.chat.input.messageInput) {
|
if(document.activeElement !== this.appImManager.chat.input.messageInput) {
|
||||||
this.hide();
|
this.hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selection = document.getSelection();
|
const selection = document.getSelection();
|
||||||
|
|
||||||
if(!selection.toString().trim().length) {
|
if(!selection.toString().trim().length) {
|
||||||
this.hide();
|
this.hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[MARKUP]: selectionchange');
|
|
||||||
if(isTouchSupported) {
|
if(isTouchSupported) {
|
||||||
if(isApple) {
|
if(isApple) {
|
||||||
this.show();
|
this.show();
|
||||||
|
@ -8,6 +8,7 @@ import RadioField from "../radioField";
|
|||||||
import Scrollable from "../scrollable";
|
import Scrollable from "../scrollable";
|
||||||
import { toast } from "../toast";
|
import { toast } from "../toast";
|
||||||
import SendContextMenu from "../chat/sendContextMenu";
|
import SendContextMenu from "../chat/sendContextMenu";
|
||||||
|
import { MessageEntity } from "../../layer";
|
||||||
|
|
||||||
const MAX_LENGTH_QUESTION = 255;
|
const MAX_LENGTH_QUESTION = 255;
|
||||||
const MAX_LENGTH_OPTION = 100;
|
const MAX_LENGTH_OPTION = 100;
|
||||||
@ -187,7 +188,8 @@ export default class PopupCreatePoll extends PopupElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const quizSolution = this.quizSolutionField.value || undefined;
|
const quizSolutionEntities: MessageEntity[] = [];
|
||||||
|
const quizSolution = getRichValue(this.quizSolutionField.input, quizSolutionEntities) || undefined;
|
||||||
if(quizSolution?.length > MAX_LENGTH_SOLUTION) {
|
if(quizSolution?.length > MAX_LENGTH_SOLUTION) {
|
||||||
toast('Explanation is too long.');
|
toast('Explanation is too long.');
|
||||||
return;
|
return;
|
||||||
@ -236,7 +238,7 @@ export default class PopupCreatePoll extends PopupElement {
|
|||||||
};
|
};
|
||||||
//poll.id = randomIDS;
|
//poll.id = randomIDS;
|
||||||
|
|
||||||
const inputMediaPoll = this.chat.appPollsManager.getInputMediaPoll(poll, this.correctAnswers, quizSolution);
|
const inputMediaPoll = this.chat.appPollsManager.getInputMediaPoll(poll, this.correctAnswers, quizSolution, quizSolutionEntities);
|
||||||
|
|
||||||
//console.log('Will try to create poll:', inputMediaPoll);
|
//console.log('Will try to create poll:', inputMediaPoll);
|
||||||
|
|
||||||
|
@ -333,7 +333,9 @@ export default class PopupDatePicker extends PopupElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.container.classList.toggle('is-max-lines', (this.month.childElementCount / 7) > 6);
|
const lines = this.month.childElementCount / 7;
|
||||||
|
this.container.dataset.lines = '' + lines;
|
||||||
|
this.container.classList.toggle('is-max-lines', lines > 6);
|
||||||
|
|
||||||
this.monthsContainer.append(this.month);
|
this.monthsContainer.append(this.month);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
import { MessageEntity } from "../layer";
|
||||||
import { MOUNT_CLASS_TO } from "../lib/mtproto/mtproto_config";
|
import { MOUNT_CLASS_TO } from "../lib/mtproto/mtproto_config";
|
||||||
|
import RichTextProcessor from "../lib/richtextprocessor";
|
||||||
import ListenerSetter from "./listenerSetter";
|
import ListenerSetter from "./listenerSetter";
|
||||||
import { isTouchSupported } from "./touchSupport";
|
import { isTouchSupported } from "./touchSupport";
|
||||||
import { isSafari } from "./userAgent";
|
import { isSafari } from "./userAgent";
|
||||||
@ -101,7 +103,7 @@ export function placeCaretAtEnd(el: HTMLElement) {
|
|||||||
return len;
|
return len;
|
||||||
} */
|
} */
|
||||||
|
|
||||||
export function getRichValue(field: HTMLElement) {
|
export function getRichValue(field: HTMLElement, entities?: MessageEntity[]) {
|
||||||
if(!field) {
|
if(!field) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@ -109,7 +111,7 @@ export function getRichValue(field: HTMLElement) {
|
|||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
const line: string[] = [];
|
const line: string[] = [];
|
||||||
|
|
||||||
getRichElementValue(field, lines, line);
|
getRichElementValue(field, lines, line, undefined, undefined, entities);
|
||||||
if(line.length) {
|
if(line.length) {
|
||||||
lines.push(line.join(''));
|
lines.push(line.join(''));
|
||||||
}
|
}
|
||||||
@ -117,6 +119,12 @@ export function getRichValue(field: HTMLElement) {
|
|||||||
let value = lines.join('\n');
|
let value = lines.join('\n');
|
||||||
value = value.replace(/\u00A0/g, ' ');
|
value = value.replace(/\u00A0/g, ' ');
|
||||||
|
|
||||||
|
if(entities) {
|
||||||
|
RichTextProcessor.combineSameEntities(entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('getRichValue:', value, entities);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,64 +142,78 @@ const markdownTypes = {
|
|||||||
export type MarkdownType = 'bold' | 'italic' | 'underline' | 'strikethrough' | 'monospace' | 'link';
|
export type MarkdownType = 'bold' | 'italic' | 'underline' | 'strikethrough' | 'monospace' | 'link';
|
||||||
export type MarkdownTag = {
|
export type MarkdownTag = {
|
||||||
match: string,
|
match: string,
|
||||||
markdown: string | ((node: HTMLElement) => string)
|
markdown: string | ((node: HTMLElement) => string),
|
||||||
|
entityName: 'messageEntityBold' | 'messageEntityUnderline' | 'messageEntityItalic' | 'messageEntityPre' | 'messageEntityStrike' | 'messageEntityTextUrl';
|
||||||
};
|
};
|
||||||
export const markdownTags: {[type in MarkdownType]: MarkdownTag} = {
|
export const markdownTags: {[type in MarkdownType]: MarkdownTag} = {
|
||||||
bold: {
|
bold: {
|
||||||
match: '[style*="font-weight"]',
|
match: '[style*="font-weight"], b',
|
||||||
markdown: markdownTypes.bold
|
markdown: markdownTypes.bold,
|
||||||
|
entityName: 'messageEntityBold'
|
||||||
},
|
},
|
||||||
underline: {
|
underline: {
|
||||||
match: isSafari ? '[style="text-decoration: underline;"]' : '[style="text-decoration-line: underline;"]',
|
match: '[style*="underline"], u',
|
||||||
markdown: markdownTypes.underline
|
markdown: markdownTypes.underline,
|
||||||
|
entityName: 'messageEntityUnderline'
|
||||||
},
|
},
|
||||||
italic: {
|
italic: {
|
||||||
match: '[style="font-style: italic;"]',
|
match: '[style*="italic"], i',
|
||||||
markdown: markdownTypes.italic
|
markdown: markdownTypes.italic,
|
||||||
|
entityName: 'messageEntityItalic'
|
||||||
},
|
},
|
||||||
monospace: {
|
monospace: {
|
||||||
match: '[style="font-family: monospace;"]',
|
match: '[style*="monospace"], [face="monospace"]',
|
||||||
markdown: markdownTypes.monospace
|
markdown: markdownTypes.monospace,
|
||||||
|
entityName: 'messageEntityPre'
|
||||||
},
|
},
|
||||||
strikethrough: {
|
strikethrough: {
|
||||||
match: isSafari ? '[style="text-decoration: line-through;"]' : '[style="text-decoration-line: line-through;"]',
|
match: '[style*="line-through"], strike',
|
||||||
markdown: markdownTypes.strikethrough
|
markdown: markdownTypes.strikethrough,
|
||||||
|
entityName: 'messageEntityStrike'
|
||||||
},
|
},
|
||||||
link: {
|
link: {
|
||||||
match: 'A',
|
match: 'A',
|
||||||
markdown: (node: HTMLElement) => `[${(node.parentElement as HTMLAnchorElement).href}](${node.nodeValue})`
|
markdown: (node: HTMLElement) => `[${(node.parentElement as HTMLAnchorElement).href}](${node.nodeValue})`,
|
||||||
|
entityName: 'messageEntityTextUrl'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export function getRichElementValue(node: HTMLElement, lines: string[], line: string[], selNode?: Node, selOffset?: number) {
|
export function getRichElementValue(node: HTMLElement, lines: string[], line: string[], selNode?: Node, selOffset?: number, entities?: MessageEntity[], offset = {offset: 0}) {
|
||||||
if(node.nodeType == 3) { // TEXT
|
if(node.nodeType == 3) { // TEXT
|
||||||
if(selNode === node) {
|
if(selNode === node) {
|
||||||
const value = node.nodeValue;
|
const value = node.nodeValue;
|
||||||
line.push(value.substr(0, selOffset) + '\x01' + value.substr(selOffset));
|
line.push(value.substr(0, selOffset) + '\x01' + value.substr(selOffset));
|
||||||
} else {
|
} else {
|
||||||
let markdown: string;
|
const nodeValue = node.nodeValue;
|
||||||
|
line.push(nodeValue);
|
||||||
|
|
||||||
|
if(entities && nodeValue.trim()) {
|
||||||
if(node.parentNode) {
|
if(node.parentNode) {
|
||||||
const parentElement = node.parentElement;
|
const parentElement = node.parentElement;
|
||||||
|
|
||||||
let markdownTag: MarkdownTag;
|
|
||||||
for(const type in markdownTags) {
|
for(const type in markdownTags) {
|
||||||
const tag = markdownTags[type as MarkdownType];
|
const tag = markdownTags[type as MarkdownType];
|
||||||
if(parentElement.matches(tag.match)) {
|
const closest = parentElement.closest(tag.match + ', [contenteditable]');
|
||||||
markdownTag = tag;
|
if(closest && closest.getAttribute('contenteditable') === null) {
|
||||||
break;
|
if(tag.entityName === 'messageEntityTextUrl') {
|
||||||
|
entities.push({
|
||||||
|
_: tag.entityName as any,
|
||||||
|
url: (parentElement as HTMLAnchorElement).href,
|
||||||
|
offset: offset.offset,
|
||||||
|
length: nodeValue.length
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
entities.push({
|
||||||
|
_: tag.entityName as any,
|
||||||
|
offset: offset.offset,
|
||||||
|
length: nodeValue.length
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(markdownTag) {
|
offset.offset += nodeValue.length;
|
||||||
if(typeof(markdownTag.markdown) === 'function') {
|
|
||||||
line.push(markdownTag.markdown(node));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
markdown = markdownTag.markdown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
line.push(markdown && node.nodeValue.trim() ? '\x01' + markdown + node.nodeValue + markdown + '\x01' : node.nodeValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -207,8 +229,10 @@ export function getRichElementValue(node: HTMLElement, lines: string[], line: st
|
|||||||
lines.push(line.join(''));
|
lines.push(line.join(''));
|
||||||
line.splice(0, line.length);
|
line.splice(0, line.length);
|
||||||
} else if(node.tagName == 'IMG') {
|
} else if(node.tagName == 'IMG') {
|
||||||
if((node as HTMLImageElement).alt) {
|
const alt = (node as HTMLImageElement).alt;
|
||||||
line.push((node as HTMLImageElement).alt);
|
if(alt) {
|
||||||
|
line.push(alt);
|
||||||
|
offset.offset += alt.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +242,7 @@ export function getRichElementValue(node: HTMLElement, lines: string[], line: st
|
|||||||
|
|
||||||
let curChild = node.firstChild as HTMLElement;
|
let curChild = node.firstChild as HTMLElement;
|
||||||
while(curChild) {
|
while(curChild) {
|
||||||
getRichElementValue(curChild, lines, line, selNode, selOffset);
|
getRichElementValue(curChild, lines, line, selNode, selOffset, entities, offset);
|
||||||
curChild = curChild.nextSibling as any;
|
curChild = curChild.nextSibling as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,6 +457,13 @@ export class AppImManager {
|
|||||||
|
|
||||||
const spliced = this.chats.splice(fromIndex, this.chats.length - fromIndex);
|
const spliced = this.chats.splice(fromIndex, this.chats.length - fromIndex);
|
||||||
|
|
||||||
|
// * fix middle chat z-index on animation
|
||||||
|
if(spliced.length > 1) {
|
||||||
|
spliced.slice(0, -1).forEach(chat => {
|
||||||
|
chat.container.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.chatsSelectTab(this.chat.container);
|
this.chatsSelectTab(this.chat.container);
|
||||||
|
|
||||||
if(justReturn) {
|
if(justReturn) {
|
||||||
|
@ -397,11 +397,8 @@ export class AppMessagesManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let entities = options.entities;
|
let entities = options.entities || [];
|
||||||
if(typeof(text) === 'string' && !entities) {
|
|
||||||
entities = [];
|
|
||||||
text = RichTextProcessor.parseMarkdown(text, entities);
|
text = RichTextProcessor.parseMarkdown(text, entities);
|
||||||
}
|
|
||||||
|
|
||||||
const schedule_date = options.scheduleDate || (message.pFlags.is_scheduled ? message.date : undefined);
|
const schedule_date = options.scheduleDate || (message.pFlags.is_scheduled ? message.date : undefined);
|
||||||
return apiManager.invokeApi('messages.editMessage', {
|
return apiManager.invokeApi('messages.editMessage', {
|
||||||
@ -494,7 +491,7 @@ export class AppMessagesManager {
|
|||||||
reply_to: this.generateReplyHeader(options.replyToMsgId, options.threadId),
|
reply_to: this.generateReplyHeader(options.replyToMsgId, options.threadId),
|
||||||
via_bot_id: options.viaBotId,
|
via_bot_id: options.viaBotId,
|
||||||
reply_markup: options.reply_markup,
|
reply_markup: options.reply_markup,
|
||||||
entities: entities,
|
entities,
|
||||||
views: isBroadcast && 1,
|
views: isBroadcast && 1,
|
||||||
pending: true
|
pending: true
|
||||||
};
|
};
|
||||||
@ -641,8 +638,8 @@ export class AppMessagesManager {
|
|||||||
|
|
||||||
this.log('sendFile', file, fileType);
|
this.log('sendFile', file, fileType);
|
||||||
|
|
||||||
|
const entities = options.entities || [];
|
||||||
if(caption) {
|
if(caption) {
|
||||||
let entities = options.entities || [];
|
|
||||||
caption = RichTextProcessor.parseMarkdown(caption, entities);
|
caption = RichTextProcessor.parseMarkdown(caption, entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -792,6 +789,7 @@ export class AppMessagesManager {
|
|||||||
id: messageId,
|
id: messageId,
|
||||||
from_id: this.generateFromId(peerId),
|
from_id: this.generateFromId(peerId),
|
||||||
peer_id: appPeersManager.getOutputPeer(peerId),
|
peer_id: appPeersManager.getOutputPeer(peerId),
|
||||||
|
entities,
|
||||||
pFlags,
|
pFlags,
|
||||||
date,
|
date,
|
||||||
message: caption,
|
message: caption,
|
||||||
@ -913,7 +911,8 @@ export class AppMessagesManager {
|
|||||||
random_id: randomIdS,
|
random_id: randomIdS,
|
||||||
reply_to_msg_id: replyToMsgId,
|
reply_to_msg_id: replyToMsgId,
|
||||||
schedule_date: options.scheduleDate,
|
schedule_date: options.scheduleDate,
|
||||||
silent: options.silent
|
silent: options.silent,
|
||||||
|
entities
|
||||||
}).then((updates) => {
|
}).then((updates) => {
|
||||||
apiUpdatesManager.processUpdateMessage(updates);
|
apiUpdatesManager.processUpdateMessage(updates);
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
@ -960,9 +959,8 @@ export class AppMessagesManager {
|
|||||||
const replyToMsgId = options.replyToMsgId ? this.getLocalMessageId(options.replyToMsgId) : undefined;
|
const replyToMsgId = options.replyToMsgId ? this.getLocalMessageId(options.replyToMsgId) : undefined;
|
||||||
|
|
||||||
let caption = options.caption || '';
|
let caption = options.caption || '';
|
||||||
let entities: MessageEntity[];
|
let entities = options.entities || [];
|
||||||
if(caption) {
|
if(caption) {
|
||||||
entities = options.entities || [];
|
|
||||||
caption = RichTextProcessor.parseMarkdown(caption, entities);
|
caption = RichTextProcessor.parseMarkdown(caption, entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { copy } from "../../helpers/object";
|
import { copy } from "../../helpers/object";
|
||||||
import { InputMedia } from "../../layer";
|
import { InputMedia, MessageEntity } from "../../layer";
|
||||||
import { logger, LogLevels } from "../logger";
|
import { logger, LogLevels } from "../logger";
|
||||||
import apiManager from "../mtproto/mtprotoworker";
|
import apiManager from "../mtproto/mtprotoworker";
|
||||||
import { MOUNT_CLASS_TO } from "../mtproto/mtproto_config";
|
import { MOUNT_CLASS_TO } from "../mtproto/mtproto_config";
|
||||||
@ -143,11 +143,13 @@ export class AppPollsManager {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getInputMediaPoll(poll: Poll, correctAnswers?: Uint8Array[], solution?: string): InputMedia.inputMediaPoll {
|
public getInputMediaPoll(poll: Poll, correctAnswers?: Uint8Array[], solution?: string, solutionEntities?: MessageEntity[]): InputMedia.inputMediaPoll {
|
||||||
let solution_entities: any[];
|
|
||||||
if(solution) {
|
if(solution) {
|
||||||
solution_entities = [];
|
if(!solutionEntities) {
|
||||||
solution = RichTextProcessor.parseMarkdown(solution, solution_entities);
|
solutionEntities = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
solution = RichTextProcessor.parseMarkdown(solution, solutionEntities);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -155,7 +157,7 @@ export class AppPollsManager {
|
|||||||
poll,
|
poll,
|
||||||
correct_answers: correctAnswers,
|
correct_answers: correctAnswers,
|
||||||
solution,
|
solution,
|
||||||
solution_entities
|
solution_entities: solutionEntities
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,11 +215,12 @@ namespace RichTextProcessor {
|
|||||||
})
|
})
|
||||||
} */
|
} */
|
||||||
|
|
||||||
export function parseMarkdown(text: string, entities: MessageEntity[], noTrim?: any): string {
|
export function parseMarkdown(text: string, currentEntities: MessageEntity[], noTrim?: any): string {
|
||||||
/* if(!markdownTestRegExp.test(text)) {
|
/* if(!markdownTestRegExp.test(text)) {
|
||||||
return noTrim ? text : text.trim();
|
return noTrim ? text : text.trim();
|
||||||
} */
|
} */
|
||||||
|
|
||||||
|
const entities: MessageEntity[] = [];
|
||||||
let raw = text;
|
let raw = text;
|
||||||
let match;
|
let match;
|
||||||
let newText: any = [];
|
let newText: any = [];
|
||||||
@ -302,68 +303,12 @@ namespace RichTextProcessor {
|
|||||||
newText = newText.trim();
|
newText = newText.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mergeEntities(currentEntities, entities);
|
||||||
|
combineSameEntities(currentEntities);
|
||||||
|
|
||||||
return newText;
|
return newText;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* export function mergeEntities(currentEntities: MessageEntity[], newEntities: MessageEntity[], fromApi?: boolean) {
|
|
||||||
const totalEntities = newEntities.slice();
|
|
||||||
const newLength = newEntities.length;
|
|
||||||
let startJ = 0;
|
|
||||||
for(let i = 0, length = currentEntities.length; i < length; i++) {
|
|
||||||
const curEntity = currentEntities[i];
|
|
||||||
// if(fromApi &&
|
|
||||||
// curEntity._ != 'messageEntityLinebreak' &&
|
|
||||||
// curEntity._ != 'messageEntityEmoji') {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// console.log('s', curEntity, newEntities);
|
|
||||||
const start = curEntity.offset;
|
|
||||||
const end = start + curEntity.length;
|
|
||||||
let bad = false;
|
|
||||||
for(let j = startJ; j < newLength; j++) {
|
|
||||||
const newEntity = newEntities[j];
|
|
||||||
const cStart = newEntity.offset;
|
|
||||||
const cEnd = cStart + newEntity.length;
|
|
||||||
if(cStart <= start) {
|
|
||||||
startJ = j;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(start >= cStart && start < cEnd ||
|
|
||||||
end > cStart && end <= cEnd) {
|
|
||||||
// console.log('bad', curEntity, newEntity)
|
|
||||||
if(fromApi && start >= cStart && end <= cEnd) {
|
|
||||||
if(newEntity.nested === undefined) {
|
|
||||||
newEntity.nested = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
curEntity.offset -= cStart;
|
|
||||||
newEntity.nested.push(copy(curEntity));
|
|
||||||
}
|
|
||||||
|
|
||||||
bad = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cStart >= end) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(bad) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
totalEntities.push(curEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
totalEntities.sort((a, b) => {
|
|
||||||
return a.offset - b.offset;
|
|
||||||
});
|
|
||||||
// console.log('merge', currentEntities, newEntities, totalEntities)
|
|
||||||
return totalEntities;
|
|
||||||
} */
|
|
||||||
|
|
||||||
export function mergeEntities(currentEntities: MessageEntity[], newEntities: MessageEntity[]) {
|
export function mergeEntities(currentEntities: MessageEntity[], newEntities: MessageEntity[]) {
|
||||||
currentEntities = currentEntities.slice();
|
currentEntities = currentEntities.slice();
|
||||||
const filtered = newEntities.filter(e => !currentEntities.find(_e => e._ == _e._ && e.offset == _e.offset && e.length == _e.length));
|
const filtered = newEntities.filter(e => !currentEntities.find(_e => e._ == _e._ && e.offset == _e.offset && e.length == _e.length));
|
||||||
@ -372,14 +317,23 @@ namespace RichTextProcessor {
|
|||||||
return currentEntities;
|
return currentEntities;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* export function wrapRichNestedText(text: string, nested: MessageEntity[], options: any) {
|
export function combineSameEntities(entities: MessageEntity[]) {
|
||||||
if(nested === undefined) {
|
//entities = entities.slice();
|
||||||
return encodeEntities(text);
|
for(let i = 0; i < entities.length; ++i) {
|
||||||
}
|
const entity = entities[i];
|
||||||
|
|
||||||
options.hasNested = true;
|
let nextEntityIdx = -1;
|
||||||
return wrapRichText(text, {entities: nested, nested: true});
|
do {
|
||||||
} */
|
nextEntityIdx = entities.findIndex((e, _i) => _i !== i && e._ === entity._ && (e.offset - entity.length) === entity.offset);
|
||||||
|
if(nextEntityIdx !== -1) {
|
||||||
|
const nextEntity = entities[nextEntityIdx];
|
||||||
|
entity.length += nextEntity.length;
|
||||||
|
entities.splice(nextEntityIdx, 1);
|
||||||
|
}
|
||||||
|
} while(nextEntityIdx !== -1);
|
||||||
|
}
|
||||||
|
//return entities;
|
||||||
|
}
|
||||||
|
|
||||||
export function wrapRichText(text: string, options: Partial<{
|
export function wrapRichText(text: string, options: Partial<{
|
||||||
entities: MessageEntity[],
|
entities: MessageEntity[],
|
||||||
@ -620,365 +574,6 @@ namespace RichTextProcessor {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* export function wrapRichTextOld(text: string, options: Partial<{
|
|
||||||
entities: MessageEntity[],
|
|
||||||
contextSite: string,
|
|
||||||
highlightUsername: string,
|
|
||||||
noLinks: true,
|
|
||||||
noLinebreaks: true,
|
|
||||||
noCommands: true,
|
|
||||||
wrappingDraft: true,
|
|
||||||
fromBot: boolean,
|
|
||||||
noTextFormat: true,
|
|
||||||
passEntities: Partial<{
|
|
||||||
[_ in MessageEntity['_']]: true
|
|
||||||
}>,
|
|
||||||
|
|
||||||
nested?: true,
|
|
||||||
contextHashtag?: string
|
|
||||||
}> = {}) {
|
|
||||||
if(!text || !text.length) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
const passEntities: typeof options.passEntities = options.passEntities || {};
|
|
||||||
const entities = options.entities || parseEntities(text);
|
|
||||||
const contextSite = options.contextSite || 'Telegram';
|
|
||||||
const contextExternal = contextSite != 'Telegram';
|
|
||||||
|
|
||||||
//console.log('wrapRichText got entities:', text, entities);
|
|
||||||
const html: string[] = [];
|
|
||||||
let lastOffset = 0;
|
|
||||||
for(let i = 0, len = entities.length; i < len; i++) {
|
|
||||||
const entity = entities[i];
|
|
||||||
if(entity.offset > lastOffset) {
|
|
||||||
html.push(
|
|
||||||
encodeEntities(text.substr(lastOffset, entity.offset - lastOffset))
|
|
||||||
);
|
|
||||||
} else if(entity.offset < lastOffset) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let skipEntity = false;
|
|
||||||
const entityText = text.substr(entity.offset, entity.length);
|
|
||||||
switch(entity._) {
|
|
||||||
case 'messageEntityMention':
|
|
||||||
var contextUrl = !options.noLinks && siteMentions[contextSite]
|
|
||||||
if (!contextUrl) {
|
|
||||||
skipEntity = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
var username = entityText.substr(1)
|
|
||||||
var attr = ''
|
|
||||||
if (options.highlightUsername &&
|
|
||||||
options.highlightUsername.toLowerCase() == username.toLowerCase()) {
|
|
||||||
attr = 'class="im_message_mymention"'
|
|
||||||
}
|
|
||||||
html.push(
|
|
||||||
'<a ',
|
|
||||||
attr,
|
|
||||||
contextExternal ? ' target="_blank" rel="noopener noreferrer" ' : '',
|
|
||||||
' href="',
|
|
||||||
contextUrl.replace('{1}', encodeURIComponent(username)),
|
|
||||||
'">',
|
|
||||||
wrapRichNestedText(entityText, entity.nested, options),
|
|
||||||
//encodeEntities(entityText),
|
|
||||||
'</a>'
|
|
||||||
)
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'messageEntityMentionName':
|
|
||||||
if(options.noLinks) {
|
|
||||||
skipEntity = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
html.push(
|
|
||||||
'<a href="#/im?p=u',
|
|
||||||
encodeURIComponent(entity.user_id),
|
|
||||||
'">',
|
|
||||||
wrapRichNestedText(entityText, entity.nested, options),
|
|
||||||
'</a>'
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'messageEntityHashtag':
|
|
||||||
var contextUrl = !options.noLinks && siteHashtags[contextSite];
|
|
||||||
if(!contextUrl) {
|
|
||||||
skipEntity = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hashtag = entityText.substr(1);
|
|
||||||
html.push(
|
|
||||||
'<a ',
|
|
||||||
contextExternal ? ' target="_blank" rel="noopener noreferrer" ' : '',
|
|
||||||
'href="',
|
|
||||||
contextUrl.replace('{1}', encodeURIComponent(hashtag))
|
|
||||||
,
|
|
||||||
'">',
|
|
||||||
encodeEntities(entityText),
|
|
||||||
'</a>'
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'messageEntityEmail':
|
|
||||||
if(options.noLinks) {
|
|
||||||
skipEntity = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
html.push(
|
|
||||||
'<a href="',
|
|
||||||
encodeEntities('mailto:' + entityText),
|
|
||||||
'" target="_blank" rel="noopener noreferrer">',
|
|
||||||
encodeEntities(entityText),
|
|
||||||
'</a>'
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'messageEntityUrl':
|
|
||||||
case 'messageEntityTextUrl':
|
|
||||||
let inner: string;
|
|
||||||
let url: string;
|
|
||||||
if(entity._ == 'messageEntityTextUrl') {
|
|
||||||
url = (entity as MessageEntity.messageEntityTextUrl).url;
|
|
||||||
url = wrapUrl(url, true);
|
|
||||||
inner = wrapRichNestedText(entityText, entity.nested, options);
|
|
||||||
} else {
|
|
||||||
url = wrapUrl(entityText, false);
|
|
||||||
inner = encodeEntities(replaceUrlEncodings(entityText));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(options.noLinks && !passEntities[entity._]) {
|
|
||||||
html.push(inner);
|
|
||||||
} else {
|
|
||||||
html.push(
|
|
||||||
'<a href="',
|
|
||||||
encodeEntities(url),
|
|
||||||
'" target="_blank" rel="noopener noreferrer">',
|
|
||||||
inner,
|
|
||||||
'</a>'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'messageEntityLinebreak':
|
|
||||||
html.push(options.noLinebreaks ? ' ' : '<br/>');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'messageEntityEmoji':
|
|
||||||
if(options.wrappingDraft && emojiSupported) { // * fix safari emoji
|
|
||||||
html.push(encodeEntities(entityText));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
html.push(emojiSupported ? // ! contenteditable="false" нужен для поля ввода, иначе там будет меняться шрифт в Safari, или же рендерить смайлик напрямую, без контейнера
|
|
||||||
`<span class="emoji">${encodeEntities(entityText)}</span>` :
|
|
||||||
`<img src="assets/img/emoji/${entity.unicode}.png" alt="${encodeEntities(entityText)}" class="emoji">`);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'messageEntityBotCommand':
|
|
||||||
if(options.noLinks || options.noCommands || contextExternal) {
|
|
||||||
skipEntity = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var command = entityText.substr(1);
|
|
||||||
var bot;
|
|
||||||
var atPos;
|
|
||||||
if ((atPos = command.indexOf('@')) != -1) {
|
|
||||||
bot = command.substr(atPos + 1);
|
|
||||||
command = command.substr(0, atPos);
|
|
||||||
} else {
|
|
||||||
bot = options.fromBot;
|
|
||||||
}
|
|
||||||
|
|
||||||
html.push(
|
|
||||||
'<a href="',
|
|
||||||
encodeEntities('tg://bot_command?command=' + encodeURIComponent(command) + (bot ? '&bot=' + encodeURIComponent(bot) : '')),
|
|
||||||
'">',
|
|
||||||
encodeEntities(entityText),
|
|
||||||
'</a>'
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'messageEntityBold': {
|
|
||||||
if(options.noTextFormat) {
|
|
||||||
html.push(wrapRichNestedText(entityText, entity.nested, options));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(options.wrappingDraft) {
|
|
||||||
html.push(`<span style="font-weight: bold;">${wrapRichNestedText(entityText, entity.nested, options)}</span>`);
|
|
||||||
} else {
|
|
||||||
html.push(`<strong>${wrapRichNestedText(entityText, entity.nested, options)}</strong>`);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'messageEntityItalic': {
|
|
||||||
if(options.noTextFormat) {
|
|
||||||
html.push(wrapRichNestedText(entityText, entity.nested, options));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(options.wrappingDraft) {
|
|
||||||
html.push(`<span style="font-style: italic;">${wrapRichNestedText(entityText, entity.nested, options)}</span>`);
|
|
||||||
} else {
|
|
||||||
html.push(`<em>${wrapRichNestedText(entityText, entity.nested, options)}</em>`);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'messageEntityHighlight':
|
|
||||||
html.push(
|
|
||||||
'<i>',
|
|
||||||
wrapRichNestedText(entityText, entity.nested, options),
|
|
||||||
'</i>'
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'messageEntityStrike':
|
|
||||||
if(options.wrappingDraft) {
|
|
||||||
const styleName = isSafari ? 'text-decoration' : 'text-decoration-line';
|
|
||||||
html.push(`<span style="${styleName}: line-through;">${wrapRichNestedText(entityText, entity.nested, options)}</span>`);
|
|
||||||
} else {
|
|
||||||
html.push(`<del>${wrapRichNestedText(entityText, entity.nested, options)}</del>`);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'messageEntityUnderline':
|
|
||||||
if(options.wrappingDraft) {
|
|
||||||
const styleName = isSafari ? 'text-decoration' : 'text-decoration-line';
|
|
||||||
html.push(`<span style="${styleName}: underline;">${wrapRichNestedText(entityText, entity.nested, options)}</span>`);
|
|
||||||
} else {
|
|
||||||
html.push(`<u>${wrapRichNestedText(entityText, entity.nested, options)}</u>`);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'messageEntityCode':
|
|
||||||
if(options.noTextFormat) {
|
|
||||||
html.push(encodeEntities(entityText));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(options.wrappingDraft) {
|
|
||||||
html.push(`<span style="font-family: monospace;">${encodeEntities(entityText)}</span>`);
|
|
||||||
} else {
|
|
||||||
html.push(
|
|
||||||
'<code>',
|
|
||||||
encodeEntities(entityText),
|
|
||||||
'</code>'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'messageEntityPre':
|
|
||||||
if(options.noTextFormat) {
|
|
||||||
html.push(encodeEntities(entityText));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
html.push(
|
|
||||||
'<pre><code', (entity.language ? ' class="language-' + encodeEntities(entity.language) + '"' : ''), '>',
|
|
||||||
encodeEntities(entityText),
|
|
||||||
'</code></pre>'
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
skipEntity = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastOffset = entity.offset + (skipEntity ? 0 : entity.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
html.push(encodeEntities(text.substr(lastOffset))); // may be empty string
|
|
||||||
//console.log(html);
|
|
||||||
text = html.join('');
|
|
||||||
|
|
||||||
return text;
|
|
||||||
} */
|
|
||||||
|
|
||||||
/* export function wrapDraftText(text: string, options: any = {}) {
|
|
||||||
if(!text || !text.length) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
var entities = options.entities;
|
|
||||||
if(entities === undefined) {
|
|
||||||
entities = parseEntities(text);
|
|
||||||
}
|
|
||||||
var i = 0;
|
|
||||||
var len = entities.length;
|
|
||||||
var entity;
|
|
||||||
var entityText;
|
|
||||||
var skipEntity;
|
|
||||||
var code = [];
|
|
||||||
var lastOffset = 0;
|
|
||||||
for(i = 0; i < len; i++) {
|
|
||||||
entity = entities[i];
|
|
||||||
if(entity.offset > lastOffset) {
|
|
||||||
code.push(
|
|
||||||
text.substr(lastOffset, entity.offset - lastOffset)
|
|
||||||
);
|
|
||||||
} else if(entity.offset < lastOffset) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
skipEntity = false;
|
|
||||||
entityText = text.substr(entity.offset, entity.length);
|
|
||||||
switch(entity._) {
|
|
||||||
case 'messageEntityEmoji':
|
|
||||||
code.push(
|
|
||||||
':',
|
|
||||||
entity.title,
|
|
||||||
':'
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'messageEntityCode':
|
|
||||||
code.push(
|
|
||||||
'`', entityText, '`'
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'messageEntityBold':
|
|
||||||
code.push(
|
|
||||||
'**', entityText, '**'
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'messageEntityItalic':
|
|
||||||
code.push(
|
|
||||||
'__', entityText, '__'
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'messageEntityPre':
|
|
||||||
code.push(
|
|
||||||
'```', entityText, '```'
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'messageEntityMentionName':
|
|
||||||
code.push(
|
|
||||||
'@', entity.user_id, ' (', entityText, ')'
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
skipEntity = true;
|
|
||||||
}
|
|
||||||
lastOffset = entity.offset + (skipEntity ? 0 : entity.length);
|
|
||||||
}
|
|
||||||
code.push(text.substr(lastOffset));
|
|
||||||
return code.join('');
|
|
||||||
} */
|
|
||||||
|
|
||||||
export function wrapDraftText(text: string, options: Partial<{
|
export function wrapDraftText(text: string, options: Partial<{
|
||||||
entities: MessageEntity[]
|
entities: MessageEntity[]
|
||||||
}> = {}) {
|
}> = {}) {
|
||||||
@ -998,31 +593,6 @@ namespace RichTextProcessor {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//const draftEntityTypes: MessageEntity['_'][] = (['messageEntityTextUrl', 'messageEntityEmoji'] as MessageEntity['_'][]).concat(Object.values(markdownEntities) as any);
|
|
||||||
/* const draftEntityTypes: Partial<{[_ in MessageEntity['_']]: true}> = {
|
|
||||||
messageEntityCode: true,
|
|
||||||
messageEntityPre: true,
|
|
||||||
messageEntityBold: true,
|
|
||||||
messageEntityItalic: true,
|
|
||||||
messageEntityStrike: true,
|
|
||||||
messageEntityEmoji: true,
|
|
||||||
messageEntityLinebreak: true,
|
|
||||||
messageEntityUnderline: true,
|
|
||||||
messageEntityTextUrl: true
|
|
||||||
};
|
|
||||||
export function wrapDraftText(text: string, options: Partial<{
|
|
||||||
entities: MessageEntity[]
|
|
||||||
}> = {}) {
|
|
||||||
const checkEntity = (entity: MessageEntity) => {
|
|
||||||
return draftEntityTypes[entity._];
|
|
||||||
};
|
|
||||||
const entities = options.entities ? options.entities.filter(entity => {
|
|
||||||
return draftEntityTypes[entity._];
|
|
||||||
}) : [];
|
|
||||||
|
|
||||||
return wrapRichText(text, {entities});
|
|
||||||
} */
|
|
||||||
|
|
||||||
export function checkBrackets(url: string) {
|
export function checkBrackets(url: string) {
|
||||||
var urlLength = url.length;
|
var urlLength = url.length;
|
||||||
var urlOpenBrackets = url.split('(').length - 1;
|
var urlOpenBrackets = url.split('(').length - 1;
|
||||||
@ -1150,29 +720,6 @@ namespace RichTextProcessor {
|
|||||||
return !text ? null : text.match(urlRegExp);
|
return !text ? null : text.match(urlRegExp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* const el = document.createElement('span');
|
|
||||||
export function getAbbreviation(str: string, onlyFirst = false) {
|
|
||||||
const wrapped = wrapEmojiText(str);
|
|
||||||
el.innerHTML = wrapped;
|
|
||||||
|
|
||||||
const childNodes = el.childNodes;
|
|
||||||
let first = '', last = '';
|
|
||||||
|
|
||||||
const firstNode = childNodes[0];
|
|
||||||
if('length' in firstNode) first = (firstNode as any).textContent.trim().charAt(0).toUpperCase();
|
|
||||||
else first = (firstNode as HTMLElement).outerHTML;
|
|
||||||
|
|
||||||
if(onlyFirst) return first;
|
|
||||||
|
|
||||||
if(str.indexOf(' ') !== -1) {
|
|
||||||
const lastNode = childNodes[childNodes.length - 1];
|
|
||||||
if(lastNode == firstNode) last = lastNode.textContent.split(' ').pop().trim().charAt(0).toUpperCase();
|
|
||||||
else if('length' in lastNode) last = (lastNode as any).textContent.trim().charAt(0).toUpperCase();
|
|
||||||
else last = (lastNode as HTMLElement).outerHTML;
|
|
||||||
}
|
|
||||||
|
|
||||||
return first + last;
|
|
||||||
} */
|
|
||||||
export function getAbbreviation(str: string, onlyFirst = false) {
|
export function getAbbreviation(str: string, onlyFirst = false) {
|
||||||
const splitted = str.trim().split(' ');
|
const splitted = str.trim().split(' ');
|
||||||
if(!splitted[0]) return '';
|
if(!splitted[0]) return '';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import CacheStorageController from './cacheStorage';
|
import CacheStorageController from './cacheStorage';
|
||||||
import { MOUNT_CLASS_TO } from './mtproto/mtproto_config';
|
import { DEBUG, MOUNT_CLASS_TO } from './mtproto/mtproto_config';
|
||||||
//import { stringify } from '../helpers/json';
|
//import { stringify } from '../helpers/json';
|
||||||
|
|
||||||
class AppStorage {
|
class AppStorage {
|
||||||
@ -72,13 +72,15 @@ class AppStorage {
|
|||||||
key = prefix + key;
|
key = prefix + key;
|
||||||
this.cache[key] = value;
|
this.cache[key] = value;
|
||||||
|
|
||||||
let perf = performance.now();
|
let perf = /* DEBUG */false ? performance.now() : 0;
|
||||||
value = JSON.stringify(value);
|
value = JSON.stringify(value);
|
||||||
|
|
||||||
|
if(perf) {
|
||||||
let elapsedTime = performance.now() - perf;
|
let elapsedTime = performance.now() - perf;
|
||||||
if(elapsedTime > 10) {
|
if(elapsedTime > 10) {
|
||||||
console.warn('LocalStorage set: stringify time by JSON.stringify:', elapsedTime, key);
|
console.warn('LocalStorage set: stringify time by JSON.stringify:', elapsedTime, key);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/* perf = performance.now();
|
/* perf = performance.now();
|
||||||
value = stringify(value);
|
value = stringify(value);
|
||||||
console.log('LocalStorage set: stringify time by own stringify:', performance.now() - perf); */
|
console.log('LocalStorage set: stringify time by own stringify:', performance.now() - perf); */
|
||||||
|
@ -9,11 +9,16 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity var(--layer-transition), transform var(--layer-transition), width var(--layer-transition);
|
transition: opacity var(--layer-transition), transform var(--layer-transition), width var(--layer-transition);
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
|
||||||
top: 0;
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
width: $widthRegular;
|
width: $widthRegular;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
&-wrapper {
|
&-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -27,6 +32,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
transition: transform var(--layer-transition);
|
transition: transform var(--layer-transition);
|
||||||
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-tools {
|
&-tools {
|
||||||
@ -34,6 +40,8 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: $padding;
|
padding: $padding;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
width: $widthRegular;
|
width: $widthRegular;
|
||||||
|
@ -142,6 +142,14 @@
|
|||||||
width: 312px;
|
width: 312px;
|
||||||
padding: 4px 14px 14px 14px;
|
padding: 4px 14px 14px 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&[data-lines="5"] {
|
||||||
|
top: -16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-lines="7"] {
|
||||||
|
top: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.date-picker {
|
.date-picker {
|
||||||
|
Loading…
Reference in New Issue
Block a user