mirror of
https://github.com/zulip/zulip.git
synced 2025-11-10 17:07:07 +00:00
compose: Have at least 2 new lines before and after a quoted message.
Uptil now, 1 new line was added before and 1 after a quoted message. Now for more breathing room around a quoted message, new lines are inserted to space it from any content before and after by at least 2 new lines. Fixes: #23608.
This commit is contained in:
@@ -536,25 +536,17 @@ export function quote_and_reply(opts) {
|
|||||||
const message = message_lists.current.selected_message();
|
const message = message_lists.current.selected_message();
|
||||||
const quoting_placeholder = $t({defaultMessage: "[Quoting…]"});
|
const quoting_placeholder = $t({defaultMessage: "[Quoting…]"});
|
||||||
|
|
||||||
if (compose_state.has_message_content()) {
|
if (!compose_state.has_message_content()) {
|
||||||
// The user already started typing a message,
|
// The user has not started typing a message,
|
||||||
// so we won't re-open the compose box.
|
// so we will re-open the compose box.
|
||||||
// (If you did re-open the compose box, you
|
// (If you did re-open the compose box, you
|
||||||
// are prone to glitches where you select the
|
// are prone to glitches where you select the
|
||||||
// text, plus it's a complicated codepath that
|
// text, plus it's a complicated codepath that
|
||||||
// can have other unintended consequences.)
|
// can have other unintended consequences.)
|
||||||
|
|
||||||
if ($textarea.caret() !== 0) {
|
|
||||||
// Insert a newline before quoted message if there is
|
|
||||||
// already some content in the compose box and quoted
|
|
||||||
// message is not being inserted at the beginning.
|
|
||||||
$textarea.caret("\n");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
respond_to_message(opts);
|
respond_to_message(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
compose_ui.insert_syntax_and_focus(quoting_placeholder + "\n", $textarea);
|
compose_ui.insert_syntax_and_focus(quoting_placeholder, $textarea, "block");
|
||||||
|
|
||||||
function replace_content(message) {
|
function replace_content(message) {
|
||||||
// Final message looks like:
|
// Final message looks like:
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export function autosize_textarea($textarea) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function smart_insert($textarea, syntax) {
|
export function smart_insert_inline($textarea, syntax) {
|
||||||
function is_space(c) {
|
function is_space(c) {
|
||||||
return c === " " || c === "\t" || c === "\n";
|
return c === " " || c === "\t" || c === "\n";
|
||||||
}
|
}
|
||||||
@@ -68,11 +68,69 @@ export function smart_insert($textarea, syntax) {
|
|||||||
autosize_textarea($textarea);
|
autosize_textarea($textarea);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function insert_syntax_and_focus(syntax, $textarea = $("#compose-textarea")) {
|
export function smart_insert_block($textarea, syntax) {
|
||||||
|
const pos = $textarea.caret();
|
||||||
|
const before_str = $textarea.val().slice(0, pos);
|
||||||
|
const after_str = $textarea.val().slice(pos);
|
||||||
|
|
||||||
|
if (pos > 0) {
|
||||||
|
// Insert newline/s before the content block if there is
|
||||||
|
// already some content in the compose box and the content
|
||||||
|
// block is not being inserted at the beginning, such
|
||||||
|
// that there are at least 2 new lines between the content
|
||||||
|
// and start of the content block.
|
||||||
|
let new_lines_before_count = 0;
|
||||||
|
let current_pos = pos - 1;
|
||||||
|
while (
|
||||||
|
current_pos >= 0 &&
|
||||||
|
before_str.charAt(current_pos) === "\n" &&
|
||||||
|
new_lines_before_count < 2
|
||||||
|
) {
|
||||||
|
// count up to 2 new lines before cursor
|
||||||
|
current_pos -= 1;
|
||||||
|
new_lines_before_count += 1;
|
||||||
|
}
|
||||||
|
const new_lines_needed_before_count = 2 - new_lines_before_count;
|
||||||
|
syntax = "\n".repeat(new_lines_needed_before_count) + syntax;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_lines_after_count = 0;
|
||||||
|
let current_pos = 0;
|
||||||
|
while (
|
||||||
|
current_pos < after_str.length &&
|
||||||
|
after_str.charAt(current_pos) === "\n" &&
|
||||||
|
new_lines_after_count < 2
|
||||||
|
) {
|
||||||
|
// count up to 2 new lines after cursor
|
||||||
|
current_pos += 1;
|
||||||
|
new_lines_after_count += 1;
|
||||||
|
}
|
||||||
|
// Insert newline/s after the content block, such that there
|
||||||
|
// are at least 2 new lines between the content block and
|
||||||
|
// the content after the cursor, if any.
|
||||||
|
const new_lines_needed_after_count = 2 - new_lines_after_count;
|
||||||
|
syntax = syntax + "\n".repeat(new_lines_needed_after_count);
|
||||||
|
|
||||||
|
// text-field-edit ensures `$textarea` is focused before inserting
|
||||||
|
// the new syntax.
|
||||||
|
insert($textarea[0], syntax);
|
||||||
|
|
||||||
|
autosize_textarea($textarea);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function insert_syntax_and_focus(
|
||||||
|
syntax,
|
||||||
|
$textarea = $("#compose-textarea"),
|
||||||
|
mode = "inline",
|
||||||
|
) {
|
||||||
// Generic helper for inserting syntax into the main compose box
|
// Generic helper for inserting syntax into the main compose box
|
||||||
// where the cursor was and focusing the area. Mostly a thin
|
// where the cursor was and focusing the area. Mostly a thin
|
||||||
// wrapper around smart_insert.
|
// wrapper around smart_insert_inline and smart_inline_block.
|
||||||
smart_insert($textarea, syntax);
|
if (mode === "inline") {
|
||||||
|
smart_insert_inline($textarea, syntax);
|
||||||
|
} else if (mode === "block") {
|
||||||
|
smart_insert_block($textarea, syntax);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function replace_syntax(old_syntax, new_syntax, $textarea = $("#compose-textarea")) {
|
export function replace_syntax(old_syntax, new_syntax, $textarea = $("#compose-textarea")) {
|
||||||
|
|||||||
@@ -359,8 +359,9 @@ test("quote_and_reply", ({disallow, override, override_rewire}) => {
|
|||||||
|
|
||||||
override(message_lists.current, "selected_id", () => 100);
|
override(message_lists.current, "selected_id", () => 100);
|
||||||
|
|
||||||
override(compose_ui, "insert_syntax_and_focus", (syntax) => {
|
override(compose_ui, "insert_syntax_and_focus", (syntax, $textarea, mode) => {
|
||||||
assert.equal(syntax, "translated: [Quoting…]\n");
|
assert.equal(syntax, "translated: [Quoting…]");
|
||||||
|
assert.equal(mode, "block");
|
||||||
});
|
});
|
||||||
|
|
||||||
const opts = {
|
const opts = {
|
||||||
|
|||||||
@@ -120,34 +120,34 @@ run_test("smart_insert", ({override}) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
override_with_expected_syntax(" :smile: ");
|
override_with_expected_syntax(" :smile: ");
|
||||||
compose_ui.smart_insert($textbox, ":smile:");
|
compose_ui.smart_insert_inline($textbox, ":smile:");
|
||||||
|
|
||||||
override_with_expected_syntax(" :airplane: ");
|
override_with_expected_syntax(" :airplane: ");
|
||||||
compose_ui.smart_insert($textbox, ":airplane:");
|
compose_ui.smart_insert_inline($textbox, ":airplane:");
|
||||||
|
|
||||||
$textbox.caret(0);
|
$textbox.caret(0);
|
||||||
override_with_expected_syntax(":octopus: ");
|
override_with_expected_syntax(":octopus: ");
|
||||||
compose_ui.smart_insert($textbox, ":octopus:");
|
compose_ui.smart_insert_inline($textbox, ":octopus:");
|
||||||
|
|
||||||
$textbox.caret($textbox.val().length);
|
$textbox.caret($textbox.val().length);
|
||||||
override_with_expected_syntax(" :heart: ");
|
override_with_expected_syntax(" :heart: ");
|
||||||
compose_ui.smart_insert($textbox, ":heart:");
|
compose_ui.smart_insert_inline($textbox, ":heart:");
|
||||||
|
|
||||||
// Test handling of spaces for ```quote
|
// Test handling of spaces for ```quote
|
||||||
$textbox = make_textbox("");
|
$textbox = make_textbox("");
|
||||||
$textbox.caret(0);
|
$textbox.caret(0);
|
||||||
override_with_expected_syntax("```quote\nquoted message\n```\n");
|
override_with_expected_syntax("```quote\nquoted message\n```\n\n");
|
||||||
compose_ui.smart_insert($textbox, "```quote\nquoted message\n```\n");
|
compose_ui.smart_insert_block($textbox, "```quote\nquoted message\n```");
|
||||||
|
|
||||||
$textbox = make_textbox("");
|
$textbox = make_textbox("");
|
||||||
$textbox.caret(0);
|
$textbox.caret(0);
|
||||||
override_with_expected_syntax("translated: [Quoting…]\n");
|
override_with_expected_syntax("translated: [Quoting…]\n\n");
|
||||||
compose_ui.smart_insert($textbox, "translated: [Quoting…]\n");
|
compose_ui.smart_insert_block($textbox, "translated: [Quoting…]");
|
||||||
|
|
||||||
$textbox = make_textbox("abc");
|
$textbox = make_textbox("abc");
|
||||||
$textbox.caret(3);
|
$textbox.caret(3);
|
||||||
override_with_expected_syntax(" test with space ");
|
override_with_expected_syntax("\n\n test with space\n\n");
|
||||||
compose_ui.smart_insert($textbox, " test with space");
|
compose_ui.smart_insert_block($textbox, " test with space");
|
||||||
|
|
||||||
// Note that we don't have any special logic for strings that are
|
// Note that we don't have any special logic for strings that are
|
||||||
// already surrounded by spaces, since we are usually inserting things
|
// already surrounded by spaces, since we are usually inserting things
|
||||||
@@ -281,23 +281,28 @@ run_test("quote_and_reply", ({override, override_rewire}) => {
|
|||||||
textarea_caret_pos = arg;
|
textarea_caret_pos = arg;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
/* istanbul ignore if */
|
|
||||||
if (typeof arg !== "string") {
|
/* This next block of mocking code is currently unused, but
|
||||||
console.info(arg);
|
is preserved, since it may be useful in the future. */
|
||||||
throw new Error("We expected the actual code to pass in a string.");
|
/* istanbul ignore next */
|
||||||
|
{
|
||||||
|
if (typeof arg !== "string") {
|
||||||
|
console.info(arg);
|
||||||
|
throw new Error("We expected the actual code to pass in a string.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const before = textarea_val.slice(0, textarea_caret_pos);
|
||||||
|
const after = textarea_val.slice(textarea_caret_pos);
|
||||||
|
|
||||||
|
textarea_val = before + arg + after;
|
||||||
|
textarea_caret_pos += arg.length;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
const before = textarea_val.slice(0, textarea_caret_pos);
|
|
||||||
const after = textarea_val.slice(textarea_caret_pos);
|
|
||||||
|
|
||||||
textarea_val = before + arg + after;
|
|
||||||
textarea_caret_pos += arg.length;
|
|
||||||
return this;
|
|
||||||
};
|
};
|
||||||
$("#compose-textarea")[0] = "compose-textarea";
|
$("#compose-textarea")[0] = "compose-textarea";
|
||||||
override(text_field_edit, "insert", (elt, syntax) => {
|
override(text_field_edit, "insert", (elt, syntax) => {
|
||||||
assert.equal(elt, "compose-textarea");
|
assert.equal(elt, "compose-textarea");
|
||||||
assert.equal(syntax, "translated: [Quoting…]\n");
|
assert.equal(syntax, "\n\ntranslated: [Quoting…]\n\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
function set_compose_content_with_caret(content) {
|
function set_compose_content_with_caret(content) {
|
||||||
@@ -343,7 +348,11 @@ run_test("quote_and_reply", ({override, override_rewire}) => {
|
|||||||
reset_test_state();
|
reset_test_state();
|
||||||
|
|
||||||
// If the caret is initially positioned at 0, it should not
|
// If the caret is initially positioned at 0, it should not
|
||||||
// add a newline before the quoted message.
|
// add newlines before the quoted message.
|
||||||
|
override(text_field_edit, "insert", (elt, syntax) => {
|
||||||
|
assert.equal(elt, "compose-textarea");
|
||||||
|
assert.equal(syntax, "translated: [Quoting…]\n\n");
|
||||||
|
});
|
||||||
set_compose_content_with_caret("%hello there");
|
set_compose_content_with_caret("%hello there");
|
||||||
compose_actions.quote_and_reply();
|
compose_actions.quote_and_reply();
|
||||||
|
|
||||||
@@ -387,6 +396,40 @@ run_test("quote_and_reply", ({override, override_rewire}) => {
|
|||||||
success_function({
|
success_function({
|
||||||
raw_content: quote_text,
|
raw_content: quote_text,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
reset_test_state();
|
||||||
|
|
||||||
|
// When there is already 1 newline before and after the caret,
|
||||||
|
// only 1 newline is added before and after the quoted message.
|
||||||
|
override(text_field_edit, "insert", (elt, syntax) => {
|
||||||
|
assert.equal(elt, "compose-textarea");
|
||||||
|
assert.equal(syntax, "\ntranslated: [Quoting…]\n");
|
||||||
|
});
|
||||||
|
set_compose_content_with_caret("1st line\n%\n2nd line");
|
||||||
|
compose_actions.quote_and_reply();
|
||||||
|
|
||||||
|
quote_text = "Testing with caret on a new line between 2 lines of text.";
|
||||||
|
override_with_quote_text(quote_text);
|
||||||
|
success_function({
|
||||||
|
raw_content: quote_text,
|
||||||
|
});
|
||||||
|
|
||||||
|
reset_test_state();
|
||||||
|
|
||||||
|
// When there are many (>=2) newlines before and after the caret,
|
||||||
|
// no newline is added before or after the quoted message.
|
||||||
|
override(text_field_edit, "insert", (elt, syntax) => {
|
||||||
|
assert.equal(elt, "compose-textarea");
|
||||||
|
assert.equal(syntax, "translated: [Quoting…]");
|
||||||
|
});
|
||||||
|
set_compose_content_with_caret("lots of\n\n\n\n%\n\n\nnewlines");
|
||||||
|
compose_actions.quote_and_reply();
|
||||||
|
|
||||||
|
quote_text = "Testing with caret on a new line between many empty newlines.";
|
||||||
|
override_with_quote_text(quote_text);
|
||||||
|
success_function({
|
||||||
|
raw_content: quote_text,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
run_test("set_compose_box_top", () => {
|
run_test("set_compose_box_top", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user