Skip to content

Commit

Permalink
message_edit: Fix event handlers lost on restoring edit form.
Browse files Browse the repository at this point in the history
To avoid event handlers being lost when we restore a message edit
form we go through the process of creating the edit form for the
message again. This has the additional benefit of updating the
status of buttons based on org settings like GIF or upload button.
  • Loading branch information
amanagr authored and timabbott committed May 14, 2024
1 parent ca8f04e commit f57fb08
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 50 deletions.
76 changes: 29 additions & 47 deletions web/src/message_edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ import * as ui_report from "./ui_report";
import * as upload from "./upload";
import * as util from "./util";

// Stores the message ID of the message being edited, and the
// textarea element which has the modified content.
// Storing textarea makes it easy to get the current content.
export const currently_editing_messages = new Map();
let currently_deleting_messages = [];
let currently_topic_editing_messages = [];
Expand Down Expand Up @@ -414,10 +417,9 @@ function timer_text(seconds_left) {
return $t({defaultMessage: "{seconds} sec to edit"}, {seconds: seconds.toString()});
}

function create_copy_to_clipboard_handler($row, source, message_id) {
function create_copy_to_clipboard_handler($row, source, $message_edit_content) {
const clipboard = new ClipboardJS(source, {
target: () =>
document.querySelector(`#edit_form_${CSS.escape(message_id)} .message_edit_content`),
target: () => $message_edit_content[0],
});

clipboard.on("success", () => {
Expand All @@ -434,12 +436,16 @@ function create_copy_to_clipboard_handler($row, source, message_id) {
}

function edit_message($row, raw_content) {
// Open the message-edit UI for a given message.
//
// Notably, when switching views, this can be called for a row
// that hasn't been added to the DOM yet so, keep all the selector
// queries and events to operate on `$row` or `$form`.
assert(message_lists.current !== undefined);
const message = message_lists.current.get(rows.id($row));
$row.find(".message_reactions").hide();
condense.hide_message_expander($row);
condense.hide_message_condenser($row);
const content_top = $row.find(".message_controls")[0].getBoundingClientRect().top;

// We potentially got to this function by clicking a button that implied the
// user would be able to edit their message. Give a little bit of buffer in
Expand Down Expand Up @@ -471,9 +477,10 @@ function edit_message($row, raw_content) {
}),
);

const edit_obj = {$form, raw_content};
currently_editing_messages.set(message.id, edit_obj);
message_lists.current.show_edit_message($row, edit_obj);
const $message_edit_content = $form.find("textarea.message_edit_content");
assert($message_edit_content.length === 1);
currently_editing_messages.set(message.id, $message_edit_content);
message_lists.current.show_edit_message($row, $form);

$form.on("keydown", handle_message_row_edit_keydown);

Expand All @@ -484,27 +491,22 @@ function edit_message($row, raw_content) {
.find(".message-edit-feature-group .audio_link")
.toggle(compose_call.compute_show_audio_chat_button());

const $message_edit_content = $row.find("textarea.message_edit_content");
const $message_edit_countdown_timer = $row.find(".message_edit_countdown_timer");
const $copy_message = $row.find(".copy_message");

if (!is_editable) {
$message_edit_content.attr("readonly", "readonly");
create_copy_to_clipboard_handler($row, $copy_message[0], message.id);
create_copy_to_clipboard_handler($row, $copy_message[0], $message_edit_content);
} else {
$copy_message.remove();
const edit_id = `#edit_form_${CSS.escape(rows.id($row))} .message_edit_content`;
const listeners = resize.watch_manual_resize(edit_id);
if (listeners) {
currently_editing_messages.get(rows.id($row)).listeners = listeners;
}
composebox_typeahead.initialize_compose_typeahead(edit_id);
compose_ui.handle_keyup(null, $(edit_id).expectOne());
$(edit_id).on("keydown", function (event) {
compose_ui.handle_keydown(event, $(this).expectOne());
resize.watch_manual_resize_for_element($message_edit_content[0]);
composebox_typeahead.initialize_compose_typeahead($message_edit_content[0]);
compose_ui.handle_keyup(null, $message_edit_content);
$message_edit_content.on("keydown", (event) => {
compose_ui.handle_keydown(event, $message_edit_content);
});
$(edit_id).on("keyup", function (event) {
compose_ui.handle_keyup(event, $(this).expectOne());
$message_edit_content.on("keyup", (event) => {
compose_ui.handle_keyup(event, $message_edit_content);
});
}

Expand Down Expand Up @@ -556,17 +558,12 @@ function edit_message($row, raw_content) {
$message_edit_content.val("");
$message_edit_content.val(contents);
}

// Scroll to keep the top of the message content text in the same
// place visually, adjusting for border and padding.
const edit_top = $message_edit_content[0].getBoundingClientRect().top;
const scroll_by = edit_top - content_top + 5 - 14;

edit_obj.scrolled_by = scroll_by;
message_viewport.scrollTop(message_viewport.scrollTop() + scroll_by);
}

function start_edit_maintaining_scroll($row, content) {
// This function makes the bottom of the edit form visible, so
// call this for cases where it is important to show the bottom
// like showing error messages or upload status.
edit_message($row, content);
const row_bottom = $row.get_offset_to_window().bottom;
const composebox_top = $("#compose").get_offset_to_window().top;
Expand Down Expand Up @@ -802,24 +799,8 @@ export function end_message_row_edit($row) {

const message = message_lists.current.get(row_id);
if (message !== undefined && currently_editing_messages.has(message.id)) {
const scroll_by = currently_editing_messages.get(message.id).scrolled_by;
const original_scrollTop = message_viewport.scrollTop();

// Clean up resize event listeners
const listeners = currently_editing_messages.get(message.id).listeners;
const edit_box = document.querySelector(
`#edit_form_${CSS.escape(message.id)} .message_edit_content`,
);
if (listeners !== undefined) {
// Event listeners to clean up are only set in some edit types
edit_box.removeEventListener("mousedown", listeners[0]);
document.body.removeEventListener("mouseup", listeners[1]);
}

currently_editing_messages.delete(message.id);
message_lists.current.hide_edit_message($row);
message_viewport.scrollTop(original_scrollTop - scroll_by);

compose_call.abort_video_callbacks(message.id);
}
if ($row.find(".condensed").length !== 0) {
Expand Down Expand Up @@ -1129,7 +1110,8 @@ export function maybe_show_edit($row, id) {
}

if (currently_editing_messages.has(id)) {
message_lists.current.show_edit_message($row, currently_editing_messages.get(id));
const $message_edit_content = currently_editing_messages.get(id);
edit_message($row, $message_edit_content.val());
}
}

Expand Down Expand Up @@ -1238,10 +1220,10 @@ export function delete_topic(stream_id, topic_name, failures = 0) {

export function restore_edit_state_after_message_view_change() {
assert(message_lists.current !== undefined);
for (const [idx, elem] of currently_editing_messages) {
for (const [idx, $content] of currently_editing_messages) {
if (message_lists.current.get(idx) !== undefined) {
const $row = message_lists.current.get_row(idx);
message_lists.current.show_edit_message($row, elem);
edit_message($row, $content.val());
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions web/src/message_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -392,14 +392,16 @@ export class MessageList {
this.rerender();
}

show_edit_message($row, edit_obj) {
show_edit_message($row, $form) {
if ($row.find(".message_edit_form form").length !== 0) {
return;
}
$row.find(".message_edit_form").append(edit_obj.$form);
$row.find(".message_edit_form").append($form);
$row.find(".message_content, .status-message, .message_controls").hide();
$row.find(".messagebox-content").addClass("content_edit_mode");
$row.find(".message_edit").css("display", "block");
// autosize will not change the height of the textarea if the `$row` is not
// rendered in DOM yet. So, we call `autosize.update` post render.
autosize($row.find(".message_edit_content"));
}

Expand Down
4 changes: 4 additions & 0 deletions web/src/message_list_view.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import autosize from "autosize";
import $ from "jquery";
import _ from "lodash";

Expand Down Expand Up @@ -979,6 +980,9 @@ export class MessageListView {
condense.condense_and_collapse($dom_messages);
}

// After all the messages are rendered, resize any message edit textarea if required.
autosize.update(this.$list.find(".message_edit_content"));

restore_scroll_position();

const last_message_group = this._message_groups.at(-1);
Expand Down
6 changes: 5 additions & 1 deletion web/src/resize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ export function watch_manual_resize(element: string): (() => void)[] | undefined
return undefined;
}

return watch_manual_resize_for_element(box);
}

export function watch_manual_resize_for_element(box: Element): (() => void)[] {
let height: number;
let mousedown = false;

Expand All @@ -71,7 +75,7 @@ export function watch_manual_resize(element: string): (() => void)[] | undefined
mousedown = false;
if (height !== box.clientHeight) {
height = box.clientHeight;
autosize.destroy($(element)).height(height + "px");
autosize.destroy($(box)).height(height + "px");
}
}
};
Expand Down

0 comments on commit f57fb08

Please sign in to comment.