From 52249fad112a65d62be1cfbfca747f3ca38d414f Mon Sep 17 00:00:00 2001 From: David Battersby Date: Wed, 31 Jan 2024 18:26:43 +0800 Subject: [PATCH] FIX: remove fast-edit regex and string replacement (#25496) This change removes the regex we used previously, which only allowed ASCII characters in fast-edit. Now multi-language content can be used with fast-edit. It also removes the string replacement we relied on in the past to catch various forms of punctuation marks, as this no longer appears necessary (possibly since this component was updated to use Glimmer). --- .../discourse/app/components/fast-edit.gjs | 6 +-- .../app/components/post-text-selection.gjs | 10 +--- .../tests/acceptance/fast-edit-test.js | 33 ++++++++++--- spec/system/post_selection_fast_edit_spec.rb | 47 +++++++++++++++++++ 4 files changed, 76 insertions(+), 20 deletions(-) diff --git a/app/assets/javascripts/discourse/app/components/fast-edit.gjs b/app/assets/javascripts/discourse/app/components/fast-edit.gjs index ff2fb75715db97..693c7d13ec672b 100644 --- a/app/assets/javascripts/discourse/app/components/fast-edit.gjs +++ b/app/assets/javascripts/discourse/app/components/fast-edit.gjs @@ -5,7 +5,6 @@ import { on } from "@ember/modifier"; import { action } from "@ember/object"; import DButton from "discourse/components/d-button"; import PluginOutlet from "discourse/components/plugin-outlet"; -import { fixQuotes } from "discourse/components/post-text-selection"; import { ajax } from "discourse/lib/ajax"; import { popupAjaxError } from "discourse/lib/ajax-error"; import { translateModKey } from "discourse/lib/utilities"; @@ -52,10 +51,7 @@ export default class FastEdit extends Component { try { const result = await ajax(`/posts/${this.args.post.id}`); - const newRaw = result.raw.replace( - fixQuotes(this.args.initialValue), - fixQuotes(this.value) - ); + const newRaw = result.raw.replace(this.args.initialValue, this.value); await this.args.post.save({ raw: newRaw }); } catch (error) { diff --git a/app/assets/javascripts/discourse/app/components/post-text-selection.gjs b/app/assets/javascripts/discourse/app/components/post-text-selection.gjs index f7212834d6ac3d..e9e2b221a076ef 100644 --- a/app/assets/javascripts/discourse/app/components/post-text-selection.gjs +++ b/app/assets/javascripts/discourse/app/components/post-text-selection.gjs @@ -31,12 +31,6 @@ function getQuoteTitle(element) { return titleEl.textContent.trim().replace(/:$/, ""); } -export function fixQuotes(str) { - // u+201c, u+201d = “ ” - // u+2018, u+2019 = ‘ ’ - return str.replace(/[\u201C\u201D]/g, '"').replace(/[\u2018\u2019]/g, "'"); -} - export default class PostTextSelection extends Component { @service appEvents; @service capabilities; @@ -177,14 +171,12 @@ export default class PostTextSelection extends Component { if (this.canEditPost) { const regexp = new RegExp(escapeRegExp(quoteState.buffer), "gi"); const matches = cooked.innerHTML.match(regexp); - const non_ascii_regex = /[^\x00-\x7F]/; if ( 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 - non_ascii_regex.test(quoteState.buffer) // non-ascii chars break fast-edit + matches?.length > 1 // duplicates are too complex ) { supportsFastEdit = false; } else if (matches?.length === 1) { diff --git a/app/assets/javascripts/discourse/tests/acceptance/fast-edit-test.js b/app/assets/javascripts/discourse/tests/acceptance/fast-edit-test.js index 2706de4534d6eb..cd8b91c2b393b3 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/fast-edit-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/fast-edit-test.js @@ -86,18 +86,39 @@ acceptance("Fast Edit", function (needs) { assert.dom(".d-editor-input").exists(); }); - test("Opens full composer when editing non-ascii characters", async function (assert) { + test("Works with diacritics", async function (assert) { await visit("/t/internationalization-localization/280"); - query("#post_2 .cooked").append( - `Je suis désolé, ”comment ça va”? A bientôt!` - ); + query("#post_2 .cooked").append(`Je suis désolé, comment ça va?`); const textNode = query("#post_2 .cooked").childNodes[2]; await selectText(textNode); await click(".quote-button .quote-edit-label"); - assert.dom("#fast-edit-input").doesNotExist(); - assert.dom(".d-editor-input").exists(); + assert.dom("#fast-edit-input").exists(); + }); + + test("Works with CJK ranges", async function (assert) { + await visit("/t/internationalization-localization/280"); + + query("#post_2 .cooked").append(`这是一个测试`); + const textNode = query("#post_2 .cooked").childNodes[2]; + + await selectText(textNode); + await click(".quote-button .quote-edit-label"); + + assert.dom("#fast-edit-input").exists(); + }); + + test("Works with emoji", async function (assert) { + await visit("/t/internationalization-localization/280"); + + query("#post_2 .cooked").append(`This is great 👍`); + const textNode = query("#post_2 .cooked").childNodes[2]; + + await selectText(textNode); + await click(".quote-button .quote-edit-label"); + + assert.dom("#fast-edit-input").exists(); }); }); diff --git a/spec/system/post_selection_fast_edit_spec.rb b/spec/system/post_selection_fast_edit_spec.rb index c4ba481372f1bb..5a89126ca6d71c 100644 --- a/spec/system/post_selection_fast_edit_spec.rb +++ b/spec/system/post_selection_fast_edit_spec.rb @@ -6,6 +6,9 @@ fab!(:topic) fab!(:post) { Fabricate(:post, topic: topic) } fab!(:post_2) { Fabricate(:post, topic: topic, raw: "It ‘twas a great’ “time”!") } + 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!(:current_user) { Fabricate(:admin) } before { sign_in(current_user) } @@ -52,5 +55,49 @@ text: "It ‘twas a great’ “day”!", ) end + + it "saves when text contains diacratics" do + topic_page.visit_topic(topic) + + select_text_range("#{topic_page.post_by_number_selector(3)} .cooked p", 11, 12) + + topic_page.click_fast_edit_button + + fast_editor.fill_content("¿está todo bien?") + fast_editor.save + + expect(page).to have_selector( + "#{topic_page.post_by_number_selector(3)} .cooked p", + text: "Hola Juan, ¿está todo bien?", + ) + end + + it "saves when text contains CJK ranges" do + topic_page.visit_topic(topic) + + select_text_range("#{topic_page.post_by_number_selector(4)} .cooked p", 0, 2) + topic_page.click_fast_edit_button + + fast_editor.fill_content("今天") + fast_editor.save + + expect(page).to have_selector( + "#{topic_page.post_by_number_selector(4)} .cooked p", + text: "今天一个测试", + ) + end + + it "saves when text contains emoji" do + topic_page.visit_topic(topic) + + select_text_range("#{topic_page.post_by_number_selector(5)} .cooked p", 5, 7) + topic_page.click_fast_edit_button + + fast_editor.fill_content("day") + fast_editor.save + + expect(page).to have_no_css("#fast-edit-input") + expect(post_with_emoji.raw).to eq("Good day :wave:!") + end end end