mirror of
				https://github.com/zulip/zulip.git
				synced 2025-10-31 12:03:46 +00:00 
			
		
		
		
	This commit adds tooltip support for various invalid conditions mentioned in issue 32115. A `show_banner` positional argument is added in the `validate` method which has a default value of true. The reason behind introducing this is to not trigger banners on hovering the disabled send button, since the tooltip message is also determined using the same validate method. We want to only disable the button on hover, which is why the update_send_button_status() method is called only on "mouseenter" event, which is added to the send button in compose_setup.js To incorporate this change a new param is introduced which determines whether to enable/disable send_button by running update_send_button_status Earlier, typing something in the textarea or recipient box would also trigger `update_send_button_status` which doesn't work well since we've introduced a lot of new booleans which determine whether send button gets disabled causing send button to get disabled while typing instead while hovering Hence this change.
		
			
				
	
	
		
			636 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			636 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import $ from "jquery";
 | |
| import _ from "lodash";
 | |
| 
 | |
| import {unresolve_name} from "../shared/src/resolved_topic.ts";
 | |
| import render_add_poll_modal from "../templates/add_poll_modal.hbs";
 | |
| import render_add_todo_list_modal from "../templates/add_todo_list_modal.hbs";
 | |
| 
 | |
| import * as compose from "./compose.js";
 | |
| import * as compose_actions from "./compose_actions.ts";
 | |
| import * as compose_banner from "./compose_banner.ts";
 | |
| import * as compose_call from "./compose_call.ts";
 | |
| import * as compose_call_ui from "./compose_call_ui.ts";
 | |
| import * as compose_notifications from "./compose_notifications.ts";
 | |
| import * as compose_recipient from "./compose_recipient.ts";
 | |
| import * as compose_send_menu_popover from "./compose_send_menu_popover.js";
 | |
| import * as compose_state from "./compose_state.ts";
 | |
| import * as compose_ui from "./compose_ui.ts";
 | |
| import * as compose_validate from "./compose_validate.ts";
 | |
| import * as dialog_widget from "./dialog_widget.ts";
 | |
| import * as flatpickr from "./flatpickr.ts";
 | |
| import {$t_html} from "./i18n.ts";
 | |
| import * as message_edit from "./message_edit.ts";
 | |
| import * as message_view from "./message_view.ts";
 | |
| import * as narrow_state from "./narrow_state.ts";
 | |
| import * as onboarding_steps from "./onboarding_steps.ts";
 | |
| import {page_params} from "./page_params.ts";
 | |
| import * as popovers from "./popovers.ts";
 | |
| import * as resize from "./resize.ts";
 | |
| import * as rows from "./rows.ts";
 | |
| import * as scheduled_messages from "./scheduled_messages.ts";
 | |
| import * as stream_data from "./stream_data.ts";
 | |
| import * as stream_settings_components from "./stream_settings_components.ts";
 | |
| import * as sub_store from "./sub_store.ts";
 | |
| import * as subscriber_api from "./subscriber_api.ts";
 | |
| import {get_timestamp_for_flatpickr} from "./timerender.ts";
 | |
| import * as ui_report from "./ui_report.ts";
 | |
| import * as upload from "./upload.ts";
 | |
| import * as user_topics from "./user_topics.ts";
 | |
| import * as widget_modal from "./widget_modal.ts";
 | |
| 
 | |
| export function abort_xhr() {
 | |
|     $("#compose-send-button").prop("disabled", false);
 | |
|     upload.compose_upload_cancel();
 | |
| }
 | |
| 
 | |
| function setup_compose_actions_hooks() {
 | |
|     compose_actions.register_compose_box_clear_hook(compose.clear_invites);
 | |
|     compose_actions.register_compose_box_clear_hook(compose.clear_private_stream_alert);
 | |
|     compose_actions.register_compose_box_clear_hook(compose.clear_preview_area);
 | |
| 
 | |
|     compose_actions.register_compose_cancel_hook(abort_xhr);
 | |
|     compose_actions.register_compose_cancel_hook(compose_call.abort_video_callbacks);
 | |
| }
 | |
| 
 | |
