message_edit: Add message length limit indicator in edit message UI.

Adds a message length limit indicator similar to the one in the compose box.
The tooltip message for the disabled save button now appears dynamically
based on whether the message exceeds the length limit or the editing time
has expired.

Fixes #25271.
This commit is contained in:
opmkumar
2024-12-04 07:53:41 +05:30
committed by Tim Abbott
parent 88727862b4
commit 5eb51e2df6
7 changed files with 108 additions and 17 deletions

View File

@@ -54,6 +54,17 @@ function set_message_too_long_for_compose(status: boolean): void {
update_send_button_status();
}
function set_message_too_long_for_edit(status: boolean, $container: JQuery): void {
message_too_long = status;
const $message_edit_save_container = $container.find(".message_edit_save_container");
const save_is_disabled =
message_too_long ||
$message_edit_save_container.hasClass("message-edit-time-limit-expired");
$container.find(".message_edit_save").prop("disabled", save_is_disabled);
$message_edit_save_container.toggleClass("disabled-message-edit-save", save_is_disabled);
}
export function set_recipient_disallowed(status: boolean): void {
recipient_disallowed = status;
update_send_button_status();
@@ -78,6 +89,25 @@ export function get_disabled_send_tooltip(): string {
return "";
}
export function get_disabled_save_tooltip($container: JQuery): string {
const $button_wrapper = $container.find(".message_edit_save_container");
if ($button_wrapper.hasClass("message-edit-time-limit-expired")) {
return $t({
defaultMessage: "You can no longer save changes to this message.",
});
}
if (message_too_long) {
return $t(
{
defaultMessage: `Message length shouldn't be greater than {max_length} characters.`,
},
{
max_length: realm.max_message_length,
},
);
}
return "";
}
export function needs_subscribe_warning(user_id: number, stream_id: number): boolean {
// This returns true if all of these conditions are met:
// * the user is valid
@@ -727,6 +757,7 @@ export function check_overflow_text($container: JQuery): number {
const max_length = realm.max_message_length;
const remaining_characters = max_length - text.length;
const $indicator = $container.find(".message-limit-indicator");
const is_edit_container = $textarea.closest(".message_row").length > 0;
if (text.length > max_length) {
$indicator.addClass("over_limit");
@@ -736,7 +767,11 @@ export function check_overflow_text($container: JQuery): number {
remaining_characters,
}),
);
set_message_too_long_for_compose(true);
if (is_edit_container) {
set_message_too_long_for_edit(true, $container);
} else {
set_message_too_long_for_compose(true);
}
} else if (remaining_characters <= 900) {
$indicator.removeClass("over_limit");
$textarea.removeClass("over_limit");
@@ -745,12 +780,20 @@ export function check_overflow_text($container: JQuery): number {
remaining_characters,
}),
);
set_message_too_long_for_compose(false);
if (is_edit_container) {
set_message_too_long_for_edit(false, $container);
} else {
set_message_too_long_for_compose(false);
}
} else {
$indicator.text("");
$textarea.removeClass("over_limit");
set_message_too_long_for_compose(false);
if (is_edit_container) {
set_message_too_long_for_edit(false, $container);
} else {
set_message_too_long_for_compose(false);
}
}
return text.length;

View File

@@ -404,6 +404,7 @@ function handle_message_edit_enter(
// Prevent default to avoid new-line on pressing
// Enter inside the textarea in this case
e.preventDefault();
compose_validate.validate_message_length($row);
return;
}
save_message_row_edit($row);
@@ -538,6 +539,10 @@ function edit_message($row: JQuery, raw_content: string): void {
}
});
$form.on("input propertychange", () => {
compose_validate.check_overflow_text($row);
});
$form
.find(".message-edit-feature-group .video_link")
.toggle(compose_call.compute_show_video_chat_button());
@@ -595,7 +600,8 @@ function edit_message($row: JQuery, raw_content: string): void {
// the half-finished edit around so that they can copy-paste it, but we don't want
// people to think "Save" will save the half-finished edit.
$message_edit_save.prop("disabled", true);
$message_edit_save_container.addClass("tippy-zulip-tooltip");
$message_edit_save_container.addClass("message-edit-time-limit-expired");
$message_edit_save_container.addClass("disabled-message-edit-save");
$message_edit_countdown_timer.addClass("expired");
$message_edit_countdown_timer.text($t({defaultMessage: "Time's up!"}));
} else {
@@ -614,6 +620,7 @@ function edit_message($row: JQuery, raw_content: string): void {
if (contents) {
$message_edit_content.val(contents);
}
compose_validate.check_overflow_text($row);
}
}

