diff --git a/web/src/compose.js b/web/src/compose.js index bf70856541..12698d8e57 100644 --- a/web/src/compose.js +++ b/web/src/compose.js @@ -58,13 +58,16 @@ export function show_preview_area() { $("#compose").addClass("preview_mode"); $("#compose .preview_mode_disabled .compose_control_button").attr("tabindex", -1); - const $compose_textarea = $("textarea#compose-textarea"); - const content = $compose_textarea.val(); - $("#compose .markdown_preview").hide(); $("#compose .undo_markdown_preview").show(); $("#compose .undo_markdown_preview").trigger("focus"); + render_preview_area(); +} + +export function render_preview_area() { + const $compose_textarea = $("textarea#compose-textarea"); + const content = $compose_textarea.val(); const $preview_message_area = $("#compose .preview_message_area"); compose_ui.render_and_show_preview( $("#compose .markdown_preview_spinner"), diff --git a/web/src/compose_setup.js b/web/src/compose_setup.js index 22e833eb43..924f292315 100644 --- a/web/src/compose_setup.js +++ b/web/src/compose_setup.js @@ -80,6 +80,9 @@ export function initialize() { }); $("textarea#compose-textarea").on("input propertychange", () => { + if ($("#compose").hasClass("preview_mode")) { + compose.render_preview_area(); + } compose_validate.warn_if_topic_resolved(false); const compose_text_length = compose_validate.check_overflow_text($("#send_message_form")); if (compose_text_length !== 0 && $("textarea#compose-textarea").hasClass("invalid")) { diff --git a/web/src/compose_state.ts b/web/src/compose_state.ts index 97c3dd7f6e..e94c124728 100644 --- a/web/src/compose_state.ts +++ b/web/src/compose_state.ts @@ -9,6 +9,7 @@ let message_type: "stream" | "private" | undefined; let recipient_edited_manually = false; let is_content_unedited_restored_draft = false; let last_focused_compose_type_input: HTMLTextAreaElement | undefined; +let preview_render_count = 0; // We use this variable to keep track of whether user has viewed the topic resolved // banner for the current compose session, for a narrow. This prevents the banner @@ -67,6 +68,14 @@ export function get_recipient_guest_ids_for_dm_warning(): number[] { return recipient_guest_ids_for_dm_warning; } +export function get_preview_render_count(): number { + return preview_render_count; +} + +export function set_preview_render_count(count: number): void { + preview_render_count = count; +} + export function composing(): boolean { // This is very similar to get_message_type(), but it returns // a boolean. diff --git a/web/src/compose_ui.ts b/web/src/compose_ui.ts index ce486247b9..9711d8b7b0 100644 --- a/web/src/compose_ui.ts +++ b/web/src/compose_ui.ts @@ -17,6 +17,7 @@ import type {Typeahead} from "./bootstrap_typeahead.ts"; import * as bulleted_numbered_list_util from "./bulleted_numbered_list_util.ts"; import * as channel from "./channel.ts"; import * as common from "./common.ts"; +import * as compose_state from "./compose_state.ts"; import type {TypeaheadSuggestion} from "./composebox_typeahead.ts"; import {$t, $t_html} from "./i18n.ts"; import * as loading from "./loading.ts"; @@ -1310,6 +1311,9 @@ export function render_and_show_preview( $preview_content_box: JQuery, content: string, ): void { + const preview_render_count = compose_state.get_preview_render_count() + 1; + compose_state.set_preview_render_count(preview_render_count); + function show_preview(rendered_content: string, raw_content?: string): void { // content is passed to check for status messages ("/me ...") // and will be undefined in case of errors @@ -1350,6 +1354,12 @@ export function render_and_show_preview( url: "/json/messages/render", data: {content}, success(response_data) { + if (preview_render_count !== compose_state.get_preview_render_count()) { + // The compose input has already been updated with new raw Markdown + // since this rendering request was sent off to the server, so + // there's nothing to do. + return; + } const data = message_render_response_schema.parse(response_data); if (markdown.contains_backend_only_syntax(content)) { loading.destroy_indicator($preview_spinner);