| export function initialize() {
 | |
|     // Register hooks for compose_actions.
 | |
|     setup_compose_actions_hooks();
 | |
| 
 | |
|     $(".compose-control-buttons-container .video_link").toggle(
 | |
|         compose_call.compute_show_video_chat_button(),
 | |
|     );
 | |
|     $(".compose-control-buttons-container .audio_link").toggle(
 | |
|         compose_call.compute_show_audio_chat_button(),
 | |
|     );
 | |
| 
 | |
|     $("textarea#compose-textarea").on("keydown", (event) => {
 | |
|         compose_ui.handle_keydown(event, $("textarea#compose-textarea").expectOne());
 | |
|     });
 | |
|     $("textarea#compose-textarea").on("keyup", (event) => {
 | |
|         compose_ui.handle_keyup(event, $("textarea#compose-textarea").expectOne());
 | |
|     });
 | |
| 
 | |
|     $("#compose-send-button").on("mouseenter", () => {
 | |
|         compose_validate.validate(false, false);
 | |
|         compose_validate.update_send_button_status();
 | |
|     });
 | |
|     $("#compose-send-button").on("mouseleave", () => {
 | |
|         $(".message-send-controls").removeClass("disabled-message-send-controls");
 | |
|     });
 | |
| 
 | |
|     $("textarea#compose-textarea").on("input propertychange", () => {
 | |
|         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")) {
 | |
|             $("textarea#compose-textarea").toggleClass("invalid", false);
 | |
|         }
 | |
|         // Change compose close button tooltip as per condition.
 | |
|         // We save compose text in draft only if its length is > 2.
 | |
|         if (compose_text_length > 2) {
 | |
|             $("#compose_close").attr(
 | |
|                 "data-tooltip-template-id",
 | |
|                 "compose_close_and_save_tooltip_template",
 | |
|             );
 | |
|         } else {
 | |
|             $("#compose_close").attr("data-tooltip-template-id", "compose_close_tooltip_template");
 | |
|         }
 | |
| 
 | |
|         // The poll widget requires an empty compose box.
 | |
|         if (compose_text_length > 0) {
 | |
|             $(".needs-empty-compose").parent().addClass("disabled-on-hover");
 | |
|         } else {
 | |
|             $(".needs-empty-compose").parent().removeClass("disabled-on-hover");
 | |
|         }
 | |
| 
 | |
|         if (compose_state.get_is_content_unedited_restored_draft()) {
 | |
|             compose_state.set_is_content_unedited_restored_draft(false);
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     $("#compose form").on("submit", (e) => {
 | |
|         e.preventDefault();
 | |
|         compose.finish();
 | |
|     });
 | |
| 
 | |
|     resize.watch_manual_resize("#compose-textarea");
 | |
| 
 | |
|     // Updates compose max-height and scroll to bottom button position when
 | |
|     // there is a change in compose height like when a compose banner is displayed.
 | |
|     const update_compose_max_height = new ResizeObserver((_entries) => {
 | |
|         requestAnimationFrame(() => {
 | |
|             resize.reset_compose_message_max_height();
 | |
|         });
 | |
|     });
 | |
|     update_compose_max_height.observe(document.querySelector("#compose"));
 | |
| 
 | |
|     function get_input_info(event) {
 | |
|         const $edit_banners_container = $(event.target).closest(".edit_form_banners");
 | |
|         const is_edit_input = $edit_banners_container.length > 0;
 | |
|         const $banner_container = is_edit_input ? $edit_banners_container : $("#compose_banners");
 | |
|         return {is_edit_input, $banner_container};
 | |
|     }
 | |
| 
 | |
|     $("body").on(
 | |
|         "click",
 | |
|         `.${CSS.escape(
 | |
|             compose_banner.CLASSNAMES.wildcard_warning,
 | |
|         )} .main-view-banner-action-button`,
 | |
|         (event) => {
 | |
|             event.preventDefault();
 | |
|             const {$banner_container, is_edit_input} = get_input_info(event);
 | |
|             const $row = $(event.target).closest(".message_row");
 | |
|             compose_validate.clear_stream_wildcard_warnings($banner_container);
 | |
|             compose_validate.set_user_acknowledged_stream_wildcard_flag(true);
 | |
|             if (is_edit_input) {
 | |
|                 message_edit.save_message_row_edit($row);
 | |
|             } else if (event.target.dataset.validationTrigger === "schedule") {
 | |
|                 compose_send_menu_popover.open_send_later_menu();
 | |
| 
 | |
|                 // We need to set this flag to true here because `open_send_later_menu` validates the message and sets
 | |
|                 // the user acknowledged wildcard flag back to 'false' and we don't want that to happen because then it
 | |
|                 // would again show the wildcard warning banner when we actually send the message from 'send-later' modal.
 | |
|                 compose_validate.set_user_acknowledged_stream_wildcard_flag(true);
 | |
|             } else {
 | |
|                 compose.finish();
 | |
|             }
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     const user_not_subscribed_selector = `.${CSS.escape(
 | |
|         compose_banner.CLASSNAMES.user_not_subscribed,
 | |
|     )}`;
 | |
|     $("body").on(
 | |
|         "click",
 | |
|         `${user_not_subscribed_selector} .main-view-banner-action-button`,
 | |
|         (event) => {
 | |
|             event.preventDefault();
 | |
| 
 | |
|             const stream_id = compose_state.stream_id();
 | |
|             if (stream_id === undefined) {
 | |
|                 return;
 | |
|             }
 | |
|             const sub = stream_data.get_sub_by_id(stream_id);
 | |
|             stream_settings_components.sub_or_unsub(sub);
 | |
|             $(user_not_subscribed_selector).remove();
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     $("body").on(
 | |
|         "click",
 | |
|         `.${CSS.escape(compose_banner.CLASSNAMES.topic_resolved)} .main-view-banner-action-button`,
 | |
|         (event) => {
 | |
|             event.preventDefault();
 | |
| 
 | |
|             const $target = $(event.target).parents(".main-view-banner");
 | |
|             const stream_id = Number.parseInt($target.attr("data-stream-id"), 10);
 | |
|             const topic_name = $target.attr("data-topic-name");
 | |
| 
 | |
|             message_edit.with_first_message_id(stream_id, topic_name, (message_id) => {
 | |
|                 if (message_id === undefined) {
 | |
|                     // There is no message in the topic, so it is sufficient to
 | |
|                     // just remove the topic resolved prefix (✔) from the topic name.
 | |
|                     const $input = $("input#stream_message_recipient_topic");
 | |
|                     const new_topic = unresolve_name(topic_name);
 | |
|                     $input.val(new_topic);
 | |
|                     // Trigger an input event, since this is a form of
 | |
|                     // user-triggered edit to that field.
 | |
|                     $input.trigger("input");
 | |
| 
 | |
|                     // Renarrow to the unresolved topic if currently viewing the resolved topic.
 | |
|                     const current_filter = narrow_state.filter();
 | |
|                     const stream_id_string = stream_id.toString();
 | |
|                     if (
 | |
|                         current_filter &&
 | |
|                         (current_filter.is_conversation_view() ||
 | |
|                             current_filter.is_conversation_view_with_near()) &&
 | |
|                         current_filter.has_topic(stream_id_string, topic_name)
 | |
|                     ) {
 | |
|                         message_view.show(
 | |
|                             [
 | |
|                                 {operator: "channel", operand: stream_id_string},
 | |
|                                 {operator: "topic", operand: new_topic},
 | |
|                             ],
 | |
|                             {},
 | |
|                         );
 | |
|                     }
 | |
|                 } else {
 | |
|                     message_edit.toggle_resolve_topic(message_id, topic_name, true);
 | |
|                 }
 | |
|                 compose_validate.clear_topic_resolved_warning(true);
 | |
|             });
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     $("body").on(
 | |
|         "click",
 | |
|         `.${CSS.escape(
 | |
|             compose_banner.CLASSNAMES.unmute_topic_notification,
 | |
|         )} .main-view-banner-action-button`,
 | |
|         (event) => {
 | |
|             event.preventDefault();
 | |
| 
 | |
|             const $target = $(event.target).parents(".main-view-banner");
 | |
|             const stream_id = Number.parseInt($target.attr("data-stream-id"), 10);
 | |
|             const topic_name = $target.attr("data-topic-name");
 | |
| 
 | |
|             user_topics.set_user_topic_visibility_policy(
 | |
|                 stream_id,
 | |
|                 topic_name,
 | |
|                 user_topics.all_visibility_policies.UNMUTED,
 | |
|                 false,
 | |
|                 true,
 | |
|             );
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     const automatic_new_visibility_policy_banner_selector = `.${CSS.escape(compose_banner.CLASSNAMES.automatic_new_visibility_policy)}`;
 | |
|     $("body").on(
 | |
|         "click",
 | |
|         `${automatic_new_visibility_policy_banner_selector} .main-view-banner-action-button`,
 | |
|         (event) => {
 | |
|             event.preventDefault();
 | |
|             if ($(event.target).attr("data-action") === "mark-as-read") {
 | |
|                 $(event.target)
 | |
|                     .parents(`${automatic_new_visibility_policy_banner_selector}`)
 | |
|                     .remove();
 | |
|                 onboarding_steps.post_onboarding_step_as_read("visibility_policy_banner");
 | |
|                 return;
 | |
|             }
 | |
|             window.location.href = "/#settings/notifications";
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     $("body").on(
 | |
|         "click",
 | |
|         `.${CSS.escape(
 | |
|             compose_banner.CLASSNAMES.unscheduled_message,
 | |
|         )} .main-view-banner-action-button`,
 | |
|         (event) => {
 | |
|             event.preventDefault();
 | |
|             const send_at_timestamp = scheduled_messages.get_selected_send_later_timestamp();
 | |
|             compose_send_menu_popover.do_schedule_message(send_at_timestamp);
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     $("body").on(
 | |
|         "click",
 | |
|         `.${CSS.escape(
 | |
|             compose_banner.CLASSNAMES.recipient_not_subscribed,
 | |
|         )} .main-view-banner-action-button`,
 | |
|         (event) => {
 | |
|             event.preventDefault();
 | |
|             const {$banner_container} = get_input_info(event);
 | |
|             const $invite_row = $(event.target).parents(".main-view-banner");
 | |
| 
 | |
|             const user_id = Number($invite_row.attr("data-user-id"));
 | |
|             const stream_id = Number($invite_row.attr("data-stream-id"));
 | |
| 
 | |
|             function success() {
 | |
|                 $invite_row.remove();
 | |
|             }
 | |
| 
 | |
|             function xhr_failure(xhr) {
 | |
|                 let error_message = "Failed to subscribe user!";
 | |
|                 if (xhr.responseJSON?.msg) {
 | |
|                     error_message = xhr.responseJSON.msg;
 | |
|                 }
 | |
|                 compose.clear_invites();
 | |
|                 compose_banner.show_error_message(
 | |
|                     error_message,
 | |
|                     compose_banner.CLASSNAMES.generic_compose_error,
 | |
|                     $banner_container,
 | |
|                     $("textarea#compose-textarea"),
 | |
|                 );
 | |
|                 $(event.target).prop("disabled", true);
 | |
|             }
 | |
| 
 | |
|             const sub = sub_store.get(stream_id);
 | |
| 
 | |
|             subscriber_api.add_user_ids_to_stream([user_id], sub, success, xhr_failure);
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     $("body").on(
 | |
|         "click",
 | |
|         `.${CSS.escape(compose_banner.CLASSNAMES.search_view)} .main-view-banner-action-button`,
 | |
|         (event) => {
 | |
|             event.preventDefault();
 | |
|             message_view.to_compose_target();
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     const jump_to_conversation_banner_selector = `.${CSS.escape(compose_banner.CLASSNAMES.jump_to_sent_message_conversation)}`;
 | |
|     $("body").on(
 | |
|         "click",
 | |
|         `${jump_to_conversation_banner_selector} .main-view-banner-action-button`,
 | |
|         (event) => {
 | |
|             event.preventDefault();
 | |
|             $(event.target).parents(`${jump_to_conversation_banner_selector}`).remove();
 | |
|             onboarding_steps.post_onboarding_step_as_read("jump_to_conversation_banner");
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     const non_interleaved_view_messages_fading_banner_selector = `.${CSS.escape(compose_banner.CLASSNAMES.non_interleaved_view_messages_fading)}`;
 | |
|     $("body").on(
 | |
|         "click",
 | |
|         `${non_interleaved_view_messages_fading_banner_selector} .main-view-banner-action-button`,
 | |
|         (event) => {
 | |
|             event.preventDefault();
 | |
|             $(event.target)
 | |
|                 .parents(`${non_interleaved_view_messages_fading_banner_selector}`)
 | |
|                 .remove();
 | |
|             onboarding_steps.post_onboarding_step_as_read("non_interleaved_view_messages_fading");
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     const interleaved_view_messages_fading_banner_selector = `.${CSS.escape(compose_banner.CLASSNAMES.interleaved_view_messages_fading)}`;
 | |
|     $("body").on(
 | |
|         "click",
 | |
|         `${interleaved_view_messages_fading_banner_selector} .main-view-banner-action-button`,
 | |
|         (event) => {
 | |
|             event.preventDefault();
 | |
|             $(event.target).parents(`${interleaved_view_messages_fading_banner_selector}`).remove();
 | |
|             onboarding_steps.post_onboarding_step_as_read("interleaved_view_messages_fading");
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     for (const classname of Object.values(compose_banner.CLASSNAMES)) {
 | |
|         const classname_selector = `.${CSS.escape(classname)}`;
 | |
|         $("body").on("click", `${classname_selector} .main-view-banner-close-button`, (event) => {
 | |
|             event.preventDefault();
 | |
|             $(event.target).parents(classname_selector).remove();
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     // Click event binding for "Attach files" button
 | |
|     // Triggers a click on a hidden file input field
 | |
| 
 | |
|     $("#compose").on("click", ".compose_upload_file", (e) => {
 | |
|         e.preventDefault();
 | |
|         e.stopPropagation();
 | |
| 
 | |
|         $("#compose .file_input").trigger("click");
 | |
|     });
 | |
| 
 | |
|     $("body").on("click", ".video_link", (e) => {
 | |
|         e.preventDefault();
 | |
|         e.stopPropagation();
 | |
| 
 | |
|         const show_video_chat_button = compose_call.compute_show_video_chat_button();
 | |
| 
 | |
|         if (!show_video_chat_button) {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         compose_call_ui.generate_and_insert_audio_or_video_call_link($(e.target), false);
 | |
|     });
 | |
| 
 | |
|     $("body").on("click", ".audio_link", (e) => {
 | |
|         e.preventDefault();
 | |
|         e.stopPropagation();
 | |
| 
 | |
|         const show_audio_chat_button = compose_call.compute_show_audio_chat_button();
 | |
| 
 | |
|         if (!show_audio_chat_button) {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         compose_call_ui.generate_and_insert_audio_or_video_call_link($(e.target), true);
 | |
|     });
 | |
| 
 | |
|     $("body").on("click", ".time_pick", function (e) {
 | |
|         e.preventDefault();
 | |
|         e.stopPropagation();
 | |
| 
 | |
|         let $target_textarea;
 | |
|         let edit_message_id;
 | |
|         const $compose_click_target = $(this);
 | |
|         if ($compose_click_target.parents(".message_edit_form").length === 1) {
 | |
|             edit_message_id = rows.id($compose_click_target.parents(".message_row"));
 | |
|             $target_textarea = $(`#edit_form_${CSS.escape(edit_message_id)} .message_edit_content`);
 | |
|         } else {
 | |
|             $target_textarea = $compose_click_target.closest("form").find("textarea");
 | |
|         }
 | |
| 
 | |
|         if (!flatpickr.is_open()) {
 | |
|             const on_timestamp_selection = (val) => {
 | |
|                 const timestr = `<time:${val}> `;
 | |
|                 compose_ui.insert_syntax_and_focus(timestr, $target_textarea);
 | |
|             };
 | |
| 
 | |
|             flatpickr.show_flatpickr(
 | |
|                 $compose_click_target[0],
 | |
|                 on_timestamp_selection,
 | |
|                 get_timestamp_for_flatpickr(),
 | |
|                 {
 | |
|                     // place the time picker wherever there is space and center it horizontally
 | |
|                     position: "auto center",
 | |
|                     // Since we want to handle close of flatpickr manually, we don't want
 | |
|                     // flatpickr to hide automatically on clicking its trigger element.
 | |
|                     ignoredFocusElements: [e.currentTarget],
 | |
|                 },
 | |
|             );
 | |
|         } else {
 | |
|             flatpickr.flatpickr_instance?.close();
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     $("body").on("click", ".compose_control_button_container:not(.disabled) .add-poll", (e) => {
 | |
|         e.preventDefault();
 | |
|         e.stopPropagation();
 | |
| 
 | |
|         function validate_input() {
 | |
|             const question = $("#poll-question-input").val().trim();
 | |
| 
 | |
|             if (question === "") {
 | |
|                 ui_report.error(
 | |
|                     $t_html({defaultMessage: "Please enter a question."}),
 | |
|                     undefined,
 | |
|                     $("#dialog_error"),
 | |
|                 );
 | |
|                 return false;
 | |
|             }
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         dialog_widget.launch({
 | |
|             html_heading: $t_html({defaultMessage: "Create a poll"}),
 | |
|             html_body: render_add_poll_modal(),
 | |
|             html_submit_button: $t_html({defaultMessage: "Add poll"}),
 | |
|             close_on_submit: true,
 | |
|             on_click(e) {
 | |
|                 // frame a message using data input in modal, then populate the compose textarea with it
 | |
|                 e.preventDefault();
 | |
|                 e.stopPropagation();
 | |
|                 const poll_message_content = widget_modal.frame_poll_message_content();
 | |
|                 compose_ui.insert_syntax_and_focus(poll_message_content);
 | |
|             },
 | |
|             on_show() {
 | |
|                 setTimeout(() => {
 | |
|                     $("#poll-question-input").trigger("focus");
 | |
|                 }, 0);
 | |
|             },
 | |
|             validate_input,
 | |
|             form_id: "add-poll-form",
 | |
|             id: "add-poll-modal",
 | |
|             post_render: widget_modal.poll_options_setup,
 | |
|             help_link: "https://zulip.com/help/create-a-poll",
 | |
|         });
 | |
|     });
 | |
| 
 | |
|     $("body").on("input", "#add-todo-modal .todo-input", (e) => {
 | |
|         e.preventDefault();
 | |
|         e.stopPropagation();
 | |
| 
 | |
|         $(".option-row").each(function () {
 | |
|             const todo_name = $(this).find(".todo-input").val();
 | |
|             const $todo_description = $(this).find(".todo-description-input");
 | |
|             $todo_description.prop("disabled", !todo_name);
 | |
|         });
 | |
|     });
 | |
| 
 | |
|     $("body").on(
 | |
|         "click",
 | |
|         ".compose_control_button_container:not(.disabled) .add-todo-list",
 | |
|         (e) => {
 | |
|             e.preventDefault();
 | |
|             e.stopPropagation();
 | |
| 
 | |
|             function validate_input(e) {
 | |
|                 let is_valid = true;
 | |
|                 e.preventDefault();
 | |
|                 e.stopPropagation();
 | |
|                 $(".option-row").each(function () {
 | |
|                     const todo_name = $(this).find(".todo-input").val();
 | |
|                     const todo_description = $(this).find(".todo-description-input").val();
 | |
|                     if (!todo_name && todo_description) {
 | |
|                         ui_report.error(
 | |
|                             $t_html({defaultMessage: "Please enter task title."}),
 | |
|                             undefined,
 | |
|                             $("#dialog_error"),
 | |
|                         );
 | |
|                         is_valid = false;
 | |
|                     }
 | |
|                 });
 | |
|                 return is_valid;
 | |
|             }
 | |
| 
 | |
|             dialog_widget.launch({
 | |
|                 html_heading: $t_html({defaultMessage: "Create a collaborative to-do list"}),
 | |
|                 html_body: render_add_todo_list_modal(),
 | |
|                 html_submit_button: $t_html({defaultMessage: "Create to-do list"}),
 | |
|                 close_on_submit: true,
 | |
|                 on_click(e) {
 | |
|                     // frame a message using data input in modal, then populate the compose textarea with it
 | |
|                     e.preventDefault();
 | |
|                     e.stopPropagation();
 | |
|                     const todo_message_content = widget_modal.frame_todo_message_content();
 | |
|                     compose_ui.insert_syntax_and_focus(todo_message_content);
 | |
|                 },
 | |
|                 on_show() {
 | |
|                     setTimeout(() => {
 | |
|                         $("#todo-title-input").trigger("select");
 | |
|                     }, 0);
 | |
|                 },
 | |
|                 form_id: "add-todo-form",
 | |
|                 validate_input,
 | |
|                 id: "add-todo-modal",
 | |
|                 post_render: widget_modal.todo_list_tasks_setup,
 | |
|                 help_link: "https://zulip.com/help/collaborative-to-do-lists",
 | |
|             });
 | |
|         },
 | |
|     );
 | |
| 
 | |
|     $("#compose").on("click", ".markdown_preview", (e) => {
 | |
|         e.preventDefault();
 | |
|         e.stopPropagation();
 | |
| 
 | |
|         compose.show_preview_area();
 | |
|     });
 | |
| 
 | |
|     $("#compose").on("click", ".undo_markdown_preview", (e) => {
 | |
|         e.preventDefault();
 | |
|         e.stopPropagation();
 | |
| 
 | |
|         compose.clear_preview_area();
 | |
|     });
 | |
| 
 | |
|     $("#compose").on("click", ".expand-composebox-button", (e) => {
 | |
|         e.preventDefault();
 | |
|         e.stopPropagation();
 | |
| 
 | |
|         compose_ui.make_compose_box_intermediate_size();
 | |
|     });
 | |
| 
 | |
|     $("#compose").on("click", ".maximize-composebox-button", (e) => {
 | |
|         e.preventDefault();
 | |
|         e.stopPropagation();
 | |
| 
 | |
|         compose_ui.make_compose_box_full_size();
 | |
|     });
 | |
| 
 | |
|     $("#compose").on("click", ".narrow_to_compose_recipients", (e) => {
 | |
|         e.preventDefault();
 | |
|         message_view.to_compose_target();
 | |
|     });
 | |
| 
 | |
|     $("#compose").on("click", ".collapse-composebox-button", (e) => {
 | |
|         e.preventDefault();
 | |
|         e.stopPropagation();
 | |
| 
 | |
|         compose_ui.make_compose_box_original_size();
 | |
|     });
 | |
| 
 | |
|     $("textarea#compose-textarea").on("focus", () => {
 | |
|         compose_recipient.update_placeholder_text();
 | |
|         if (narrow_state.narrowed_by_reply()) {
 | |
|             compose_notifications.maybe_show_one_time_non_interleaved_view_messages_fading_banner();
 | |
|         } else {
 | |
|             compose_notifications.maybe_show_one_time_interleaved_view_messages_fading_banner();
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     $(".compose-scrollable-buttons").on(
 | |
|         "scroll",
 | |
|         _.throttle((e) => {
 | |
|             compose_ui.handle_scrolling_formatting_buttons(e);
 | |
|         }, 150),
 | |
|     );
 | |
| 
 | |
|     $("#compose_recipient_box").on("click", "#recipient_box_clear_topic_button", () => {
 | |
|         const $input = $("input#stream_message_recipient_topic");
 | |
|         $input.val("");
 | |
|         $input.trigger("focus");
 | |
|     });
 | |
| 
 | |
|     $("input#stream_message_recipient_topic").on("focus", () => {
 | |
|         compose_recipient.update_placeholder_text();
 | |
|     });
 | |
| 
 | |
|     $("input#stream_message_recipient_topic").on("input", () => {
 | |
|         compose_recipient.update_placeholder_text();
 | |
|     });
 | |
| 
 | |
|     $("body").on("click", ".formatting_button", function (e) {
 | |
|         const $compose_click_target = $(this);
 | |
|         const $textarea = $compose_click_target.closest("form").find("textarea");
 | |
|         const format_type = $(this).attr("data-format-type");
 | |
|         compose_ui.format_text($textarea, format_type);
 | |
|         popovers.hide_all();
 | |
|         $textarea.trigger("focus");
 | |
|         e.preventDefault();
 | |
|         e.stopPropagation();
 | |
|     });
 | |
| 
 | |
|     if (page_params.narrow !== undefined) {
 | |
|         if (page_params.narrow_topic !== undefined) {
 | |
|             compose_actions.start({
 | |
|                 message_type: "stream",
 | |
|                 topic: page_params.narrow_topic,
 | |
|             });
 | |
|         } else {
 | |
|             compose_actions.start({message_type: "stream"});
 | |
|         }
 | |
|     }
 | |
| }
 |