View File

@@ -6,6 +6,7 @@ import render_message_edit_notice_tooltip from "../templates/message_edit_notice
import render_message_inline_image_tooltip from "../templates/message_inline_image_tooltip.hbs";
import render_narrow_tooltip from "../templates/narrow_tooltip.hbs";
import * as compose_validate from "./compose_validate.ts";
import {$t} from "./i18n.ts";
import * as message_lists from "./message_lists.ts";
import type {Message} from "./message_store.ts";
@@ -282,6 +283,19 @@ export function initialize(): void {
},
});
message_list_tooltip(".disabled-message-edit-save", {
onShow(instance) {
const $elem = $(instance.reference);
const $row = $elem.closest(".message_row");
assert($row !== undefined);
instance.setContent(compose_validate.get_disabled_save_tooltip($row));
return undefined;
},
onHidden(instance) {
instance.destroy();
},
});
message_list_tooltip(".recipient_row_date > span", {
onHidden(instance) {
instance.destroy();

View File

@@ -327,16 +327,6 @@
border-color: var(--color-message-content-container-border-focus);
}
&:has(.new_message_textarea.over_limit),
&:has(.new_message_textarea.over_limit:focus) {
box-shadow: 0 0 0 1pt
var(--color-message-content-container-border-over-limit);
}
&:has(.new_message_textarea.over_limit.flash) {
animation: message-limit-flash 0.5s ease-in-out 3;
}
&:has(.new_message_textarea.invalid),
&:has(.new_message_textarea.invalid:focus) {
border-color: hsl(3deg 57% 33%);
@@ -344,6 +334,19 @@
}
}
#message-content-container:has(.new_message_textarea.over_limit),
#message-content-container:has(.new_message_textarea.over_limit:focus),
.edit-content-container:has(.message_edit_content.over_limit),
.edit-content-container:has(.message_edit_content.over_limit:focus) {
box-shadow: 0 0 0 1pt
var(--color-message-content-container-border-over-limit);
}
#message-content-container:has(.new_message_textarea.over_limit.flash),
.edit-content-container:has(.message_edit_content.over_limit.flash) {
animation: message-limit-flash 0.5s ease-in-out 3;
}
#message-content-container .composebox-buttons {
grid-area: composebox-buttons;
/* z-index is needed to avoid flickering of cursor and the

View File

@@ -8,7 +8,7 @@
<span class="copy_message copy-button copy-button-square tippy-zulip-tooltip" data-tippy-content="{{t 'Copy and close' }}" aria-label="{{t 'Copy and close' }}" role="button">
<i class="zulip-icon zulip-icon-copy" aria-hidden="true"></i>
</span>
<textarea class="message_edit_content" maxlength="{{ max_message_length }}">{{content}}</textarea>
<textarea class="message_edit_content message-textarea">{{content}}</textarea>
</div>
<div class="scrolling_list preview_message_area" id="preview_message_area_{{message_id}}" style="display:none;">
<div class="markdown_preview_spinner"></div>
@@ -24,14 +24,14 @@
{{/if}}
<div class="message-edit-buttons-and-timer">
{{#if is_editable}}
<div class="message_edit_save_container"
data-tippy-content="{{t 'You can no longer save changes to this message.' }}">
<div class="message_edit_save_container">
<button type="button" class="message-actions-button message_edit_save">
<img class="loader" alt="" src="" />
<span>{{t "Save" }}</span>
</button>
</div>
<button type="button" class="message-actions-button message_edit_cancel"><span>{{t "Cancel" }}</span></button>
<span class="tippy-zulip-tooltip message-limit-indicator" data-tippy-content="{{t 'Maximum message length: {max_message_length} characters' }}"></span>
<div class="message-edit-timer">
<span class="message_edit_countdown_timer
tippy-zulip-tooltip" data-tippy-content="{{t 'This organization is configured to restrict editing of message content to {minutes_to_edit} minutes after it is sent.' }}"></span>

View File

@@ -140,6 +140,15 @@ const everyone = {
user_groups.initialize({realm_user_groups: [nobody, everyone]});
function stub_message_row($textarea) {
const $stub = $.create("message_row_stub");
$textarea.closest = (selector) => {
assert.equal(selector, ".message_row");
$stub.length = 0;
return $stub;
};
}
function test_ui(label, f) {
// TODO: initialize data more aggressively.
run_test(label, f);
@@ -181,6 +190,7 @@ test_ui("send_message_success", ({override, override_rewire}) => {
const $elem = $("#send_message_form");
const $textarea = $("textarea#compose-textarea");
const $indicator = $("#compose-limit-indicator");
stub_message_row($textarea);
$elem.set_find_results(".message-textarea", $textarea);
$elem.set_find_results(".message-limit-indicator", $indicator);
@@ -248,6 +258,7 @@ test_ui("send_message", ({override, override_rewire, mock_template}) => {
const $elem = $("#send_message_form");
const $textarea = $("textarea#compose-textarea");
const $indicator = $("#compose-limit-indicator");
stub_message_row($textarea);
$elem.set_find_results(".message-textarea", $textarea);
$elem.set_find_results(".message-limit-indicator", $indicator);

View File

@@ -126,6 +126,15 @@ function test(label, f) {
});
}
function stub_message_row($textarea) {
const $stub = $.create("message_row_stub");
$textarea.closest = (selector) => {
assert.equal(selector, ".message_row");
$stub.length = 0;
return $stub;
};
}
test("initial_state", () => {
assert.equal(compose_state.composing(), false);
assert.equal(compose_state.get_message_type(), undefined);
@@ -143,6 +152,7 @@ test("start", ({override, override_rewire, mock_template}) => {
const $elem = $("#send_message_form");
const $textarea = $("textarea#compose-textarea");
const $indicator = $("#compose-limit-indicator");
stub_message_row($textarea);
$elem.set_find_results(".message-textarea", $textarea);
$elem.set_find_results(".message-limit-indicator", $indicator);
@@ -290,6 +300,7 @@ test("respond_to_message", ({override, override_rewire, mock_template}) => {
const $elem = $("#send_message_form");
const $textarea = $("textarea#compose-textarea");
const $indicator = $("#compose-limit-indicator");
stub_message_row($textarea);
$elem.set_find_results(".message-textarea", $textarea);
$elem.set_find_results(".message-limit-indicator", $indicator);
@@ -351,6 +362,7 @@ test("reply_with_mention", ({override, override_rewire, mock_template}) => {
const $elem = $("#send_message_form");
const $textarea = $("textarea#compose-textarea");
const $indicator = $("#compose-limit-indicator");
stub_message_row($textarea);
$elem.set_find_results(".message-textarea", $textarea);
$elem.set_find_results(".message-limit-indicator", $indicator);
@@ -411,6 +423,7 @@ test("quote_and_reply", ({disallow, override, override_rewire}) => {
const $elem = $("#send_message_form");
const $textarea = $("textarea#compose-textarea");
const $indicator = $("#compose-limit-indicator");
stub_message_row($textarea);
$elem.set_find_results(".message-textarea", $textarea);
$elem.set_find_results(".message-limit-indicator", $indicator);