message_list: Convert module to typescript.

This commit is contained in:
evykassirer 2024-09-12 10:52:47 -07:00 committed by Tim Abbott
parent 620db3057b
commit deb5d90941
11 changed files with 129 additions and 161 deletions

View File

@ -143,7 +143,7 @@ EXEMPT_FILES = make_set(
"web/src/message_feed_loading.ts", "web/src/message_feed_loading.ts",
"web/src/message_feed_top_notices.ts", "web/src/message_feed_top_notices.ts",
"web/src/message_fetch.ts", "web/src/message_fetch.ts",
"web/src/message_list.js", "web/src/message_list.ts",
"web/src/message_list_data.ts", "web/src/message_list_data.ts",
"web/src/message_list_data_cache.ts", "web/src/message_list_data_cache.ts",
"web/src/message_list_hover.js", "web/src/message_list_hover.js",

View File

@ -161,6 +161,7 @@ export function reply_with_mention(opts: {
keep_composebox_empty: true, keep_composebox_empty: true,
}); });
const message = message_lists.current.selected_message(); const message = message_lists.current.selected_message();
assert(message !== undefined);
const mention = people.get_mention_syntax(message.sender_full_name, message.sender_id); const mention = people.get_mention_syntax(message.sender_full_name, message.sender_id);
compose_ui.insert_syntax_and_focus(mention); compose_ui.insert_syntax_and_focus(mention);
} }

View File

@ -3,8 +3,8 @@ import {z} from "zod";
import * as blueslip from "./blueslip"; import * as blueslip from "./blueslip";
import * as channel from "./channel"; import * as channel from "./channel";
import * as compose_notifications from "./compose_notifications"; import * as compose_notifications from "./compose_notifications";
import type {MessageList, RenderInfo} from "./message_list";
import * as message_lists from "./message_lists"; import * as message_lists from "./message_lists";
import type {MessageList, RenderInfo} from "./message_lists";
import * as message_store from "./message_store"; import * as message_store from "./message_store";
import type {Message} from "./message_store"; import type {Message} from "./message_store";
import * as narrow_state from "./narrow_state"; import * as narrow_state from "./narrow_state";

View File

@ -3,8 +3,8 @@ import _ from "lodash";
import assert from "minimalistic-assert"; import assert from "minimalistic-assert";
import * as hash_util from "./hash_util"; import * as hash_util from "./hash_util";
import type {MessageList} from "./message_list";
import * as message_lists from "./message_lists"; import * as message_lists from "./message_lists";
import type {MessageList} from "./message_lists";
import * as narrow_banner from "./narrow_banner"; import * as narrow_banner from "./narrow_banner";
import * as narrow_state from "./narrow_state"; import * as narrow_state from "./narrow_state";
import * as people from "./people"; import * as people from "./people";

View File

@ -11,6 +11,7 @@ import * as direct_message_group_data from "./direct_message_group_data";
import * as message_feed_loading from "./message_feed_loading"; import * as message_feed_loading from "./message_feed_loading";
import * as message_feed_top_notices from "./message_feed_top_notices"; import * as message_feed_top_notices from "./message_feed_top_notices";
import * as message_helper from "./message_helper"; import * as message_helper from "./message_helper";
import type {MessageList} from "./message_list";
import type {MessageListData} from "./message_list_data"; import type {MessageListData} from "./message_list_data";
import * as message_lists from "./message_lists"; import * as message_lists from "./message_lists";
import {raw_message_schema} from "./message_store"; import {raw_message_schema} from "./message_store";
@ -44,7 +45,7 @@ type MessageFetchOptions = {
cont: (data: MessageFetchResponse, args: MessageFetchOptions) => void; cont: (data: MessageFetchResponse, args: MessageFetchOptions) => void;
fetch_again?: boolean; fetch_again?: boolean;
msg_list_data: MessageListData; msg_list_data: MessageListData;
msg_list?: message_lists.MessageList | undefined; msg_list?: MessageList | undefined;
validate_filter_topic_post_fetch?: boolean | undefined; validate_filter_topic_post_fetch?: boolean | undefined;
}; };
@ -496,7 +497,7 @@ export function load_messages(opts: MessageFetchOptions, attempt = 1): void {
export function load_messages_for_narrow(opts: { export function load_messages_for_narrow(opts: {
anchor: string | number; anchor: string | number;
msg_list: message_lists.MessageList; msg_list: MessageList;
cont: () => void; cont: () => void;
validate_filter_topic_post_fetch?: boolean | undefined; validate_filter_topic_post_fetch?: boolean | undefined;
}): void { }): void {
@ -520,7 +521,7 @@ export function get_backfill_anchor(msg_list_data: MessageListData): string | nu
return "first_unread"; return "first_unread";
} }
export function get_frontfill_anchor(msg_list: message_lists.MessageList): number | string { export function get_frontfill_anchor(msg_list: MessageList): number | string {
const last_msg = msg_list.data.last_including_muted(); const last_msg = msg_list.data.last_including_muted();
if (last_msg) { if (last_msg) {
@ -557,7 +558,7 @@ export function maybe_load_older_messages(opts: {
recent_view?: boolean; recent_view?: boolean;
first_unread_message_id?: number; first_unread_message_id?: number;
cont?: () => void; cont?: () => void;
msg_list?: message_lists.MessageList | undefined; msg_list?: MessageList | undefined;
msg_list_data: MessageListData; msg_list_data: MessageListData;
}): void { }): void {
// This function gets called when you scroll to the top // This function gets called when you scroll to the top
@ -626,7 +627,7 @@ export function do_backfill(opts: {
num_before: number; num_before: number;
cont?: () => void; cont?: () => void;
msg_list_data: MessageListData; msg_list_data: MessageListData;
msg_list?: message_lists.MessageList | undefined; msg_list?: MessageList | undefined;
}): void { }): void {
const msg_list_data = opts.msg_list_data; const msg_list_data = opts.msg_list_data;
const anchor = get_backfill_anchor(msg_list_data); const anchor = get_backfill_anchor(msg_list_data);
@ -648,7 +649,7 @@ export function do_backfill(opts: {
}); });
} }
export function maybe_load_newer_messages(opts: {msg_list: message_lists.MessageList}): void { export function maybe_load_newer_messages(opts: {msg_list: MessageList}): void {
// This function gets called when you scroll to the bottom // This function gets called when you scroll to the bottom
// of your window, and you want to get messages newer // of your window, and you want to get messages newer
// than what the browsers originally fetched. // than what the browsers originally fetched.

View File

@ -4,9 +4,11 @@ import assert from "minimalistic-assert";
import * as blueslip from "./blueslip"; import * as blueslip from "./blueslip";
import * as compose_tooltips from "./compose_tooltips"; import * as compose_tooltips from "./compose_tooltips";
import type {Filter} from "./filter";
import {MessageListData} from "./message_list_data"; import {MessageListData} from "./message_list_data";
import * as message_list_tooltips from "./message_list_tooltips"; import * as message_list_tooltips from "./message_list_tooltips";
import {MessageListView} from "./message_list_view"; import {MessageListView} from "./message_list_view";
import type {Message} from "./message_store";
import * as narrow_banner from "./narrow_banner"; import * as narrow_banner from "./narrow_banner";
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";
@ -15,8 +17,19 @@ import * as stream_data from "./stream_data";
import * as unread from "./unread"; import * as unread from "./unread";
import {user_settings} from "./user_settings"; import {user_settings} from "./user_settings";
export class MessageList { export type RenderInfo = {need_user_to_scroll: boolean};
static id_counter = 0;
type SelectIdOpts = {
then_scroll?: boolean;
target_scroll_offset?: number;
use_closest?: boolean;
empty_ok?: boolean;
mark_read?: boolean;
force_rerender?: boolean;
from_scroll?: boolean;
from_rendering?: boolean;
};
// A MessageList is the main interface for a message feed that is // A MessageList is the main interface for a message feed that is
// rendered in the DOM. Code outside the message feed rendering // rendered in the DOM. Code outside the message feed rendering
// internals will directly call this module in order to manipulate // internals will directly call this module in order to manipulate
@ -29,7 +42,32 @@ export class MessageList {
// TODO: The abstraction boundary between this and MessageListView // TODO: The abstraction boundary between this and MessageListView
// is not particularly well-defined; it could be nice to figure // is not particularly well-defined; it could be nice to figure
// out a good rule. // out a good rule.
constructor(opts) { export class MessageList {
static id_counter = 0;
id: number;
data: MessageListData;
// The MessageListView object that is responsible for
// maintaining this message feed's HTML representation in the
// DOM.
view: MessageListView;
// If this message list is for the combined feed view.
is_combined_feed_view: boolean;
// Keeps track of whether the user has done a UI interaction,
// such as "Mark as unread", that should disable marking
// messages as read until prevent_reading is called again.
//
// Distinct from filter.can_mark_messages_read(), which is a
// property of the type of narrow, regardless of actions by
// the user. Possibly this can be unified in some nice way.
reading_prevented: boolean;
last_message_historical?: boolean;
constructor(opts: {
data: MessageListData;
filter: Filter;
excludes_muted_topics: boolean;
is_node_test: boolean;
}) {
MessageList.id_counter += 1; MessageList.id_counter += 1;
this.id = MessageList.id_counter; this.id = MessageList.id_counter;
// The MessageListData keeps track of the actual sequence of // The MessageListData keeps track of the actual sequence of
@ -55,27 +93,14 @@ export class MessageList {
// query .data.filter directly. // query .data.filter directly.
const collapse_messages = this.data.filter.supports_collapsing_recipients(); const collapse_messages = this.data.filter.supports_collapsing_recipients();
// The MessageListView object that is responsible for
// maintaining this message feed's HTML representation in the
// DOM.
this.view = new MessageListView(this, collapse_messages, opts.is_node_test); this.view = new MessageListView(this, collapse_messages, opts.is_node_test);
// If this message list is for the combined feed view.
this.is_combined_feed_view = this.data.filter.is_in_home(); this.is_combined_feed_view = this.data.filter.is_in_home();
// Keeps track of whether the user has done a UI interaction,
// such as "Mark as unread", that should disable marking
// messages as read until prevent_reading is called again.
//
// Distinct from filter.can_mark_messages_read(), which is a
// property of the type of narrow, regardless of actions by
// the user. Possibly this can be unified in some nice way.
this.reading_prevented = false; this.reading_prevented = false;
return this; return this;
} }
should_preserve_current_rendered_state() { should_preserve_current_rendered_state(): boolean {
// Whether this message list is preserved in the DOM even // Whether this message list is preserved in the DOM even
// when viewing other views -- a valuable optimization for // when viewing other views -- a valuable optimization for
// fast toggling between the combined feed and other views, // fast toggling between the combined feed and other views,
@ -125,19 +150,22 @@ export class MessageList {
return true; return true;
} }
is_current_message_list() { is_current_message_list(): boolean {
return this.view.is_current_message_list(); return this.view.is_current_message_list();
} }
prevent_reading() { prevent_reading(): void {
this.reading_prevented = true; this.reading_prevented = true;
} }
resume_reading() { resume_reading(): void {
this.reading_prevented = false; this.reading_prevented = false;
} }
add_messages(messages, opts) { add_messages(
messages: Message[],
append_to_view_opts: {messages_are_new?: boolean},
): RenderInfo | undefined {
// This adds all messages to our data, but only returns // This adds all messages to our data, but only returns
// the currently viewable ones. // the currently viewable ones.
const info = this.data.add_messages(messages); const info = this.data.add_messages(messages);
@ -161,7 +189,7 @@ export class MessageList {
} }
if (bottom_messages.length > 0) { if (bottom_messages.length > 0) {
render_info = this.append_to_view(bottom_messages, opts); render_info = this.append_to_view(bottom_messages, append_to_view_opts);
} }
if (!this.visibly_empty() && this.is_current_message_list()) { if (!this.visibly_empty() && this.is_current_message_list()) {
@ -185,51 +213,51 @@ export class MessageList {
return render_info; return render_info;
} }
get(id) { get(id: number): Message | undefined {
return this.data.get(id); return this.data.get(id);
} }
msg_id_in_fetched_range(msg_id) { msg_id_in_fetched_range(msg_id: number): boolean {
return this.data.msg_id_in_fetched_range(msg_id); return this.data.msg_id_in_fetched_range(msg_id);
} }
num_items() { num_items(): number {
return this.data.num_items(); return this.data.num_items();
} }
empty() { empty(): boolean {
return this.data.empty(); return this.data.empty();
} }
visibly_empty() { visibly_empty(): boolean {
return this.data.visibly_empty(); return this.data.visibly_empty();
} }
first() { first(): Message | undefined {
return this.data.first(); return this.data.first();
} }
last() { last(): Message | undefined {
return this.data.last(); return this.data.last();
} }
prev() { prev(): number | undefined {
return this.data.prev(); return this.data.prev();
} }
next() { next(): number | undefined {
return this.data.next(); return this.data.next();
} }
is_at_end() { is_at_end(): boolean {
return this.data.is_at_end(); return this.data.is_at_end();
} }
is_keyword_search() { is_keyword_search(): boolean {
return this.data.is_keyword_search(); return this.data.is_keyword_search();
} }
can_mark_messages_read() { can_mark_messages_read(): boolean {
/* Automatically marking messages as read can be disabled for /* Automatically marking messages as read can be disabled for
three different reasons: three different reasons:
* The view is structurally a search view, encoded in the * The view is structurally a search view, encoded in the
@ -256,7 +284,7 @@ export class MessageList {
); );
} }
can_mark_messages_read_without_setting() { can_mark_messages_read_without_setting(): boolean {
/* /*
Similar to can_mark_messages_read() above, this is a helper Similar to can_mark_messages_read() above, this is a helper
function to check if messages can be automatically read without function to check if messages can be automatically read without
@ -265,7 +293,7 @@ export class MessageList {
return this.data.can_mark_messages_read() && !this.reading_prevented; return this.data.can_mark_messages_read() && !this.reading_prevented;
} }
clear({clear_selected_id = true} = {}) { clear({clear_selected_id = true} = {}): void {
this.data.clear(); this.data.clear();
this.view.clear_rendering_state(true); this.view.clear_rendering_state(true);
@ -274,34 +302,31 @@ export class MessageList {
} }
} }
selected_id() { selected_id(): number {
return this.data.selected_id(); return this.data.selected_id();
} }
select_id(id, opts) { select_id(id: number | string, select_id_opts?: SelectIdOpts): void {
opts = { if (typeof id === "string") {
blueslip.warn("Call to select_id with string id");
id = Number.parseFloat(id);
if (Number.isNaN(id)) {
throw new TypeError("Bad message id " + id);
}
}
const opts = {
then_scroll: false, then_scroll: false,
target_scroll_offset: undefined, target_scroll_offset: undefined,
use_closest: false, use_closest: false,
empty_ok: false, empty_ok: false,
mark_read: true, mark_read: true,
force_rerender: false, force_rerender: false,
...opts, ...select_id_opts,
id, id,
msg_list: this, msg_list: this,
previously_selected_id: this.data.selected_id(), previously_selected_id: this.data.selected_id(),
}; };
const convert_id = (str_id) => {
const id = Number.parseFloat(str_id);
if (Number.isNaN(id)) {
throw new TypeError("Bad message id " + str_id);
}
return id;
};
id = convert_id(id);
const closest_id = this.closest_id(id); const closest_id = this.closest_id(id);
let error_data; let error_data;
@ -353,30 +378,30 @@ export class MessageList {
} }
} }
selected_message() { selected_message(): Message | undefined {
return this.get(this.data.selected_id()); return this.get(this.data.selected_id());
} }
selected_row() { selected_row(): JQuery {
return this.get_row(this.data.selected_id()); return this.get_row(this.data.selected_id());
} }
closest_id(id) { closest_id(id: number): number {
return this.data.closest_id(id); return this.data.closest_id(id);
} }
advance_past_messages(msg_ids) { advance_past_messages(msg_ids: number[]): void {
return this.data.advance_past_messages(msg_ids); this.data.advance_past_messages(msg_ids);
} }
selected_idx() { selected_idx(): number {
return this.data.selected_idx(); return this.data.selected_idx();
} }
// Maintains a trailing bookend element explaining any changes in // Maintains a trailing bookend element explaining any changes in
// your subscribed/unsubscribed status at the bottom of the // your subscribed/unsubscribed status at the bottom of the
// message list. // message list.
update_trailing_bookend(force_render = false) { update_trailing_bookend(force_render = false): void {
this.view.clear_trailing_bookend(); this.view.clear_trailing_bookend();
if (this.is_combined_feed_view) { if (this.is_combined_feed_view) {
return; return;
@ -415,7 +440,7 @@ export class MessageList {
} }
this.view.render_trailing_bookend( this.view.render_trailing_bookend(
sub.name, sub?.name,
subscribed, subscribed,
deactivated, deactivated,
just_unsubscribed, just_unsubscribed,
@ -426,26 +451,25 @@ export class MessageList {
); );
} }
unmuted_messages(messages) { unmuted_messages(messages: Message[]): Message[] {
return this.data.unmuted_messages(messages); return this.data.unmuted_messages(messages);
} }
append(messages, opts) { append(messages: Message[], opts: {messages_are_new: boolean}): void {
const viewable_messages = this.data.append(messages); const viewable_messages = this.data.append(messages);
this.append_to_view(viewable_messages, opts); this.append_to_view(viewable_messages, opts);
} }
append_to_view(messages, {messages_are_new = false} = {}) { append_to_view(messages: Message[], {messages_are_new = false} = {}): RenderInfo | undefined {
const render_info = this.view.append(messages, messages_are_new); return this.view.append(messages, messages_are_new);
return render_info;
} }
remove_and_rerender(message_ids) { remove_and_rerender(message_ids: number[]): void {
this.data.remove(message_ids); this.data.remove(message_ids);
this.rerender(); this.rerender();
} }
show_edit_message($row, $form) { show_edit_message($row: JQuery, $form: JQuery): void {
if ($row.find(".message_edit_form form").length !== 0) { if ($row.find(".message_edit_form form").length !== 0) {
return; return;
} }
@ -457,7 +481,7 @@ export class MessageList {
autosize($row.find(".message_edit_content")); autosize($row.find(".message_edit_content"));
} }
hide_edit_message($row) { hide_edit_message($row: JQuery): void {
if ($row.find(".message_edit_form form").length === 0) { if ($row.find(".message_edit_form form").length === 0) {
return; return;
} }
@ -468,7 +492,7 @@ export class MessageList {
$row.trigger("mouseleave"); $row.trigger("mouseleave");
} }
show_edit_topic_on_recipient_row($recipient_row, $form) { show_edit_topic_on_recipient_row($recipient_row: JQuery, $form: JQuery): void {
$recipient_row.find(".topic_edit_form").append($form); $recipient_row.find(".topic_edit_form").append($form);
$recipient_row.find(".on_hover_topic_edit").hide(); $recipient_row.find(".on_hover_topic_edit").hide();
$recipient_row.find(".edit_message_button").hide(); $recipient_row.find(".edit_message_button").hide();
@ -479,7 +503,7 @@ export class MessageList {
$recipient_row.find(".on_hover_topic_unresolve").hide(); $recipient_row.find(".on_hover_topic_unresolve").hide();
} }
hide_edit_topic_on_recipient_row($recipient_row) { hide_edit_topic_on_recipient_row($recipient_row: JQuery): void {
$recipient_row.find(".stream_topic").show(); $recipient_row.find(".stream_topic").show();
$recipient_row.find(".on_hover_topic_edit").show(); $recipient_row.find(".on_hover_topic_edit").show();
$recipient_row.find(".edit_message_button").show(); $recipient_row.find(".edit_message_button").show();
@ -490,7 +514,7 @@ export class MessageList {
$recipient_row.find(".on_hover_topic_unresolve").show(); $recipient_row.find(".on_hover_topic_unresolve").show();
} }
reselect_selected_id() { reselect_selected_id(): void {
const selected_id = this.data.selected_id(); const selected_id = this.data.selected_id();
if (selected_id !== -1) { if (selected_id !== -1) {
@ -498,12 +522,12 @@ export class MessageList {
} }
} }
rerender_view() { rerender_view(): void {
this.view.rerender_preserving_scrolltop(); this.view.rerender_preserving_scrolltop();
this.reselect_selected_id(); this.reselect_selected_id();
} }
rerender() { rerender(): void {
// We need to destroy all the tippy instances from the DOM before re-rendering to // We need to destroy all the tippy instances from the DOM before re-rendering to
// prevent the appearance of tooltips whose reference has been removed. // prevent the appearance of tooltips whose reference has been removed.
message_list_tooltips.destroy_all_message_list_tooltips(); message_list_tooltips.destroy_all_message_list_tooltips();
@ -531,7 +555,7 @@ export class MessageList {
this.rerender_view(); this.rerender_view();
} }
update_muting_and_rerender() { update_muting_and_rerender(): void {
this.data.update_items_for_muting(); this.data.update_items_for_muting();
// We need to rerender whether or not the narrow hides muted // We need to rerender whether or not the narrow hides muted
// topics, because we need to update recipient bars for topics // topics, because we need to update recipient bars for topics
@ -549,34 +573,34 @@ export class MessageList {
this.rerender(); this.rerender();
} }
all_messages() { all_messages(): Message[] {
return this.data.all_messages(); return this.data.all_messages();
} }
first_unread_message_id() { first_unread_message_id(): number | undefined {
return this.data.first_unread_message_id(); return this.data.first_unread_message_id();
} }
has_unread_messages() { has_unread_messages(): boolean {
return this.data.has_unread_messages(); return this.data.has_unread_messages();
} }
message_range(start, end) { message_range(start: number, end: number): Message[] {
return this.data.message_range(start, end); return this.data.message_range(start, end);
} }
get_row(id) { get_row(id: number): JQuery {
return this.view.get_row(id); return this.view.get_row(id);
} }
change_message_id(old_id, new_id) { change_message_id(old_id: number, new_id: number): void {
const require_rerender = this.data.change_message_id(old_id, new_id); const require_rerender = this.data.change_message_id(old_id, new_id);
if (require_rerender) { if (require_rerender) {
this.rerender_view(); this.rerender_view();
} }
} }
get_last_message_sent_by_me() { get_last_message_sent_by_me(): Message | undefined {
return this.data.get_last_message_sent_by_me(); return this.data.get_last_message_sent_by_me();
} }
} }

View File

@ -19,9 +19,9 @@ import * as condense from "./condense";
import * as hash_util from "./hash_util"; import * as hash_util from "./hash_util";
import {$t} from "./i18n"; import {$t} from "./i18n";
import * as message_edit from "./message_edit"; import * as message_edit from "./message_edit";
import type {MessageList} from "./message_list";
import * as message_list_tooltips from "./message_list_tooltips"; import * as message_list_tooltips from "./message_list_tooltips";
import * as message_lists from "./message_lists"; import * as message_lists from "./message_lists";
import type {MessageList} from "./message_lists";
import * as message_store from "./message_store"; import * as message_store from "./message_store";
import type {Message} from "./message_store"; import type {Message} from "./message_store";
import * as message_viewport from "./message_viewport"; import * as message_viewport from "./message_viewport";
@ -1936,7 +1936,7 @@ export class MessageListView {
} }
render_trailing_bookend( render_trailing_bookend(
stream_name: string, stream_name: string | undefined,
subscribed: boolean, subscribed: boolean,
deactivated: boolean, deactivated: boolean,
just_unsubscribed: boolean, just_unsubscribed: boolean,

View File

@ -1,69 +1,11 @@
import $ from "jquery"; import $ from "jquery";
import * as inbox_util from "./inbox_util"; import * as inbox_util from "./inbox_util";
import type {MessageList} from "./message_list";
import type {MessageListData} from "./message_list_data"; import type {MessageListData} from "./message_list_data";
import * as message_list_data_cache from "./message_list_data_cache"; import * as message_list_data_cache from "./message_list_data_cache";
import type {MessageListView} from "./message_list_view";
import type {Message} from "./message_store";
import * as ui_util from "./ui_util"; import * as ui_util from "./ui_util";
export type RenderInfo = {need_user_to_scroll: boolean};
export type SelectIdOpts = {
then_scroll?: boolean;
target_scroll_offset?: number;
use_closest?: boolean;
empty_ok?: boolean;
mark_read?: boolean;
force_rerender?: boolean;
from_scroll?: boolean;
};
export type MessageList = {
id: number;
view: MessageListView;
is_combined_feed_view: boolean;
selected_id: () => number;
selected_row: () => JQuery;
selected_idx: () => number;
all_messages: () => Message[];
get: (id: number) => Message | undefined;
has_unread_messages: () => boolean;
can_mark_messages_read: () => boolean;
can_mark_messages_read_without_setting: () => boolean;
change_message_id: (old_id: number, new_id: number) => boolean;
remove_and_rerender: (id: number[]) => void;
rerender_view: () => void;
update_muting_and_rerender: () => void;
prev: () => number | undefined;
next: () => number | undefined;
is_at_end: () => boolean;
prevent_reading: () => void;
resume_reading: () => void;
data: MessageListData;
select_id: (message_id: number, opts?: SelectIdOpts) => void;
get_row: (message_id: number) => JQuery;
add_messages: (
messages: Message[],
append_opts: {messages_are_new: boolean},
) => RenderInfo | undefined;
first: () => Message | undefined;
last: () => Message | undefined;
visibly_empty: () => boolean;
selected_message: () => Message;
should_preserve_current_rendered_state: () => boolean;
show_edit_message: ($row: JQuery, $form: JQuery) => void;
show_edit_topic_on_recipient_row: ($recipient_row: JQuery, $form: JQuery) => void;
hide_edit_topic_on_recipient_row: ($recipient_row: JQuery) => void;
hide_edit_message: ($row: JQuery) => void;
get_last_message_sent_by_me: () => Message | undefined;
num_items: () => number;
last_message_historical: boolean;
reselect_selected_id: () => void;
is_keyword_search: () => boolean;
update_trailing_bookend: (force_render?: boolean) => void;
};
export let current: MessageList | undefined; export let current: MessageList | undefined;
export const rendered_message_lists = new Map<number, MessageList>(); export const rendered_message_lists = new Map<number, MessageList>();

View File

@ -1,8 +1,8 @@
import assert from "minimalistic-assert"; import assert from "minimalistic-assert";
import {all_messages_data} from "./all_messages_data"; import {all_messages_data} from "./all_messages_data";
import type {MessageList, RenderInfo} from "./message_list";
import type {MessageListData} from "./message_list_data"; import type {MessageListData} from "./message_list_data";
import {type MessageList, type RenderInfo} from "./message_lists";
import * as message_lists from "./message_lists"; import * as message_lists from "./message_lists";
import * as message_store from "./message_store"; import * as message_store from "./message_store";
import type {Message} from "./message_store"; import type {Message} from "./message_store";

View File

@ -2,7 +2,7 @@ import {z} from "zod";
import * as blueslip from "./blueslip"; import * as blueslip from "./blueslip";
import * as channel from "./channel"; import * as channel from "./channel";
import type {MessageList} from "./message_lists"; import type {MessageList} from "./message_list";
import * as message_store from "./message_store"; import * as message_store from "./message_store";
import type {Message} from "./message_store"; import type {Message} from "./message_store";
import type {PollWidgetOutboundData} from "./poll_widget"; import type {PollWidgetOutboundData} from "./poll_widget";

View File

@ -9,7 +9,7 @@ const blueslip = require("./lib/zblueslip");
const $ = require("./lib/zjquery"); const $ = require("./lib/zjquery");
const {current_user} = require("./lib/zpage_params"); const {current_user} = require("./lib/zpage_params");
// These unit tests for web/src/message_list.js emphasize the model-ish // These unit tests for web/src/message_list.ts emphasize the model-ish
// aspects of the MessageList class. We have to stub out a few functions // aspects of the MessageList class. We have to stub out a few functions
// related to views and events to get the tests working. // related to views and events to get the tests working.