diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index 4e924e4a..3debcd87 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -2918,7 +2918,10 @@ export default class ChatBubbles { const html = RichTextProcessor.wrapRichText(webpage.url); const a: HTMLAnchorElement = htmlToDocumentFragment(html).firstElementChild as any; a.classList.add('webpage-name'); + // const b = document.createElement('b'); setInnerHTML(a, RichTextProcessor.wrapEmojiText(webpage.site_name)); + // a.textContent = ''; + // a.append(b); quoteTextDiv.append(a); t = a; } @@ -2926,6 +2929,8 @@ export default class ChatBubbles { if(webpage.rTitle) { let titleDiv = document.createElement('div'); titleDiv.classList.add('title'); + // const b = document.createElement('b'); + // titleDiv.append(b); setInnerHTML(titleDiv, webpage.rTitle); quoteTextDiv.append(titleDiv); t = titleDiv; diff --git a/src/components/chat/input.ts b/src/components/chat/input.ts index 20f56be5..bd1230c6 100644 --- a/src/components/chat/input.ts +++ b/src/components/chat/input.ts @@ -2405,6 +2405,10 @@ export default class ChatInput { replyParent.insertBefore(newReply, replyParent.lastElementChild); } + if(type === 'webpage') { + newReply.style.cursor = 'default'; + } + if(!this.chat.container.classList.contains('is-helper-active')) { this.chat.container.classList.add('is-helper-active'); this.t(); diff --git a/src/components/inputField.ts b/src/components/inputField.ts index d1378ead..cc591aac 100644 --- a/src/components/inputField.ts +++ b/src/components/inputField.ts @@ -9,6 +9,7 @@ import findUpAttribute from "../helpers/dom/findUpAttribute"; import getRichValue from "../helpers/dom/getRichValue"; import isInputEmpty from "../helpers/dom/isInputEmpty"; import selectElementContents from "../helpers/dom/selectElementContents"; +import { MessageEntity } from "../layer"; import { i18n, LangPackKey, _i18n } from "../lib/langPack"; import RichTextProcessor from "../lib/richtextprocessor"; import SetTransition from "./singleTransition"; @@ -18,27 +19,33 @@ let init = () => { if(!findUpAttribute(e.target, 'contenteditable="true"')) { return; } - //console.log('document paste'); - - //console.log('messageInput paste'); e.preventDefault(); - // @ts-ignore - let text = (e.originalEvent || e).clipboardData.getData('text/plain'); + let text: string, entities: MessageEntity[]; - let entities = RichTextProcessor.parseEntities(text); - //console.log('messageInput paste', text, entities); - entities = entities.filter(e => e._ === 'messageEntityEmoji' || e._ === 'messageEntityLinebreak'); - //text = RichTextProcessor.wrapEmojiText(text); - text = RichTextProcessor.wrapRichText(text, {entities, noLinks: true, wrappingDraft: true}); + // @ts-ignore + const html: string = (e.originalEvent || e).clipboardData.getData('text/html'); + if(html.trim()) { + const span = document.createElement('span'); + span.innerHTML = html; + + const richValue = getRichValue(span, true); + text = richValue.value; + entities = richValue.entities; - // console.log('messageInput paste after', text); + let entities2 = RichTextProcessor.parseEntities(text); + entities2 = entities2.filter(e => e._ === 'messageEntityEmoji' || e._ === 'messageEntityLinebreak'); + RichTextProcessor.mergeEntities(entities, entities2); + } else { + // @ts-ignore + text = (e.originalEvent || e).clipboardData.getData('text/plain'); - // @ts-ignore - //let html = (e.originalEvent || e).clipboardData.getData('text/html'); + entities = RichTextProcessor.parseEntities(text); + entities = entities.filter(e => e._ === 'messageEntityEmoji' || e._ === 'messageEntityLinebreak'); + } - // @ts-ignore - //console.log('paste text', text, ); + text = RichTextProcessor.wrapDraftText(text, {entities}); + window.document.execCommand('insertHTML', false, text); }); diff --git a/src/helpers/dom/getRichElementValue.ts b/src/helpers/dom/getRichElementValue.ts index ca009709..c0ba4c76 100644 --- a/src/helpers/dom/getRichElementValue.ts +++ b/src/helpers/dom/getRichElementValue.ts @@ -18,7 +18,7 @@ export type MarkdownTag = { }; export const markdownTags: {[type in MarkdownType]: MarkdownTag} = { bold: { - match: '[style*="font-weight"], b', + match: '[style*="bold"], [style*="font-weight: 700"], [style*="font-weight: 600"], [style*="font-weight:700"], [style*="font-weight:600"], b, strong', entityName: 'messageEntityBold' }, underline: { @@ -47,9 +47,46 @@ export const markdownTags: {[type in MarkdownType]: MarkdownTag} = { } }; +const tabulationMatch = '[style*="table-cell"]'; + +/* export function getDepth(child: Node, container?: Node) { + let depth = 0; + + do { + if(child === container) { + return depth; + } + + ++depth; + } while((child = child.parentNode) !== null); + + return depth; +} */ + +const BLOCK_TAG_NAMES = new Set([ + 'DIV', + 'P', + 'BR', + 'LI', + 'SECTION', + 'H6', + 'H5', + 'H4', + 'H3', + 'H2', + 'H1', +]); + export default function getRichElementValue(node: HTMLElement, lines: string[], line: string[], selNode?: Node, selOffset?: number, entities?: MessageEntity[], offset = {offset: 0}) { if(node.nodeType === 3) { // TEXT - const nodeValue = node.nodeValue; + let nodeValue = node.nodeValue; + + const tabulation = node.parentElement?.closest(tabulationMatch + ', [contenteditable]'); + if(tabulation?.getAttribute('contenteditable') === null) { + nodeValue += ' '; + // line.push('\t'); + // ++offset.offset; + } if(selNode === node) { line.push(nodeValue.substr(0, selOffset) + '\x01' + nodeValue.substr(selOffset)); @@ -57,35 +94,44 @@ export default function getRichElementValue(node: HTMLElement, lines: string[], line.push(nodeValue); } - if(entities && nodeValue.trim()) { + if(entities && nodeValue.length) { if(node.parentNode) { const parentElement = node.parentElement; + // let closestTag: MarkdownTag, closestElementByTag: Element, closestDepth = Infinity; 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, - url: (parentElement as HTMLAnchorElement).href, - offset: offset.offset, - length: nodeValue.length - }); - } else if(tag.entityName === 'messageEntityMentionName') { - entities.push({ - _: tag.entityName, - offset: offset.offset, - length: nodeValue.length, - user_id: parentElement.dataset.follow.toUserId() - }); - } else { - entities.push({ - _: tag.entityName as any, - offset: offset.offset, - length: nodeValue.length - }); - } + if(closest?.getAttribute('contenteditable') !== null) { + /* const depth = getDepth(closest, parentElement.closest('[contenteditable]')); + if(closestDepth > depth) { + closestDepth = depth; + closestTag = tag; + closestElementByTag = closest; + } */ + continue; + } + + if(tag.entityName === 'messageEntityTextUrl') { + entities.push({ + _: tag.entityName, + url: (closest as HTMLAnchorElement).href, + offset: offset.offset, + length: nodeValue.length + }); + } else if(tag.entityName === 'messageEntityMentionName') { + entities.push({ + _: tag.entityName, + offset: offset.offset, + length: nodeValue.length, + user_id: (closest as HTMLElement).dataset.follow.toUserId() + }); + } else { + entities.push({ + _: tag.entityName as any, + offset: offset.offset, + length: nodeValue.length + }); } } } @@ -100,11 +146,12 @@ export default function getRichElementValue(node: HTMLElement, lines: string[], return; } - const isSelected = (selNode === node); - const isBlock = node.tagName === 'DIV' || node.tagName === 'P'; - if(isBlock && line.length || node.tagName === 'BR') { + const isSelected = selNode === node; + const isBlock = BLOCK_TAG_NAMES.has(node.tagName); + if(isBlock && line.length) { lines.push(line.join('')); line.splice(0, line.length); + ++offset.offset; } else if(node instanceof HTMLImageElement) { const alt = node.alt; if(alt) { @@ -130,5 +177,11 @@ export default function getRichElementValue(node: HTMLElement, lines: string[], if(isBlock && line.length) { lines.push(line.join('')); line.splice(0, line.length); + ++offset.offset; + } + + if(node.tagName === 'P') { + lines.push(''); + ++offset.offset; } } diff --git a/src/helpers/dom/getRichValue.ts b/src/helpers/dom/getRichValue.ts index ccf78768..4c3fe905 100644 --- a/src/helpers/dom/getRichValue.ts +++ b/src/helpers/dom/getRichValue.ts @@ -27,7 +27,15 @@ export default function getRichValue(field: HTMLElement, withEntities = true) { let value = lines.join('\n'); value = value.replace(/\u00A0/g, ' '); - if(entities) { + if(entities?.length) { + // ! cannot do that here because have the same check before the sending in RichTextProcessor.parseMarkdown + /* const entity = entities[entities.length - 1]; + const length = value.length; + const trimmedLength = value.trimRight().length; + if(length !== trimmedLength) { + entity.length -= length - trimmedLength; + } */ + RichTextProcessor.combineSameEntities(entities); } diff --git a/src/lib/richtextprocessor.ts b/src/lib/richtextprocessor.ts index 89012843..8f63143f 100644 --- a/src/lib/richtextprocessor.ts +++ b/src/lib/richtextprocessor.ts @@ -345,9 +345,10 @@ namespace RichTextProcessor { entities.splice(0, entities.length); } - if(!entities.length && !noTrim) { - newText = newText.trim(); - } + // ! idk what it was here for + // if(!entities.length && !noTrim) { + // newText = newText.trim(); + // } mergeEntities(currentEntities, entities); combineSameEntities(currentEntities);