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 opusDecodeController from "../../lib/opusDecodeController";
|
||||
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 emoticonsDropdown from "../emoticonsDropdown";
|
||||
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
|
||||
const inputType = (e as InputEvent).inputType;
|
||||
//console.log('message beforeinput event', e);
|
||||
@ -597,7 +597,7 @@ export default class ChatInput {
|
||||
cancelEvent(e); // * cancel legacy markdown event
|
||||
}
|
||||
}
|
||||
});
|
||||
}); */
|
||||
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
|
||||
*/
|
||||
const checkForSingle = () => {
|
||||
/* const checkForSingle = () => {
|
||||
const nodes = getSelectedNodes();
|
||||
//console.log('Using formatting:', commandsMap[type], nodes, this.executedHistory);
|
||||
|
||||
@ -686,11 +686,13 @@ export default class ChatInput {
|
||||
executed.push(document.execCommand('styleWithCSS', false, 'false'));
|
||||
//}
|
||||
}
|
||||
};
|
||||
}; */
|
||||
|
||||
executed.push(document.execCommand('styleWithCSS', false, 'true'));
|
||||
|
||||
//if(type === 'monospace') {
|
||||
if(type === 'monospace') {
|
||||
let haveThisType = false;
|
||||
executed.push(document.execCommand('styleWithCSS', false, 'true'));
|
||||
//executed.push(document.execCommand('styleWithCSS', false, 'true'));
|
||||
|
||||
const selection = window.getSelection();
|
||||
if(!selection.isCollapsed) {
|
||||
@ -703,18 +705,20 @@ export default class ChatInput {
|
||||
}
|
||||
}
|
||||
|
||||
executed.push(document.execCommand('removeFormat', false, null));
|
||||
|
||||
if(!haveThisType) {
|
||||
//executed.push(document.execCommand('removeFormat', false, null));
|
||||
|
||||
if(haveThisType) {
|
||||
executed.push(document.execCommand('fontName', false, 'Roboto'));
|
||||
} else {
|
||||
executed.push(typeof(command) === 'function' ? command() : document.execCommand(command, false, null));
|
||||
}
|
||||
|
||||
executed.push(document.execCommand('styleWithCSS', false, 'false'));
|
||||
/* } else {
|
||||
} else {
|
||||
executed.push(typeof(command) === 'function' ? command() : document.execCommand(command, false, null));
|
||||
} */
|
||||
}
|
||||
|
||||
checkForSingle();
|
||||
executed.push(document.execCommand('styleWithCSS', false, 'false'));
|
||||
|
||||
//checkForSingle();
|
||||
saveExecuted();
|
||||
if(this.appImManager.markupTooltip) {
|
||||
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)));
|
||||
//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 markdownEntities: MessageEntity[] = [];
|
||||
const value = RichTextProcessor.parseMarkdown(richValue, markdownEntities);
|
||||
const entities = RichTextProcessor.mergeEntities(markdownEntities, RichTextProcessor.parseEntities(value));
|
||||
|
||||
@ -815,6 +819,10 @@ export default class ChatInput {
|
||||
this.stickersHelper.checkEmoticon(emoticon);
|
||||
}
|
||||
|
||||
if(!richValue.trim()) {
|
||||
this.appImManager.markupTooltip.hide();
|
||||
}
|
||||
|
||||
const html = this.messageInput.innerHTML;
|
||||
if(this.canRedoFromHTML && html != this.canRedoFromHTML && !this.lockRedo) {
|
||||
this.canRedoFromHTML = '';
|
||||
@ -1095,19 +1103,18 @@ export default class ChatInput {
|
||||
return;
|
||||
}
|
||||
|
||||
//let str = this.serializeNodes(Array.from(this.messageInput.childNodes));
|
||||
let str = this.messageInputField.value;
|
||||
|
||||
//console.log('childnode str after:', str/* , getRichValue(this.messageInput) */);
|
||||
const entities: MessageEntity[] = [];
|
||||
const str = getRichValue(this.messageInputField.input, entities);
|
||||
|
||||
//return;
|
||||
|
||||
if(this.editMsgId) {
|
||||
this.appMessagesManager.editMessage(this.chat.getMessage(this.editMsgId), str, {
|
||||
entities,
|
||||
noWebPage: this.noWebPage
|
||||
});
|
||||
} else {
|
||||
this.appMessagesManager.sendText(this.chat.peerId, str, {
|
||||
entities,
|
||||
replyToMsgId: this.replyToMsgId,
|
||||
threadId: this.chat.threadId,
|
||||
noWebPage: this.noWebPage,
|
||||
|
@ -5,6 +5,7 @@ import ButtonIcon from "../buttonIcon";
|
||||
import { clamp } from "../../helpers/number";
|
||||
import { isTouchSupported } from "../../helpers/touchSupport";
|
||||
import { isApple } from "../../helpers/userAgent";
|
||||
//import { logger } from "../../lib/logger";
|
||||
|
||||
export default class MarkupTooltip {
|
||||
public container: HTMLElement;
|
||||
@ -17,10 +18,11 @@ export default class MarkupTooltip {
|
||||
private waitingForMouseUp = false;
|
||||
private linkInput: HTMLInputElement;
|
||||
private savedRange: Range;
|
||||
mouseUpCounter: number = 0;
|
||||
private mouseUpCounter: number = 0;
|
||||
//private log: ReturnType<typeof logger>;
|
||||
|
||||
constructor(private appImManager: AppImManager) {
|
||||
|
||||
//this.log = logger('MARKUP');
|
||||
}
|
||||
|
||||
private init() {
|
||||
@ -41,14 +43,20 @@ export default class MarkupTooltip {
|
||||
tools1.append(this.buttons[c] = button);
|
||||
|
||||
if(c !== 'link') {
|
||||
button.addEventListener('click', () => {
|
||||
button.addEventListener('mousedown', (e) => {
|
||||
cancelEvent(e);
|
||||
this.appImManager.chat.input.applyMarkdown(c);
|
||||
this.hide();
|
||||
this.cancelClosening();
|
||||
|
||||
/* this.mouseUpCounter = 0;
|
||||
this.setMouseUpEvent(); */
|
||||
//this.hide();
|
||||
});
|
||||
} else {
|
||||
attachClickEvent(button, (e) => {
|
||||
cancelEvent(e);
|
||||
this.showLinkEditor();
|
||||
this.cancelClosening();
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -81,17 +89,19 @@ export default class MarkupTooltip {
|
||||
this.linkInput.classList.remove('error');
|
||||
});
|
||||
|
||||
attachClickEvent(this.linkBackButton, (e) => {
|
||||
this.linkBackButton.addEventListener('mousedown', (e) => {
|
||||
//this.log('linkBackButton click');
|
||||
cancelEvent(e);
|
||||
this.container.classList.remove('is-link');
|
||||
//input.value = '';
|
||||
this.resetSelection();
|
||||
this.setTooltipPosition();
|
||||
this.cancelClosening();
|
||||
});
|
||||
|
||||
this.linkApplyButton = ButtonIcon('check markup-tooltip-link-apply', {noRipple: true});
|
||||
attachClickEvent(this.linkApplyButton, (e) => {
|
||||
cancelEvent(e);
|
||||
this.linkApplyButton.addEventListener('mousedown', (e) => {
|
||||
//this.log('linkApplyButton click');
|
||||
this.applyLink(e);
|
||||
});
|
||||
|
||||
@ -145,7 +155,9 @@ export default class MarkupTooltip {
|
||||
cancelEvent(e);
|
||||
this.resetSelection();
|
||||
this.appImManager.chat.input.applyMarkdown('link', this.linkInput.value);
|
||||
this.hide();
|
||||
setTimeout(() => {
|
||||
this.hide();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
private isLinkValid() {
|
||||
@ -160,10 +172,12 @@ export default class MarkupTooltip {
|
||||
}
|
||||
|
||||
public hide() {
|
||||
//return;
|
||||
|
||||
if(this.init) return;
|
||||
|
||||
this.container.classList.remove('is-visible');
|
||||
document.removeEventListener('mouseup', this.onMouseUp);
|
||||
//document.removeEventListener('mouseup', this.onMouseUp);
|
||||
document.removeEventListener('mouseup', this.onMouseUpSingle);
|
||||
this.waitingForMouseUp = false;
|
||||
|
||||
@ -178,37 +192,31 @@ export default class MarkupTooltip {
|
||||
public getActiveMarkupButton() {
|
||||
const nodes = getSelectedNodes();
|
||||
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;
|
||||
let currentMarkup: HTMLElement;
|
||||
for(const type in markdownTags) {
|
||||
const tag = markdownTags[type as MarkdownType];
|
||||
if(node.matches(tag.match)) {
|
||||
currentMarkup = this.buttons[type as MarkdownType];
|
||||
break;
|
||||
const currentMarkups: Set<HTMLElement> = new Set();
|
||||
(parents as HTMLElement[]).forEach(node => {
|
||||
for(const type in markdownTags) {
|
||||
const tag = markdownTags[type as MarkdownType];
|
||||
const closest = node.closest(tag.match + ', [contenteditable]');
|
||||
if(closest !== this.appImManager.chat.input.messageInput) {
|
||||
currentMarkups.add(this.buttons[type as MarkdownType]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return currentMarkup;
|
||||
return [...currentMarkups];
|
||||
}
|
||||
|
||||
public setActiveMarkupButton() {
|
||||
const activeButton = this.getActiveMarkupButton();
|
||||
const activeButtons = this.getActiveMarkupButton();
|
||||
|
||||
for(const i in this.buttons) {
|
||||
// @ts-ignore
|
||||
const button = this.buttons[i];
|
||||
if(button != activeButton) {
|
||||
button.classList.remove('active');
|
||||
}
|
||||
button.classList.toggle('active', activeButtons.includes(button));
|
||||
}
|
||||
|
||||
if(activeButton) {
|
||||
activeButton.classList.add('active');
|
||||
}
|
||||
|
||||
return activeButton;
|
||||
}
|
||||
|
||||
private setTooltipPosition(isLinkToggle = false) {
|
||||
@ -253,7 +261,6 @@ export default class MarkupTooltip {
|
||||
}
|
||||
|
||||
const selection = document.getSelection();
|
||||
|
||||
if(!selection.toString().trim().length) {
|
||||
this.hide();
|
||||
return;
|
||||
@ -285,21 +292,19 @@ export default class MarkupTooltip {
|
||||
|
||||
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(isTouchSupported) {
|
||||
this.appImManager.chat.input.messageInput.focus();
|
||||
cancelEvent(e);
|
||||
} */
|
||||
|
||||
this.hide();
|
||||
document.removeEventListener('mouseup', this.onMouseUp);
|
||||
};
|
||||
//document.removeEventListener('mouseup', this.onMouseUp);
|
||||
}; */
|
||||
|
||||
private onMouseUpSingle = (e: Event) => {
|
||||
//this.log('onMouseUpSingle');
|
||||
this.waitingForMouseUp = false;
|
||||
|
||||
if(isTouchSupported) {
|
||||
@ -314,39 +319,51 @@ export default class MarkupTooltip {
|
||||
|
||||
this.show();
|
||||
|
||||
!isTouchSupported && document.addEventListener('mouseup', this.onMouseUp);
|
||||
//!isTouchSupported && document.addEventListener('mouseup', this.onMouseUp);
|
||||
};
|
||||
|
||||
public setMouseUpEvent() {
|
||||
if(this.waitingForMouseUp) return;
|
||||
this.waitingForMouseUp = true;
|
||||
|
||||
console.log('[MARKUP]: setMouseUpEvent');
|
||||
//this.log('setMouseUpEvent');
|
||||
|
||||
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() {
|
||||
if(this.addedListener) return;
|
||||
this.addedListener = true;
|
||||
document.addEventListener('selectionchange', (e) => {
|
||||
if(document.activeElement == this.linkInput) {
|
||||
//this.log('selectionchange');
|
||||
|
||||
if(document.activeElement === this.linkInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(document.activeElement != this.appImManager.chat.input.messageInput) {
|
||||
if(document.activeElement !== this.appImManager.chat.input.messageInput) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
const selection = document.getSelection();
|
||||
|
||||
if(!selection.toString().trim().length) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[MARKUP]: selectionchange');
|
||||
if(isTouchSupported) {
|
||||
if(isApple) {
|
||||
this.show();
|
||||
|
@ -8,6 +8,7 @@ import RadioField from "../radioField";
|
||||
import Scrollable from "../scrollable";
|
||||
import { toast } from "../toast";
|
||||
import SendContextMenu from "../chat/sendContextMenu";
|
||||
import { MessageEntity } from "../../layer";
|
||||
|
||||
const MAX_LENGTH_QUESTION = 255;
|
||||
const MAX_LENGTH_OPTION = 100;
|
||||
@ -187,7 +188,8 @@ export default class PopupCreatePoll extends PopupElement {
|
||||
return;
|
||||
}
|
||||
|
||||
const quizSolution = this.quizSolutionField.value || undefined;
|
||||
const quizSolutionEntities: MessageEntity[] = [];
|
||||
const quizSolution = getRichValue(this.quizSolutionField.input, quizSolutionEntities) || undefined;
|
||||
if(quizSolution?.length > MAX_LENGTH_SOLUTION) {
|
||||
toast('Explanation is too long.');
|
||||
return;
|
||||
@ -236,7 +238,7 @@ export default class PopupCreatePoll extends PopupElement {
|
||||
};
|
||||
//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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { MessageEntity } from "../layer";
|
||||
import { MOUNT_CLASS_TO } from "../lib/mtproto/mtproto_config";
|
||||
import RichTextProcessor from "../lib/richtextprocessor";
|
||||
import ListenerSetter from "./listenerSetter";
|
||||
import { isTouchSupported } from "./touchSupport";
|
||||
import { isSafari } from "./userAgent";
|
||||
@ -101,7 +103,7 @@ export function placeCaretAtEnd(el: HTMLElement) {
|
||||
return len;
|
||||
} */
|
||||
|
||||
export function getRichValue(field: HTMLElement) {
|
||||
export function getRichValue(field: HTMLElement, entities?: MessageEntity[]) {
|
||||
if(!field) {
|
||||
return '';
|
||||
}
|
||||
@ -109,7 +111,7 @@ export function getRichValue(field: HTMLElement) {
|
||||
const lines: string[] = [];
|
||||
const line: string[] = [];
|
||||
|
||||
getRichElementValue(field, lines, line);
|
||||
getRichElementValue(field, lines, line, undefined, undefined, entities);
|
||||
if(line.length) {
|
||||
lines.push(line.join(''));
|
||||
}
|
||||
@ -117,6 +119,12 @@ export function getRichValue(field: HTMLElement) {
|
||||
let value = lines.join('\n');
|
||||
value = value.replace(/\u00A0/g, ' ');
|
||||
|
||||
if(entities) {
|
||||
RichTextProcessor.combineSameEntities(entities);
|
||||
}
|
||||
|
||||
console.log('getRichValue:', value, entities);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@ -134,64 +142,78 @@ const markdownTypes = {
|
||||
export type MarkdownType = 'bold' | 'italic' | 'underline' | 'strikethrough' | 'monospace' | 'link';
|
||||
export type MarkdownTag = {
|
||||
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} = {
|
||||
bold: {
|
||||
match: '[style*="font-weight"]',
|
||||
markdown: markdownTypes.bold
|
||||
match: '[style*="font-weight"], b',
|
||||
markdown: markdownTypes.bold,
|
||||
entityName: 'messageEntityBold'
|
||||
},
|
||||
underline: {
|
||||
match: isSafari ? '[style="text-decoration: underline;"]' : '[style="text-decoration-line: underline;"]',
|
||||
markdown: markdownTypes.underline
|
||||
match: '[style*="underline"], u',
|
||||
markdown: markdownTypes.underline,
|
||||
entityName: 'messageEntityUnderline'
|
||||
},
|
||||
italic: {
|
||||
match: '[style="font-style: italic;"]',
|
||||
markdown: markdownTypes.italic
|
||||
match: '[style*="italic"], i',
|
||||
markdown: markdownTypes.italic,
|
||||
entityName: 'messageEntityItalic'
|
||||
},
|
||||
monospace: {
|
||||
match: '[style="font-family: monospace;"]',
|
||||
markdown: markdownTypes.monospace
|
||||
match: '[style*="monospace"], [face="monospace"]',
|
||||
markdown: markdownTypes.monospace,
|
||||
entityName: 'messageEntityPre'
|
||||
},
|
||||
strikethrough: {
|
||||
match: isSafari ? '[style="text-decoration: line-through;"]' : '[style="text-decoration-line: line-through;"]',
|
||||
markdown: markdownTypes.strikethrough
|
||||
match: '[style*="line-through"], strike',
|
||||
markdown: markdownTypes.strikethrough,
|
||||
entityName: 'messageEntityStrike'
|
||||
},
|
||||
link: {
|
||||
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(selNode === node) {
|
||||
const value = node.nodeValue;
|
||||
line.push(value.substr(0, selOffset) + '\x01' + value.substr(selOffset));
|
||||
} else {
|
||||
let markdown: string;
|
||||
if(node.parentNode) {
|
||||
const parentElement = node.parentElement;
|
||||
|
||||
let markdownTag: MarkdownTag;
|
||||
for(const type in markdownTags) {
|
||||
const tag = markdownTags[type as MarkdownType];
|
||||
if(parentElement.matches(tag.match)) {
|
||||
markdownTag = tag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const nodeValue = node.nodeValue;
|
||||
line.push(nodeValue);
|
||||
|
||||
if(markdownTag) {
|
||||
if(typeof(markdownTag.markdown) === 'function') {
|
||||
line.push(markdownTag.markdown(node));
|
||||
return;
|
||||
if(entities && nodeValue.trim()) {
|
||||
if(node.parentNode) {
|
||||
const parentElement = node.parentElement;
|
||||
|
||||
for(const type in markdownTags) {
|
||||
const tag = markdownTags[type as MarkdownType];
|
||||
const closest = parentElement.closest(tag.match + ', [contenteditable]');
|
||||
if(closest && closest.getAttribute('contenteditable') === null) {
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
markdown = markdownTag.markdown;
|
||||
}
|
||||
}
|
||||
|
||||
line.push(markdown && node.nodeValue.trim() ? '\x01' + markdown + node.nodeValue + markdown + '\x01' : node.nodeValue);
|
||||
offset.offset += nodeValue.length;
|
||||
}
|
||||
|
||||
return;
|
||||
@ -207,8 +229,10 @@ export function getRichElementValue(node: HTMLElement, lines: string[], line: st
|
||||
lines.push(line.join(''));
|
||||
line.splice(0, line.length);
|
||||
} else if(node.tagName == 'IMG') {
|
||||
if((node as HTMLImageElement).alt) {
|
||||
line.push((node as HTMLImageElement).alt);
|
||||
const alt = (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;
|
||||
while(curChild) {
|
||||
getRichElementValue(curChild, lines, line, selNode, selOffset);
|
||||
getRichElementValue(curChild, lines, line, selNode, selOffset, entities, offset);
|
||||
curChild = curChild.nextSibling as any;
|
||||
}
|
||||
|
||||
|
@ -457,6 +457,13 @@ export class AppImManager {
|
||||
|
||||
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);
|
||||
|
||||
if(justReturn) {
|
||||
|
@ -397,11 +397,8 @@ export class AppMessagesManager {
|
||||
});
|
||||
}
|
||||
|
||||
let entities = options.entities;
|
||||
if(typeof(text) === 'string' && !entities) {
|
||||
entities = [];
|
||||
text = RichTextProcessor.parseMarkdown(text, entities);
|
||||
}
|
||||
let entities = options.entities || [];
|
||||
text = RichTextProcessor.parseMarkdown(text, entities);
|
||||
|
||||
const schedule_date = options.scheduleDate || (message.pFlags.is_scheduled ? message.date : undefined);
|
||||
return apiManager.invokeApi('messages.editMessage', {
|
||||
@ -494,7 +491,7 @@ export class AppMessagesManager {
|
||||
reply_to: this.generateReplyHeader(options.replyToMsgId, options.threadId),
|
||||
via_bot_id: options.viaBotId,
|
||||
reply_markup: options.reply_markup,
|
||||
entities: entities,
|
||||
entities,
|
||||
views: isBroadcast && 1,
|
||||
pending: true
|
||||
};
|
||||
@ -641,8 +638,8 @@ export class AppMessagesManager {
|
||||
|
||||
this.log('sendFile', file, fileType);
|
||||
|
||||
const entities = options.entities || [];
|
||||
if(caption) {
|
||||
let entities = options.entities || [];
|
||||
caption = RichTextProcessor.parseMarkdown(caption, entities);
|
||||
}
|
||||
|
||||
@ -792,6 +789,7 @@ export class AppMessagesManager {
|
||||
id: messageId,
|
||||
from_id: this.generateFromId(peerId),
|
||||
peer_id: appPeersManager.getOutputPeer(peerId),
|
||||
entities,
|
||||
pFlags,
|
||||
date,
|
||||
message: caption,
|
||||
@ -913,7 +911,8 @@ export class AppMessagesManager {
|
||||
random_id: randomIdS,
|
||||
reply_to_msg_id: replyToMsgId,
|
||||
schedule_date: options.scheduleDate,
|
||||
silent: options.silent
|
||||
silent: options.silent,
|
||||
entities
|
||||
}).then((updates) => {
|
||||
apiUpdatesManager.processUpdateMessage(updates);
|
||||
}, (error) => {
|
||||
@ -960,9 +959,8 @@ export class AppMessagesManager {
|
||||
const replyToMsgId = options.replyToMsgId ? this.getLocalMessageId(options.replyToMsgId) : undefined;
|
||||
|
||||
let caption = options.caption || '';
|
||||
let entities: MessageEntity[];
|
||||
let entities = options.entities || [];
|
||||
if(caption) {
|
||||
entities = options.entities || [];
|
||||
caption = RichTextProcessor.parseMarkdown(caption, entities);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { copy } from "../../helpers/object";
|
||||
import { InputMedia } from "../../layer";
|
||||
import { InputMedia, MessageEntity } from "../../layer";
|
||||
import { logger, LogLevels } from "../logger";
|
||||
import apiManager from "../mtproto/mtprotoworker";
|
||||
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 {
|
||||
let solution_entities: any[];
|
||||
public getInputMediaPoll(poll: Poll, correctAnswers?: Uint8Array[], solution?: string, solutionEntities?: MessageEntity[]): InputMedia.inputMediaPoll {
|
||||
if(solution) {
|
||||
solution_entities = [];
|
||||
solution = RichTextProcessor.parseMarkdown(solution, solution_entities);
|
||||
if(!solutionEntities) {
|
||||
solutionEntities = [];
|
||||
}
|
||||
|
||||
solution = RichTextProcessor.parseMarkdown(solution, solutionEntities);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -155,7 +157,7 @@ export class AppPollsManager {
|
||||
poll,
|
||||
correct_answers: correctAnswers,
|
||||
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)) {
|
||||
return noTrim ? text : text.trim();
|
||||
} */
|
||||
|
||||
|
||||
const entities: MessageEntity[] = [];
|
||||
let raw = text;
|
||||
let match;
|
||||
let newText: any = [];
|
||||
@ -302,68 +303,12 @@ namespace RichTextProcessor {
|
||||
newText = newText.trim();
|
||||
}
|
||||
|
||||
mergeEntities(currentEntities, entities);
|
||||
combineSameEntities(currentEntities);
|
||||
|
||||
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[]) {
|
||||
currentEntities = currentEntities.slice();
|
||||
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;
|
||||
}
|
||||
|
||||
/* export function wrapRichNestedText(text: string, nested: MessageEntity[], options: any) {
|
||||
if(nested === undefined) {
|
||||
return encodeEntities(text);
|
||||
export function combineSameEntities(entities: MessageEntity[]) {
|
||||
//entities = entities.slice();
|
||||
for(let i = 0; i < entities.length; ++i) {
|
||||
const entity = entities[i];
|
||||
|
||||
let nextEntityIdx = -1;
|
||||
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);
|
||||
}
|
||||
|
||||
options.hasNested = true;
|
||||
return wrapRichText(text, {entities: nested, nested: true});
|
||||
} */
|
||||
//return entities;
|
||||
}
|
||||
|
||||
export function wrapRichText(text: string, options: Partial<{
|
||||
entities: MessageEntity[],
|
||||
@ -620,365 +574,6 @@ namespace RichTextProcessor {
|
||||
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<{
|
||||
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) {
|
||||
var urlLength = url.length;
|
||||
var urlOpenBrackets = url.split('(').length - 1;
|
||||
@ -1150,29 +720,6 @@ namespace RichTextProcessor {
|
||||
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) {
|
||||
const splitted = str.trim().split(' ');
|
||||
if(!splitted[0]) return '';
|
||||
|
@ -1,5 +1,5 @@
|
||||
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';
|
||||
|
||||
class AppStorage {
|
||||
@ -72,12 +72,14 @@ class AppStorage {
|
||||
key = prefix + key;
|
||||
this.cache[key] = value;
|
||||
|
||||
let perf = performance.now();
|
||||
let perf = /* DEBUG */false ? performance.now() : 0;
|
||||
value = JSON.stringify(value);
|
||||
|
||||
let elapsedTime = performance.now() - perf;
|
||||
if(elapsedTime > 10) {
|
||||
console.warn('LocalStorage set: stringify time by JSON.stringify:', elapsedTime, key);
|
||||
if(perf) {
|
||||
let elapsedTime = performance.now() - perf;
|
||||
if(elapsedTime > 10) {
|
||||
console.warn('LocalStorage set: stringify time by JSON.stringify:', elapsedTime, key);
|
||||
}
|
||||
}
|
||||
/* perf = performance.now();
|
||||
value = stringify(value);
|
||||
|
@ -9,11 +9,16 @@
|
||||
opacity: 0;
|
||||
transition: opacity var(--layer-transition), transform var(--layer-transition), width var(--layer-transition);
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 44px;
|
||||
width: $widthRegular;
|
||||
overflow: hidden;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
||||
&-wrapper {
|
||||
position: absolute;
|
||||
@ -27,6 +32,7 @@
|
||||
height: 100%;
|
||||
transform: translateX(0);
|
||||
transition: transform var(--layer-transition);
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
&-tools {
|
||||
@ -34,6 +40,8 @@
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: $padding;
|
||||
flex: 0 0 auto;
|
||||
max-width: 100%;
|
||||
|
||||
&:first-child {
|
||||
width: $widthRegular;
|
||||
|
@ -142,6 +142,14 @@
|
||||
width: 312px;
|
||||
padding: 4px 14px 14px 14px;
|
||||
}
|
||||
|
||||
&[data-lines="5"] {
|
||||
top: -16px;
|
||||
}
|
||||
|
||||
&[data-lines="7"] {
|
||||
top: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.date-picker {
|
||||
|
Loading…
x
Reference in New Issue
Block a user