compose_actions: Convert module to typescript.

This commit is contained in:
evykassirer 2024-04-03 18:48:12 -07:00 committed by Tim Abbott
parent 55cf08c4e6
commit d6f4424102
3 changed files with 83 additions and 32 deletions

View File

@ -66,7 +66,7 @@ EXEMPT_FILES = make_set(
"web/src/buddy_list.ts", "web/src/buddy_list.ts",
"web/src/click_handlers.js", "web/src/click_handlers.js",
"web/src/compose.js", "web/src/compose.js",
"web/src/compose_actions.js", "web/src/compose_actions.ts",
"web/src/compose_banner.ts", "web/src/compose_banner.ts",
"web/src/compose_call_ui.js", "web/src/compose_call_ui.js",
"web/src/compose_closed_ui.js", "web/src/compose_closed_ui.js",

View File

@ -10,9 +10,11 @@ import * as compose_pm_pill from "./compose_pm_pill";
import * as compose_recipient from "./compose_recipient"; import * as compose_recipient from "./compose_recipient";
import * as compose_state from "./compose_state"; import * as compose_state from "./compose_state";
import * as compose_ui from "./compose_ui"; import * as compose_ui from "./compose_ui";
import type {ComposeTriggeredOptions} from "./compose_ui";
import * as compose_validate from "./compose_validate"; import * as compose_validate from "./compose_validate";
import * as drafts from "./drafts"; import * as drafts from "./drafts";
import * as message_lists from "./message_lists"; import * as message_lists from "./message_lists";
import type {Message} from "./message_store";
import * as message_viewport from "./message_viewport"; import * as message_viewport from "./message_viewport";
import * as narrow_state from "./narrow_state"; import * as narrow_state from "./narrow_state";
import {page_params} from "./page_params"; import {page_params} from "./page_params";
@ -25,28 +27,53 @@ import * as spectators from "./spectators";
import {realm} from "./state_data"; import {realm} from "./state_data";
import * as stream_data from "./stream_data"; import * as stream_data from "./stream_data";
const compose_clear_box_hooks = []; // Opts sent to `compose_actions.start`.
const compose_cancel_hooks = []; type ComposeActionsStartOpts = {
message_type: "private" | "stream";
force_close?: boolean;
trigger?: string;
private_message_recipient?: string;
message?: Message;
stream_id?: number;
topic?: string;
content?: string;
draft_id?: string;
skip_scrolling_selected_message?: boolean;
is_reply?: boolean;
};
export function register_compose_box_clear_hook(hook) { // An iteration on `ComposeActionsStartOpts` that enforces that
// some values are present.
type ComposeActionsOpts = ComposeActionsStartOpts & {
topic: string;
private_message_recipient: string;
trigger: string;
};
type ComposeHook = () => void;
const compose_clear_box_hooks: ComposeHook[] = [];
const compose_cancel_hooks: ComposeHook[] = [];
export function register_compose_box_clear_hook(hook: ComposeHook): void {
compose_clear_box_hooks.push(hook); compose_clear_box_hooks.push(hook);
} }
export function register_compose_cancel_hook(hook) { export function register_compose_cancel_hook(hook: ComposeHook): void {
compose_cancel_hooks.push(hook); compose_cancel_hooks.push(hook);
} }
function call_hooks(hooks) { function call_hooks(hooks: ComposeHook[]): void {
for (const f of hooks) { for (const f of hooks) {
f(); f();
} }
} }
export function blur_compose_inputs() { export function blur_compose_inputs(): void {
$(".message_comp").find("input, textarea, button, #private_message_recipient").trigger("blur"); $(".message_comp").find("input, textarea, button, #private_message_recipient").trigger("blur");
} }
function hide_box() { function hide_box(): void {
// This is the main hook for saving drafts when closing the compose box. // This is the main hook for saving drafts when closing the compose box.
drafts.update_draft(); drafts.update_draft();
blur_compose_inputs(); blur_compose_inputs();
@ -58,19 +85,34 @@ function hide_box() {
$("#compose_controls").show(); $("#compose_controls").show();
} }
function show_compose_box(opts) { function show_compose_box(opts: ComposeActionsOpts): void {
compose_recipient.update_compose_for_message_type(opts); let opts_by_message_type: ComposeTriggeredOptions;
if (opts.message_type === "private") {
opts_by_message_type = {
trigger: opts.trigger,
message_type: "private",
private_message_recipient: opts.private_message_recipient,
};
} else {
opts_by_message_type = {
trigger: opts.trigger,
message_type: "stream",
stream_id: opts.stream_id,
topic: opts.topic,
};
}
compose_recipient.update_compose_for_message_type(opts_by_message_type);
$("#compose").css({visibility: "visible"}); $("#compose").css({visibility: "visible"});
// When changing this, edit the 42px in _maybe_autoscroll // When changing this, edit the 42px in _maybe_autoscroll
$(".new_message_textarea").css("min-height", "3em"); $(".new_message_textarea").css("min-height", "3em");
compose_ui.set_focus(opts); compose_ui.set_focus(opts_by_message_type);
} }
export function clear_textarea() { export function clear_textarea(): void {
$("#compose").find("input[type=text], textarea").val(""); $("#compose").find("input[type=text], textarea").val("");
} }
function clear_box() { function clear_box(): void {
call_hooks(compose_clear_box_hooks); call_hooks(compose_clear_box_hooks);
// TODO: Better encapsulate at-mention warnings. // TODO: Better encapsulate at-mention warnings.
@ -90,8 +132,8 @@ function clear_box() {
$(".compose_control_button_container:has(.add-poll)").removeClass("disabled-on-hover"); $(".compose_control_button_container:has(.add-poll)").removeClass("disabled-on-hover");
} }
let autosize_callback_opts; let autosize_callback_opts: ComposeActionsStartOpts;
export function autosize_message_content(opts) { export function autosize_message_content(opts: ComposeActionsStartOpts): void {
if (!compose_ui.is_full_size()) { if (!compose_ui.is_full_size()) {
autosize_callback_opts = opts; autosize_callback_opts = opts;
$("textarea#compose-textarea") $("textarea#compose-textarea")
@ -103,14 +145,14 @@ export function autosize_message_content(opts) {
} }
} }
export function expand_compose_box() { export function expand_compose_box(): void {
$("#compose_close").attr("data-tooltip-template-id", "compose_close_tooltip_template"); $("#compose_close").attr("data-tooltip-template-id", "compose_close_tooltip_template");
$("#compose_close").show(); $("#compose_close").show();
$("#compose_controls").hide(); $("#compose_controls").hide();
$(".message_comp").show(); $(".message_comp").show();
} }
export function complete_starting_tasks(opts) { export function complete_starting_tasks(opts: ComposeActionsOpts): void {
// This is sort of a kitchen sink function, and it's called only // This is sort of a kitchen sink function, and it's called only
// by compose.start() for now. Having this as a separate function // by compose.start() for now. Having this as a separate function
// makes testing a bit easier. // makes testing a bit easier.
@ -122,7 +164,7 @@ export function complete_starting_tasks(opts) {
compose_recipient.update_narrow_to_recipient_visibility(); compose_recipient.update_narrow_to_recipient_visibility();
} }
export function maybe_scroll_up_selected_message(opts) { export function maybe_scroll_up_selected_message(opts: ComposeActionsStartOpts): void {
if (opts.skip_scrolling_selected_message) { if (opts.skip_scrolling_selected_message) {
return; return;
} }
@ -140,7 +182,7 @@ export function maybe_scroll_up_selected_message(opts) {
} }
const $selected_row = message_lists.current.selected_row(); const $selected_row = message_lists.current.selected_row();
if ($selected_row.height() > message_viewport.height() - 100) { if ($selected_row.height()! > message_viewport.height() - 100) {
// For very tall messages whose height is close to the entire // For very tall messages whose height is close to the entire
// height of the viewport, don't auto-scroll the viewport to // height of the viewport, don't auto-scroll the viewport to
// the end of the message (since that makes it feel annoying // the end of the message (since that makes it feel annoying
@ -155,7 +197,9 @@ export function maybe_scroll_up_selected_message(opts) {
} }
} }
export function fill_in_opts_from_current_narrowed_view(opts) { export function fill_in_opts_from_current_narrowed_view(
opts: ComposeActionsStartOpts,
): ComposeActionsOpts {
return { return {
stream_id: undefined, stream_id: undefined,
topic: "", topic: "",
@ -171,7 +215,7 @@ export function fill_in_opts_from_current_narrowed_view(opts) {
}; };
} }
function same_recipient_as_before(opts) { function same_recipient_as_before(opts: ComposeActionsOpts): boolean {
return ( return (
compose_state.get_message_type() === opts.message_type && compose_state.get_message_type() === opts.message_type &&
((opts.message_type === "stream" && ((opts.message_type === "stream" &&
@ -182,23 +226,23 @@ function same_recipient_as_before(opts) {
); );
} }
export function start(opts) { export function start(raw_opts: ComposeActionsStartOpts): void {
if (page_params.is_spectator) { if (page_params.is_spectator) {
spectators.login_to_access(); spectators.login_to_access();
return; return;
} }
if (!opts.message_type) { if (!raw_opts.message_type) {
// We prefer callers to be explicit about the message type, but // We prefer callers to be explicit about the message type, but
// we if we don't know, we open a stream compose box by default, // we if we don't know, we open a stream compose box by default,
// which opens stream selection dropdown. // which opens stream selection dropdown.
// Also, message_type is used to check if compose box is open in compose_state.composing(). // Also, message_type is used to check if compose box is open in compose_state.composing().
opts.message_type = "stream"; raw_opts.message_type = "stream";
blueslip.warn("Empty message type in compose.start"); blueslip.warn("Empty message type in compose.start");
} }
popovers.hide_all(); popovers.hide_all();
autosize_message_content(opts); autosize_message_content(raw_opts);
if (reload_state.is_in_progress()) { if (reload_state.is_in_progress()) {
return; return;
@ -206,7 +250,7 @@ export function start(opts) {
compose_banner.clear_message_sent_banners(); compose_banner.clear_message_sent_banners();
expand_compose_box(); expand_compose_box();
opts = fill_in_opts_from_current_narrowed_view(opts); const opts = fill_in_opts_from_current_narrowed_view(raw_opts);
const is_clear_topic_button_triggered = opts.trigger === "clear topic button"; const is_clear_topic_button_triggered = opts.trigger === "clear topic button";
// If we are invoked by a compose hotkey (c or x) or new topic // If we are invoked by a compose hotkey (c or x) or new topic
@ -306,7 +350,7 @@ export function start(opts) {
complete_starting_tasks(opts); complete_starting_tasks(opts);
} }
export function cancel() { export function cancel(): void {
// As user closes the compose box, restore the compose box max height // As user closes the compose box, restore the compose box max height
if (compose_ui.is_full_size()) { if (compose_ui.is_full_size()) {
compose_ui.make_compose_box_original_size(); compose_ui.make_compose_box_original_size();
@ -335,7 +379,7 @@ export function cancel() {
$(document).trigger("compose_canceled.zulip"); $(document).trigger("compose_canceled.zulip");
} }
export function on_show_navigation_view() { export function on_show_navigation_view(): void {
/* This function dictates the behavior of the compose box /* This function dictates the behavior of the compose box
* when navigating to a view, as opposed to a narrow. */ * when navigating to a view, as opposed to a narrow. */
@ -353,7 +397,7 @@ export function on_show_navigation_view() {
cancel(); cancel();
} }
export function on_topic_narrow() { export function on_topic_narrow(): void {
if (!compose_state.composing()) { if (!compose_state.composing()) {
// If our compose box is closed, then just // If our compose box is closed, then just
// leave it closed, assuming that the user is // leave it closed, assuming that the user is
@ -403,7 +447,14 @@ export function on_topic_narrow() {
$("textarea#compose-textarea").trigger("focus"); $("textarea#compose-textarea").trigger("focus");
} }
export function on_narrow(opts) { // TODO/typescript: Fill this in when converting narrow.js to typescripot.
type NarrowActivateOpts = {
trigger?: string;
force_close?: boolean;
private_message_recipient?: string;
};
export function on_narrow(opts: NarrowActivateOpts): void {
// We use force_close when jumping between direct message narrows with // We use force_close when jumping between direct message narrows with
// the "p" key, so that we don't have an open compose box that makes // the "p" key, so that we don't have an open compose box that makes
// it difficult to cycle quickly through unread messages. // it difficult to cycle quickly through unread messages.
@ -449,7 +500,7 @@ export function on_narrow(opts) {
opts.private_message_recipient opts.private_message_recipient
) { ) {
const emails = opts.private_message_recipient.split(","); const emails = opts.private_message_recipient.split(",");
if (emails.length !== 1 || !people.get_by_email(emails[0]).is_bot) { if (emails.length !== 1 || !people.get_by_email(emails[0])!.is_bot) {
// If we are navigating between direct message conversations, // If we are navigating between direct message conversations,
// we want the compose box to close for non-bot users. // we want the compose box to close for non-bot users.
if (compose_state.composing()) { if (compose_state.composing()) {

View File

@ -32,7 +32,7 @@ export function get_recipient_label(message) {
}; };
} else if (narrow_state.pm_ids_string()) { } else if (narrow_state.pm_ids_string()) {
// TODO: This is a total hack. Ideally, we'd rework // TODO: This is a total hack. Ideally, we'd rework
// this to not duplicate the actual compose_actions.js // this to not duplicate the actual compose_actions.ts
// logic for what happens when you click the button, // logic for what happens when you click the button,
// and not call into random modules with hacky fake // and not call into random modules with hacky fake
// "message" objects. // "message" objects.