mirror of https://github.com/zulip/zulip.git
compose_actions: Convert module to typescript.
This commit is contained in:
parent
55cf08c4e6
commit
d6f4424102
|
@ -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",
|
||||||
|
|
|
@ -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()) {
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue