FIX: bypass fast edit when selected text isn't editable
When selected some text inside a post, we offer the ability to "fast edit" the selected text without opening the composer. However, there are certain cases where this isn't working quite a expected, due to the fact that we have some text in the "cooked" version of the post that isn't literally in the "raw" version of the post. This ensures that whenever someone selects the within - a quote - a onebox - an encrypted message - a "cooked" date we directly show the composer instead of showing the fast edit modal and then leaving the user with an invisible error. Internal ref. t/128400
This commit is contained in:
parent
a658465b7d
commit
bc089dc52b
|
@ -8,6 +8,7 @@ import PostTextSelectionToolbar from "discourse/components/post-text-selection-t
|
|||
import isElementInViewport from "discourse/lib/is-element-in-viewport";
|
||||
import toMarkdown from "discourse/lib/to-markdown";
|
||||
import {
|
||||
getElement,
|
||||
selectedNode,
|
||||
selectedRange,
|
||||
selectedText,
|
||||
|
@ -32,6 +33,13 @@ function getQuoteTitle(element) {
|
|||
return titleEl.textContent.trim().replace(/:$/, "");
|
||||
}
|
||||
|
||||
const CSS_TO_DISABLE_FAST_EDIT = [
|
||||
"aside.quote",
|
||||
"aside.onebox",
|
||||
".cooked-date",
|
||||
"body.encrypted-topic-page",
|
||||
].join(",");
|
||||
|
||||
export default class PostTextSelection extends Component {
|
||||
@service appEvents;
|
||||
@service capabilities;
|
||||
|
@ -122,14 +130,8 @@ export default class PostTextSelection extends Component {
|
|||
let postId;
|
||||
for (let r = 0; r < selection.rangeCount; r++) {
|
||||
const range = selection.getRangeAt(r);
|
||||
const selectionStart =
|
||||
range.startContainer.nodeType === Node.ELEMENT_NODE
|
||||
? range.startContainer
|
||||
: range.startContainer.parentElement;
|
||||
const ancestor =
|
||||
range.commonAncestorContainer.nodeType === Node.ELEMENT_NODE
|
||||
? range.commonAncestorContainer
|
||||
: range.commonAncestorContainer.parentElement;
|
||||
const selectionStart = getElement(range.startContainer);
|
||||
const ancestor = getElement(range.commonAncestorContainer);
|
||||
|
||||
if (!selectionStart.closest(".cooked")) {
|
||||
return await this.hideToolbar();
|
||||
|
@ -142,10 +144,7 @@ export default class PostTextSelection extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
const _selectedElement =
|
||||
selectedNode().nodeType === Node.ELEMENT_NODE
|
||||
? selectedNode()
|
||||
: selectedNode().parentElement;
|
||||
const _selectedElement = getElement(selectedNode());
|
||||
const cooked =
|
||||
_selectedElement.querySelector(".cooked") ||
|
||||
_selectedElement.closest(".cooked");
|
||||
|
@ -176,7 +175,14 @@ export default class PostTextSelection extends Component {
|
|||
quoteState.selected(postId, _selectedText, opts);
|
||||
|
||||
let supportsFastEdit = this.canEditPost;
|
||||
if (this.canEditPost) {
|
||||
|
||||
const start = getElement(selection.getRangeAt(0).startContainer);
|
||||
|
||||
if (!start || start.closest(CSS_TO_DISABLE_FAST_EDIT)) {
|
||||
supportsFastEdit = false;
|
||||
}
|
||||
|
||||
if (supportsFastEdit) {
|
||||
const regexp = new RegExp(escapeRegExp(quoteState.buffer), "gi");
|
||||
const matches = cooked.innerHTML.match(regexp);
|
||||
|
||||
|
@ -184,11 +190,9 @@ export default class PostTextSelection extends Component {
|
|||
quoteState.buffer.length === 0 ||
|
||||
quoteState.buffer.includes("|") || // tables are too complex
|
||||
quoteState.buffer.match(/\n/g) || // linebreaks are too complex
|
||||
matches?.length > 1 // duplicates are too complex
|
||||
matches?.length !== 1 // duplicates are too complex
|
||||
) {
|
||||
supportsFastEdit = false;
|
||||
} else if (matches?.length === 1) {
|
||||
supportsFastEdit = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -749,3 +749,7 @@ export function cleanNullQueryParams(params) {
|
|||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
export function getElement(node) {
|
||||
return node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,13 @@ describe "Post selection | Fast edit", type: :system do
|
|||
fab!(:spanish_post) { Fabricate(:post, topic: topic, raw: "Hola Juan, ¿cómo estás?") }
|
||||
fab!(:chinese_post) { Fabricate(:post, topic: topic, raw: "这是一个测试") }
|
||||
fab!(:post_with_emoji) { Fabricate(:post, topic: topic, raw: "Good morning :wave:!") }
|
||||
fab!(:post_with_quote) do
|
||||
Fabricate(
|
||||
:post,
|
||||
topic: topic,
|
||||
raw: "[quote]\n#{post_2.raw}\n[/quote]\n\nBelle journée, n'est-ce pas ?",
|
||||
)
|
||||
end
|
||||
fab!(:current_user) { Fabricate(:admin) }
|
||||
|
||||
before { sign_in(current_user) }
|
||||
|
@ -40,6 +47,17 @@ describe "Post selection | Fast edit", type: :system do
|
|||
end
|
||||
end
|
||||
|
||||
context "when text selected is inside a quote" do
|
||||
it "opens the composer directly" do
|
||||
topic_page.visit_topic(topic)
|
||||
|
||||
select_text_range("#{topic_page.post_by_number_selector(6)} .cooked p", 5, 10)
|
||||
topic_page.click_fast_edit_button
|
||||
|
||||
expect(topic_page).to have_expanded_composer
|
||||
end
|
||||
end
|
||||
|
||||
context "when editing text that has strange characters" do
|
||||
it "saves when paragraph contains apostrophe" do
|
||||
topic_page.visit_topic(topic)
|
||||
|
|
Loading…
Reference in New Issue