message_events: Convert module to typescript.

This commit is contained in:
evykassirer
2024-11-24 23:19:54 -08:00
committed by Tim Abbott
parent f241aa77c1
commit 7bd4016254
11 changed files with 116 additions and 64 deletions

View File

@@ -136,7 +136,7 @@ EXEMPT_FILES = make_set(
"web/src/message_actions_popover.ts",
"web/src/message_edit.ts",
"web/src/message_edit_history.ts",
"web/src/message_events.js",
"web/src/message_events.ts",
"web/src/message_events_util.ts",
"web/src/message_feed_loading.ts",
"web/src/message_feed_top_notices.ts",

View File

@@ -14,7 +14,7 @@ import * as compose_ui from "./compose_ui.ts";
import * as compose_validate from "./compose_validate.ts";
import * as drafts from "./drafts.ts";
import * as echo from "./echo.ts";
import * as message_events from "./message_events.js";
import * as message_events from "./message_events.ts";
import * as onboarding_steps from "./onboarding_steps.ts";
import * as people from "./people.ts";
import * as scheduled_messages from "./scheduled_messages.ts";

View File

@@ -274,8 +274,8 @@ export function confirm_delete_all_drafts(): void {
export function rename_stream_recipient(
old_stream_id: number,
old_topic: string,
new_stream_id: number,
new_topic: string,
new_stream_id: number | undefined,
new_topic: string | undefined,
): void {
for (const [draft_id, draft] of Object.entries(draft_model.get())) {
if (draft.type !== "stream" || draft.stream_id === undefined) {

View File

@@ -1,6 +1,7 @@
import $ from "jquery";
import _ from "lodash";
import assert from "minimalistic-assert";
import {z} from "zod";
import * as activity from "./activity.ts";
import * as alert_words from "./alert_words.ts";
@@ -13,6 +14,7 @@ import * as compose_validate from "./compose_validate.ts";
import * as direct_message_group_data from "./direct_message_group_data.ts";
import * as drafts from "./drafts.ts";
import * as echo from "./echo.ts";
import type {Filter} from "./filter.ts";
import * as message_edit from "./message_edit.ts";
import * as message_edit_history from "./message_edit_history.ts";
import * as message_events_util from "./message_events_util.ts";
@@ -22,6 +24,7 @@ import * as message_lists from "./message_lists.ts";
import * as message_notifications from "./message_notifications.ts";
import * as message_parser from "./message_parser.ts";
import * as message_store from "./message_store.ts";
import {type Message, type RawMessage, raw_message_schema} from "./message_store.ts";
import * as message_util from "./message_util.ts";
import * as message_view from "./message_view.ts";
import * as narrow_state from "./narrow_state.ts";
@@ -35,18 +38,19 @@ import {realm} from "./state_data.ts";
import * as stream_list from "./stream_list.ts";
import * as stream_topic_history from "./stream_topic_history.ts";
import * as sub_store from "./sub_store.ts";
import type {UpdateMessageEvent} from "./types.ts";
import * as unread from "./unread.ts";
import * as unread_ui from "./unread_ui.ts";
import * as util from "./util.ts";
function filter_has_term_type(filter, term_type) {
function filter_has_term_type(filter: Filter, term_type: string): boolean {
return (
filter.sorted_term_types().includes(term_type) ||
filter.sorted_term_types().includes(`not-${term_type}`)
);
}
export function discard_cached_lists_with_term_type(term_type) {
export function discard_cached_lists_with_term_type(term_type: string): void {
// Discards cached MessageList and MessageListData which have
// `term_type` and `not-term_type`.
assert(!term_type.includes("not-"));
@@ -74,7 +78,7 @@ export function discard_cached_lists_with_term_type(term_type) {
}
}
export function update_current_view_for_topic_visibility() {
export function update_current_view_for_topic_visibility(): boolean {
// If we have rendered message list / cached data based on topic
// visibility policy, we need to rerender it to reflect the changes. It
// is easier to just load the narrow from scratch, instead of asking server
@@ -84,8 +88,10 @@ export function update_current_view_for_topic_visibility() {
// Use `set_timeout to call after we update the topic
// visibility policy locally.
// Calling this outside `user_topics_ui` to avoid circular imports.
assert(message_lists.current !== undefined);
const msg_list_id = message_lists.current.id;
setTimeout(() => {
assert(message_lists.current !== undefined);
if (message_lists.current.id !== msg_list_id) {
// Check if the message list is still the same.
return;
@@ -103,10 +109,10 @@ export function update_current_view_for_topic_visibility() {
}
export let update_views_filtered_on_message_property = (
message_ids,
property_term_type,
property_value,
) => {
message_ids: number[],
property_term_type: string,
property_value: boolean,
): void => {
// NOTE: Call this function after updating the message property locally.
assert(!property_term_type.includes("not-"));
@@ -142,8 +148,8 @@ export let update_views_filtered_on_message_property = (
}
// We need the message objects to determine if they match the filter.
const messages_to_fetch = [];
const messages = [];
const messages_to_fetch: number[] = [];
const messages: Message[] = [];
for (const message_id of message_ids) {
const message = message_store.get(message_id);
if (message !== undefined) {
@@ -159,8 +165,12 @@ export let update_views_filtered_on_message_property = (
continue;
}
const first_id = msg_list.first().id;
const last_id = msg_list.last().id;
const first_message = msg_list.first();
assert(first_message !== undefined);
const first_id = first_message.id;
const last_message = msg_list.last();
assert(last_message !== undefined);
const last_id = last_message.id;
const has_found_newest = msg_list.data.fetch_status.has_found_newest();
const has_found_oldest = msg_list.data.fetch_status.has_found_oldest();
@@ -185,15 +195,16 @@ export let update_views_filtered_on_message_property = (
narrow: JSON.stringify(filter.terms()),
},
success(data) {
const messages_to_add = [];
const messages_to_add: Message[] = [];
const messages_to_remove = new Set(message_ids);
for (const raw_message of data.messages) {
for (const raw_message of z
.object({messages: z.array(raw_message_schema)})
.parse(data).messages) {
messages_to_remove.delete(raw_message.id);
let message = message_store.get(raw_message.id);
if (!message) {
message = message_helper.process_new_message(raw_message);
}
messages_to_add.push(message);
const message = message_store.get(raw_message.id);
messages_to_add.push(
message ?? message_helper.process_new_message(raw_message),
);
}
msg_list.data.remove([...messages_to_remove]);
msg_list.data.add_messages(messages_to_add);
@@ -213,10 +224,15 @@ export let update_views_filtered_on_message_property = (
},
// eslint-disable-next-line no-loop-func
success(data) {
const parsed_data = z
.object({
messages: z.array(raw_message_schema),
})
.parse(data);
// `messages_to_fetch` might already be cached locally when
// we reach here but `message_helper.process_new_message`
// already handles that case.
for (const raw_message of data.messages) {
for (const raw_message of parsed_data.messages) {
message_helper.process_new_message(raw_message);
}
update_views_filtered_on_message_property(
@@ -245,7 +261,7 @@ export let update_views_filtered_on_message_property = (
// In most cases, we are only working to update a single message.
if (messages.length === 1) {
const message = messages[0];
const message = messages[0]!;
if (filter.predicate()(message)) {
msg_list.add_messages(messages);
} else {
@@ -260,13 +276,19 @@ export let update_views_filtered_on_message_property = (
}
};
export function rewire_update_views_filtered_on_message_property(value) {
export function rewire_update_views_filtered_on_message_property(
value: typeof update_views_filtered_on_message_property,
): void {
update_views_filtered_on_message_property = value;
}
export function insert_new_messages(messages, sent_by_this_client, deliver_locally) {
messages = messages.map((message) =>
message_helper.process_new_message(message, deliver_locally),
export function insert_new_messages(
raw_messages: RawMessage[],
sent_by_this_client: boolean,
deliver_locally: boolean,
): Message[] {
const messages = raw_messages.map((raw_message) =>
message_helper.process_new_message(raw_message, deliver_locally),
);
const any_untracked_unread_messages = unread.process_loaded_messages(messages, false);
@@ -308,7 +330,7 @@ export function insert_new_messages(messages, sent_by_this_client, deliver_local
// this message list is the currently visible message list.
const is_currently_visible =
narrow_state.is_message_feed_visible() && list === message_lists.current;
if (is_currently_visible && render_info && render_info.need_user_to_scroll) {
if (is_currently_visible && render_info?.need_user_to_scroll) {
need_user_to_scroll = true;
}
}
@@ -342,7 +364,9 @@ export function insert_new_messages(messages, sent_by_this_client, deliver_local
// tracking before we update the stream sidebar, to take advantage
// of how stream_topic_history uses the echo data structures.
if (deliver_locally) {
messages.map((message) => echo.track_local_message(message));
for (const message of messages) {
echo.track_local_message(message);
}
}
activity.set_received_new_messages(true);
@@ -353,8 +377,8 @@ export function insert_new_messages(messages, sent_by_this_client, deliver_local
return messages;
}
export function update_messages(events) {
const messages_to_rerender = [];
export function update_messages(events: UpdateMessageEvent[]): void {
const messages_to_rerender: Message[] = [];
let changed_narrow = false;
let refreshed_current_narrow = false;
let changed_compose = false;
@@ -426,6 +450,7 @@ export function update_messages(events) {
}
if (unread.update_message_for_mention(anchor_message, any_message_content_edited)) {
assert(anchor_message.type === "stream");
const topic_key = recent_view_util.get_topic_key(
anchor_message.stream_id,
anchor_message.topic,
@@ -442,7 +467,8 @@ export function update_messages(events) {
const old_stream_id = event.stream_id;
// old_stream will be undefined if the message was moved from
// a stream that the current user doesn't have access to.
const old_stream = sub_store.get(event.stream_id);
const old_stream =
event.stream_id === undefined ? undefined : sub_store.get(event.stream_id);
// A topic or stream edit may affect multiple messages, listed in
// event.message_ids. event.message_id is still the first message
@@ -461,24 +487,29 @@ export function update_messages(events) {
messages_to_rerender.push(anchor_message);
}
} else {
const going_forward_change = ["change_later", "change_all"].includes(
event.propagate_mode,
);
// We must be moving stream messages.
assert(old_stream_id !== undefined);
const orig_topic = util.get_edit_event_orig_topic(event);
assert(orig_topic !== undefined);
const going_forward_change =
event.propagate_mode !== undefined &&
["change_later", "change_all"].includes(event.propagate_mode);
const compose_stream_id = compose_state.stream_id();
const orig_topic = util.get_edit_event_orig_topic(event);
const current_filter = narrow_state.filter();
const current_selected_id = message_lists.current?.selected_id();
const selection_changed_topic =
message_lists.current !== undefined &&
current_selected_id !== undefined &&
event.message_ids.includes(current_selected_id);
const event_messages = [];
const event_messages: (Message & {type: "stream"})[] = [];
for (const message_id of event.message_ids) {
// We don't need to concern ourselves updating data structures
// for messages we don't have stored locally.
const message = message_store.get(message_id);
if (message !== undefined) {
assert(message.type === "stream");
event_messages.push(message);
} else {
// If we don't have the message locally, we need to
@@ -520,7 +551,14 @@ export function update_messages(events) {
* history events. This logic ensures that all
* messages that were moved are displayed as such
* without a browser reload. */
const edit_history_entry = {
const edit_history_entry: {
user_id: number | null;
timestamp: number;
stream?: number;
prev_stream?: number;
topic?: string;
prev_topic?: string;
} = {
user_id: event.user_id,
timestamp: event.edit_timestamp,
};
@@ -549,10 +587,13 @@ export function update_messages(events) {
// Now edit the attributes of our message object.
if (topic_edited) {
moved_message.topic = new_topic;
assert(event.topic_links !== undefined);
moved_message.topic_links = event.topic_links;
}
if (stream_changed) {
const new_stream_name = sub_store.get(new_stream_id).name;
const new_stream = sub_store.get(new_stream_id);
assert(new_stream !== undefined);
const new_stream_name = new_stream.name;
moved_message.stream_id = new_stream_id;
moved_message.display_recipient = new_stream_name;
}
@@ -575,7 +616,7 @@ export function update_messages(events) {
stream_id: old_stream_id,
topic_name: orig_topic,
num_messages,
max_removed_msg_id: event_messages[num_messages - 1].id,
max_removed_msg_id: event_messages[num_messages - 1]!.id,
});
}
@@ -595,9 +636,7 @@ export function update_messages(events) {
// Code further down takes care of the actual rerendering of
// messages within a narrow.
selection_changed_topic &&
current_filter &&
old_stream_id &&
current_filter.has_topic(old_stream_id, orig_topic)
current_filter?.has_topic(old_stream_id, orig_topic)
) {
let new_filter = current_filter;
if (new_filter && stream_changed) {
@@ -647,7 +686,9 @@ export function update_messages(events) {
let moved_message_stream_id_str = old_stream_id.toString();
let moved_message_topic = orig_topic;
if (stream_changed) {
moved_message_stream_id_str = sub_store.get(new_stream_id).stream_id.toString();
const new_stream = sub_store.get(new_stream_id);
assert(new_stream !== undefined);
moved_message_stream_id_str = new_stream.stream_id.toString();
}
if (topic_edited) {
@@ -721,21 +762,26 @@ export function update_messages(events) {
}
if (topic_edited || stream_changed) {
// if topic is changed
// We must be moving stream messages.
assert(old_stream_id !== undefined);
let pre_edit_topic = util.get_edit_event_orig_topic(event);
let post_edit_topic = new_topic;
assert(pre_edit_topic !== undefined);
if (!topic_edited) {
let post_edit_topic: string;
if (topic_edited) {
assert(new_topic !== undefined);
post_edit_topic = new_topic;
} else {
if (anchor_message !== undefined) {
assert(anchor_message.type === "stream");
pre_edit_topic = anchor_message.topic;
}
post_edit_topic = pre_edit_topic;
}
// new_stream_id is undefined if this is only a topic edit.
const post_edit_stream_id = new_stream_id || old_stream_id;
const post_edit_stream_id = new_stream_id ?? old_stream_id;
const args = [old_stream_id, pre_edit_topic, post_edit_topic, post_edit_stream_id];
recent_senders.process_topic_edit({
message_ids: event.message_ids,
old_stream_id,
@@ -744,14 +790,20 @@ export function update_messages(events) {
new_topic: post_edit_topic,
});
unread.clear_and_populate_unread_mention_topics();
recent_view_ui.process_topic_edit(...args);
recent_view_ui.process_topic_edit(
old_stream_id,
pre_edit_topic,
post_edit_topic,
post_edit_stream_id,
);
}
// Rerender "Message edit history" if it was open to the edited message.
if (
anchor_message !== undefined &&
$("#message-edit-history").parents(".micromodal").hasClass("modal--open") &&
anchor_message.id === Number.parseInt($("#message-history").attr("data-message-id"), 10)
anchor_message.id ===
Number.parseInt($("#message-history").attr("data-message-id")!, 10)
) {
message_edit_history.fetch_and_render_message_history(anchor_message);
}
@@ -814,7 +866,7 @@ export function update_messages(events) {
pm_list.update_private_messages();
}
export function remove_messages(message_ids) {
export function remove_messages(message_ids: number[]): void {
// Update the rendered data first since it is most user visible.
for (const list of message_lists.all_rendered_message_lists()) {
list.remove_and_rerender(message_ids);

View File

@@ -192,7 +192,7 @@ export class MessageList {
add_messages(
messages: Message[],
append_to_view_opts: {messages_are_new?: boolean},
append_to_view_opts: {messages_are_new?: boolean} = {},
): RenderInfo | undefined {
// This adds all messages to our data, but only returns
// the currently viewable ones.
@@ -608,7 +608,7 @@ export class MessageList {
// We could avoid a rerender if we can provide that this
// narrow cannot have contained messages to muted topics
// either before or after the state change. The right place
// to do this is in the message_events.js code path for
// to do this is in the message_events.ts code path for
// processing topic edits, since that's the only place we'll
// call this frequently anyway.
//

View File

@@ -153,7 +153,7 @@ export type Message = (
// The original markup for the message, which we'll have if we
// sent it or if we fetched it (usually, because the current user
// tried to edit the message).
raw_content?: string;
raw_content?: string | undefined;
// Added in `message_helper.process_new_message`.
sent_by_me: boolean;

View File

@@ -29,7 +29,7 @@ export function do_unread_count_updates(messages: Message[], expect_no_new_unrea
export function add_messages(
messages: Message[],
msg_list: MessageList,
append_to_view_opts: {messages_are_new: boolean},
append_to_view_opts?: {messages_are_new: boolean},
): RenderInfo | undefined {
if (!messages) {
return undefined;

View File

@@ -340,13 +340,13 @@ export function try_rendering_locally_for_same_narrow(
return true;
}
type ShowMessageViewOpts = {
export type ShowMessageViewOpts = {
force_rerender?: boolean;
force_close?: boolean;
change_hash?: boolean;
trigger?: string;
fetched_target_message?: boolean;
then_select_id?: number;
then_select_id?: number | undefined;
then_select_offset?: number | undefined;
show_more_topics?: boolean;
};
@@ -440,11 +440,11 @@ export let show = (raw_terms: NarrowTerm[], show_opts: ShowMessageViewOpts): voi
const coming_from_inbox = inbox_util.is_visible();
const opts = {
then_select_id: -1,
change_hash: true,
trigger: "unknown",
show_more_topics: false,
...show_opts,
then_select_id: show_opts.then_select_id ?? -1,
};
const span_data = {

View File

@@ -5,7 +5,7 @@ import * as blueslip from "./blueslip.ts";
import * as channel from "./channel.ts";
import * as echo from "./echo.ts";
import * as loading from "./loading.ts";
import * as message_events from "./message_events.js";
import * as message_events from "./message_events.ts";
import {page_params} from "./page_params.ts";
import * as reload from "./reload.ts";
import * as reload_state from "./reload_state.ts";

View File

@@ -25,7 +25,7 @@ import * as information_density from "./information_density.ts";
import * as left_sidebar_navigation_area from "./left_sidebar_navigation_area.ts";
import * as linkifiers from "./linkifiers.ts";
import * as message_edit from "./message_edit.ts";
import * as message_events from "./message_events.js";
import * as message_events from "./message_events.ts";
import * as message_lists from "./message_lists.ts";
import * as message_live_update from "./message_live_update.ts";
import * as message_view from "./message_view.ts";

View File

@@ -803,7 +803,7 @@ export function process_unread_message(message: UnreadMessageData): void {
}
export function update_message_for_mention(
message: UnreadMessageData,
message: UnreadMessageData | Message,
content_edited = false,
): boolean {
// Returns true if this is a stream message whose content was