Fix cut/copy and paste from composer
This commit is contained in:
parent
b705609341
commit
9c325ea724
4 changed files with 67 additions and 38 deletions
|
@ -1,8 +1,18 @@
|
||||||
diff --git a/node_modules/quill/dist/quill.js b/node_modules/quill/dist/quill.js
|
diff --git a/node_modules/quill/dist/quill.js b/node_modules/quill/dist/quill.js
|
||||||
index 811b3d0..b31c7fd 100644
|
index 811b3d0..1082f2a 100644
|
||||||
--- a/node_modules/quill/dist/quill.js
|
--- a/node_modules/quill/dist/quill.js
|
||||||
+++ b/node_modules/quill/dist/quill.js
|
+++ b/node_modules/quill/dist/quill.js
|
||||||
@@ -8916,10 +8916,10 @@ var Clipboard = function (_Module) {
|
@@ -8896,7 +8896,8 @@ var debug = (0, _logger2.default)('quill:clipboard');
|
||||||
|
|
||||||
|
var DOM_KEY = '__ql-matcher';
|
||||||
|
|
||||||
|
-var CLIPBOARD_CONFIG = [[Node.TEXT_NODE, matchText], [Node.TEXT_NODE, matchNewline], ['br', matchBreak], [Node.ELEMENT_NODE, matchNewline], [Node.ELEMENT_NODE, matchBlot], [Node.ELEMENT_NODE, matchSpacing], [Node.ELEMENT_NODE, matchAttributor], [Node.ELEMENT_NODE, matchStyles], ['li', matchIndent], ['b', matchAlias.bind(matchAlias, 'bold')], ['i', matchAlias.bind(matchAlias, 'italic')], ['style', matchIgnore]];
|
||||||
|
+// var CLIPBOARD_CONFIG = [[Node.TEXT_NODE, matchText], [Node.TEXT_NODE, matchNewline], ['br', matchBreak], [Node.ELEMENT_NODE, matchNewline], [Node.ELEMENT_NODE, matchBlot], [Node.ELEMENT_NODE, matchSpacing], [Node.ELEMENT_NODE, matchAttributor], [Node.ELEMENT_NODE, matchStyles], ['li', matchIndent], ['b', matchAlias.bind(matchAlias, 'bold')], ['i', matchAlias.bind(matchAlias, 'italic')], ['style', matchIgnore]];
|
||||||
|
+var CLIPBOARD_CONFIG = [[Node.TEXT_NODE, matchText], [Node.TEXT_NODE, matchNewline], ['br', matchBreak], [Node.ELEMENT_NODE, matchNewline], [Node.ELEMENT_NODE, matchSpacing]];
|
||||||
|
|
||||||
|
var ATTRIBUTE_ATTRIBUTORS = [_align.AlignAttribute, _direction.DirectionAttribute].reduce(function (memo, attr) {
|
||||||
|
memo[attr.keyName] = attr;
|
||||||
|
@@ -8916,10 +8917,10 @@ var Clipboard = function (_Module) {
|
||||||
|
|
||||||
var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this, quill, options));
|
var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this, quill, options));
|
||||||
|
|
||||||
|
@ -17,7 +27,7 @@ index 811b3d0..b31c7fd 100644
|
||||||
_this.matchers = [];
|
_this.matchers = [];
|
||||||
CLIPBOARD_CONFIG.concat(_this.options.matchers).forEach(function (_ref) {
|
CLIPBOARD_CONFIG.concat(_this.options.matchers).forEach(function (_ref) {
|
||||||
var _ref2 = _slicedToArray(_ref, 2),
|
var _ref2 = _slicedToArray(_ref, 2),
|
||||||
@@ -8941,15 +8941,18 @@ var Clipboard = function (_Module) {
|
@@ -8941,15 +8942,18 @@ var Clipboard = function (_Module) {
|
||||||
key: 'convert',
|
key: 'convert',
|
||||||
value: function convert(html) {
|
value: function convert(html) {
|
||||||
if (typeof html === 'string') {
|
if (typeof html === 'string') {
|
||||||
|
@ -42,7 +52,7 @@ index 811b3d0..b31c7fd 100644
|
||||||
|
|
||||||
var _prepareMatching = this.prepareMatching(),
|
var _prepareMatching = this.prepareMatching(),
|
||||||
_prepareMatching2 = _slicedToArray(_prepareMatching, 2),
|
_prepareMatching2 = _slicedToArray(_prepareMatching, 2),
|
||||||
@@ -8962,7 +8965,8 @@ var Clipboard = function (_Module) {
|
@@ -8962,7 +8966,8 @@ var Clipboard = function (_Module) {
|
||||||
delta = delta.compose(new _quillDelta2.default().retain(delta.length() - 1).delete(1));
|
delta = delta.compose(new _quillDelta2.default().retain(delta.length() - 1).delete(1));
|
||||||
}
|
}
|
||||||
debug.log('convert', this.container.innerHTML, delta);
|
debug.log('convert', this.container.innerHTML, delta);
|
||||||
|
@ -52,7 +62,7 @@ index 811b3d0..b31c7fd 100644
|
||||||
return delta;
|
return delta;
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
@@ -9056,9 +9060,10 @@ function applyFormat(delta, format, value) {
|
@@ -9056,9 +9061,10 @@ function applyFormat(delta, format, value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function computeStyle(node) {
|
function computeStyle(node) {
|
||||||
|
@ -66,7 +76,7 @@ index 811b3d0..b31c7fd 100644
|
||||||
}
|
}
|
||||||
|
|
||||||
function deltaEndsWith(delta, text) {
|
function deltaEndsWith(delta, text) {
|
||||||
@@ -9074,7 +9079,8 @@ function deltaEndsWith(delta, text) {
|
@@ -9074,7 +9080,8 @@ function deltaEndsWith(delta, text) {
|
||||||
function isLine(node) {
|
function isLine(node) {
|
||||||
if (node.childNodes.length === 0) return false; // Exclude embed blocks
|
if (node.childNodes.length === 0) return false; // Exclude embed blocks
|
||||||
var style = computeStyle(node);
|
var style = computeStyle(node);
|
||||||
|
@ -76,7 +86,7 @@ index 811b3d0..b31c7fd 100644
|
||||||
}
|
}
|
||||||
|
|
||||||
function traverse(node, elementMatchers, textMatchers) {
|
function traverse(node, elementMatchers, textMatchers) {
|
||||||
@@ -9177,8 +9183,10 @@ function matchIndent(node, delta) {
|
@@ -9177,8 +9184,10 @@ function matchIndent(node, delta) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function matchNewline(node, delta) {
|
function matchNewline(node, delta) {
|
||||||
|
|
|
@ -187,7 +187,7 @@ import { setBatchingStrategy } from './util/messageBatcher';
|
||||||
import { parseRemoteClientExpiration } from './util/parseRemoteClientExpiration';
|
import { parseRemoteClientExpiration } from './util/parseRemoteClientExpiration';
|
||||||
import { makeLookup } from './util/makeLookup';
|
import { makeLookup } from './util/makeLookup';
|
||||||
import { addGlobalKeyboardShortcuts } from './services/addGlobalKeyboardShortcuts';
|
import { addGlobalKeyboardShortcuts } from './services/addGlobalKeyboardShortcuts';
|
||||||
import { handleCopyEvent } from './quill/signal-clipboard/util';
|
import { createEventHandler } from './quill/signal-clipboard/util';
|
||||||
|
|
||||||
export function isOverHourIntoPast(timestamp: number): boolean {
|
export function isOverHourIntoPast(timestamp: number): boolean {
|
||||||
return isNumber(timestamp) && isOlderThan(timestamp, HOUR);
|
return isNumber(timestamp) && isOlderThan(timestamp, HOUR);
|
||||||
|
@ -551,7 +551,14 @@ export async function startApp(): Promise<void> {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Intercept clipboard copies to add our custom text/signal data
|
// Intercept clipboard copies to add our custom text/signal data
|
||||||
document.addEventListener('copy', handleCopyEvent);
|
document.addEventListener(
|
||||||
|
'copy',
|
||||||
|
createEventHandler({ deleteSelection: false })
|
||||||
|
);
|
||||||
|
document.addEventListener(
|
||||||
|
'cut',
|
||||||
|
createEventHandler({ deleteSelection: true })
|
||||||
|
);
|
||||||
|
|
||||||
startInteractionMode();
|
startInteractionMode();
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,6 @@ export class SignalClipboard {
|
||||||
this.quill = quill;
|
this.quill = quill;
|
||||||
|
|
||||||
this.quill.root.addEventListener('paste', e => this.onCapturePaste(e));
|
this.quill.root.addEventListener('paste', e => this.onCapturePaste(e));
|
||||||
|
|
||||||
const clipboard = this.quill.getModule('clipboard');
|
|
||||||
|
|
||||||
// We keep just the first few matchers (for spacing) then drop the rest!
|
|
||||||
clipboard.matchers = clipboard.matchers
|
|
||||||
.slice(0, 4)
|
|
||||||
.concat(clipboard.matchers.slice(11));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onCapturePaste(event: ClipboardEvent): void {
|
onCapturePaste(event: ClipboardEvent): void {
|
||||||
|
|
|
@ -1,36 +1,51 @@
|
||||||
// Copyright 2023 Signal Messenger, LLC
|
// Copyright 2023 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
export function handleCopyEvent(event: ClipboardEvent): void {
|
const QUILL_EMBED_GUARD = '\uFEFF';
|
||||||
if (!event.clipboardData) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const selection = window.getSelection();
|
export function createEventHandler({
|
||||||
if (!selection) {
|
deleteSelection,
|
||||||
return;
|
}: {
|
||||||
}
|
deleteSelection: boolean;
|
||||||
|
}) {
|
||||||
|
return (event: ClipboardEvent): void => {
|
||||||
|
if (!event.clipboardData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Create synthetic html with the full selection we can put into clipboard
|
const selection = window.getSelection();
|
||||||
const container = document.createElement('div');
|
if (!selection) {
|
||||||
for (let i = 0, max = selection.rangeCount; i < max; i += 1) {
|
return;
|
||||||
const range = selection.getRangeAt(i);
|
}
|
||||||
container.appendChild(range.cloneContents());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: we can't leave text/plain alone and just add text/signal; if we update
|
// Create synthetic html with the full selection we can put into clipboard
|
||||||
// clipboardData at all, all other data is reset.
|
const container = document.createElement('div');
|
||||||
const plaintext = getStringFromNode(container);
|
for (let i = 0, max = selection.rangeCount; i < max; i += 1) {
|
||||||
event.clipboardData?.setData('text/plain', plaintext);
|
const range = selection.getRangeAt(i);
|
||||||
|
container.appendChild(range.cloneContents());
|
||||||
|
}
|
||||||
|
|
||||||
event.clipboardData?.setData('text/signal', container.innerHTML);
|
// Note: we can't leave text/plain alone and just add text/signal; if we update
|
||||||
|
// clipboardData at all, all other data is reset.
|
||||||
|
const plaintext = getStringFromNode(container);
|
||||||
|
event.clipboardData?.setData('text/plain', plaintext);
|
||||||
|
|
||||||
event.preventDefault();
|
event.clipboardData?.setData('text/signal', container.innerHTML);
|
||||||
event.stopPropagation();
|
|
||||||
|
if (deleteSelection) {
|
||||||
|
selection.deleteFromDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStringFromNode(node: Node): string {
|
function getStringFromNode(node: Node): string {
|
||||||
if (node.nodeType === Node.TEXT_NODE) {
|
if (node.nodeType === Node.TEXT_NODE) {
|
||||||
|
if (node.textContent === QUILL_EMBED_GUARD) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
return node.textContent || '';
|
return node.textContent || '';
|
||||||
}
|
}
|
||||||
if (node.nodeType !== Node.ELEMENT_NODE) {
|
if (node.nodeType !== Node.ELEMENT_NODE) {
|
||||||
|
@ -38,7 +53,11 @@ function getStringFromNode(node: Node): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
const element = node as Element;
|
const element = node as Element;
|
||||||
if (element.nodeName === 'IMG' && element.classList.contains('emoji')) {
|
if (
|
||||||
|
element.nodeName === 'IMG' &&
|
||||||
|
(element.classList.contains('emoji') ||
|
||||||
|
element.classList.contains('emoji-blot'))
|
||||||
|
) {
|
||||||
return element.ariaLabel || '';
|
return element.ariaLabel || '';
|
||||||
}
|
}
|
||||||
if (element.nodeName === 'BR') {
|
if (element.nodeName === 'BR') {
|
||||||
|
|
Loading…
Reference in a new issue