2021-02-28 21:31:33 +01:00
|
|
|
import autosize from "autosize";
|
2021-03-11 05:43:45 +01:00
|
|
|
import $ from "jquery";
|
2020-08-01 03:43:15 +02:00
|
|
|
|
2021-03-16 23:38:59 +01:00
|
|
|
import * as blueslip from "./blueslip";
|
2021-02-28 21:31:33 +01:00
|
|
|
import {MessageListData} from "./message_list_data";
|
|
|
|
import {MessageListView} from "./message_list_view";
|
2021-03-26 15:21:47 +01:00
|
|
|
import * as narrow_banner from "./narrow_banner";
|
2021-02-28 21:31:33 +01:00
|
|
|
import * as narrow_state from "./narrow_state";
|
2021-03-25 22:35:45 +01:00
|
|
|
import {page_params} from "./page_params";
|
2021-02-28 21:31:33 +01:00
|
|
|
import * as stream_data from "./stream_data";
|
2019-07-16 20:19:11 +02:00
|
|
|
|
2021-02-28 21:31:33 +01:00
|
|
|
export class MessageList {
|
2023-01-28 00:07:48 +01:00
|
|
|
// A MessageList is the main interface for a message feed that is
|
|
|
|
// rendered in the DOM. Code outside the message feed rendering
|
|
|
|
// internals will directly call this module in order to manipulate
|
|
|
|
// a message feed.
|
|
|
|
//
|
|
|
|
// Each MessageList has an associated MessageListData, which
|
|
|
|
// manages the messages, and a MessageListView, which manages the
|
|
|
|
// the templates/HTML rendering as well as invisible pagination.
|
|
|
|
//
|
|
|
|
// TODO: The abstraction boundary between this and MessageListView
|
|
|
|
// is not particularly well-defined; it could be nice to figure
|
|
|
|
// out a good rule.
|
2020-07-23 00:25:12 +02:00
|
|
|
constructor(opts) {
|
2023-01-28 00:07:48 +01:00
|
|
|
// The MessageListData keeps track of the actual sequence of
|
|
|
|
// messages displayed by this MessageList. Most
|
|
|
|
// configuration/logic questions in this module will be
|
|
|
|
// answered by calling a function from the MessageListData,
|
|
|
|
// its Filter, or its FetchStatus object.
|
2020-07-23 00:25:12 +02:00
|
|
|
if (opts.data) {
|
|
|
|
this.data = opts.data;
|
|
|
|
} else {
|
|
|
|
const filter = opts.filter;
|
|
|
|
|
|
|
|
this.data = new MessageListData({
|
2021-04-29 14:57:32 +02:00
|
|
|
excludes_muted_topics: opts.excludes_muted_topics,
|
2020-07-23 00:25:12 +02:00
|
|
|
filter,
|
|
|
|
});
|
|
|
|
}
|
2013-02-26 23:30:13 +01:00
|
|
|
|
2023-01-28 00:07:48 +01:00
|
|
|
// The table_name is the outer HTML element for this message
|
|
|
|
// list in the DOM.
|
2020-07-23 00:25:12 +02:00
|
|
|
const table_name = opts.table_name;
|
|
|
|
this.table_name = table_name;
|
2023-01-28 00:07:48 +01:00
|
|
|
|
|
|
|
// TODO: This property should likely just be inlined into
|
|
|
|
// having the MessageListView code that needs to access it
|
|
|
|
// query .data.filter directly.
|
|
|
|
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, table_name, collapse_messages);
|
|
|
|
|
|
|
|
// Whether this is a narrowed message list. The only message
|
|
|
|
// list that is not is the home_msg_list global.
|
|
|
|
//
|
|
|
|
// TODO: It would probably be more readable to replace this
|
|
|
|
// with another property with an inverted meaning, since
|
|
|
|
// home_msg_list is the message list that is special/unique.
|
2020-07-23 00:25:12 +02:00
|
|
|
this.narrowed = this.table_name === "zfilt";
|
2023-01-28 00:07:48 +01:00
|
|
|
|
|
|
|
// TODO: This appears to be unused and can be deleted.
|
2020-07-23 00:25:12 +02:00
|
|
|
this.num_appends = 0;
|
2023-01-28 00:07:48 +01:00
|
|
|
|
|
|
|
// 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.
|
2021-08-05 00:59:03 +02:00
|
|
|
this.reading_prevented = false;
|
2020-07-23 00:25:12 +02:00
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-08-05 00:59:03 +02:00
|
|
|
prevent_reading() {
|
|
|
|
this.reading_prevented = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
resume_reading() {
|
|
|
|
this.reading_prevented = false;
|
|
|
|
}
|
|
|
|
|
2020-07-23 00:25:12 +02:00
|
|
|
add_messages(messages, opts) {
|
2018-05-14 12:39:34 +02:00
|
|
|
// This adds all messages to our data, but only returns
|
|
|
|
// the currently viewable ones.
|
2019-11-02 00:06:25 +01:00
|
|
|
const info = this.data.add_messages(messages);
|
2018-05-14 12:39:34 +02:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const top_messages = info.top_messages;
|
|
|
|
const bottom_messages = info.bottom_messages;
|
|
|
|
const interior_messages = info.interior_messages;
|
2013-09-22 16:41:33 +02:00
|
|
|
|
message scrolling: Fix "Scroll down to view" warning.
We recently added a feature to warn users that they
may need to scroll down to view messages that they
just sent, but it was broken due to various complexities
in the rendering code path.
Now we compute it a bit more rigorously.
It requires us to pass some info about rendering up
and down the stack, which is why it's kind of a long
commit, but the bulk of the logic is in these JS files:
* message_list_view.js
* notifications.js
I choose to pass structs around instead of booleans,
because I anticipate we may eventually add more metadata
about rendering to it, plus bools are just kinda brittle.
(The exceptions are that `_maybe_autoscroll`, which
is at the bottom of the stack, just passes back a simple
boolean, and `notify_local_mixes`, also at the bottom
of the stack, just accepts a simple boolean.)
This errs on the side of warning the user, even if the
new message is partially visible.
Fixes #11138
2019-01-07 21:00:03 +01:00
|
|
|
// Currently we only need data back from rendering to
|
|
|
|
// tell us whether users needs to scroll, which only
|
|
|
|
// applies for `append_to_view`, but this may change over
|
|
|
|
// time.
|
2019-11-02 00:06:25 +01:00
|
|
|
let render_info;
|
message scrolling: Fix "Scroll down to view" warning.
We recently added a feature to warn users that they
may need to scroll down to view messages that they
just sent, but it was broken due to various complexities
in the rendering code path.
Now we compute it a bit more rigorously.
It requires us to pass some info about rendering up
and down the stack, which is why it's kind of a long
commit, but the bulk of the logic is in these JS files:
* message_list_view.js
* notifications.js
I choose to pass structs around instead of booleans,
because I anticipate we may eventually add more metadata
about rendering to it, plus bools are just kinda brittle.
(The exceptions are that `_maybe_autoscroll`, which
is at the bottom of the stack, just passes back a simple
boolean, and `notify_local_mixes`, also at the bottom
of the stack, just accepts a simple boolean.)
This errs on the side of warning the user, even if the
new message is partially visible.
Fixes #11138
2019-01-07 21:00:03 +01:00
|
|
|
|
2013-09-22 16:41:33 +02:00
|
|
|
if (interior_messages.length > 0) {
|
2020-07-23 00:25:12 +02:00
|
|
|
this.view.rerender_preserving_scrolltop(true);
|
2013-09-22 16:41:33 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (top_messages.length > 0) {
|
2020-07-23 00:25:12 +02:00
|
|
|
this.view.prepend(top_messages);
|
2013-09-22 16:41:33 +02:00
|
|
|
}
|
2018-05-04 12:44:28 +02:00
|
|
|
|
2013-09-22 16:41:33 +02:00
|
|
|
if (bottom_messages.length > 0) {
|
2020-07-23 00:25:12 +02:00
|
|
|
render_info = this.append_to_view(bottom_messages, opts);
|
2013-09-22 16:41:33 +02:00
|
|
|
}
|
|
|
|
|
2022-09-07 09:06:25 +02:00
|
|
|
if (this.narrowed && !this.empty()) {
|
2013-09-22 16:41:33 +02:00
|
|
|
// If adding some new messages to the message tables caused
|
|
|
|
// our current narrow to no longer be empty, hide the empty
|
|
|
|
// feed placeholder text.
|
2021-03-26 15:21:47 +01:00
|
|
|
narrow_banner.hide_empty_narrow_message();
|
2014-03-04 22:39:34 +01:00
|
|
|
}
|
|
|
|
|
2022-09-07 09:06:25 +02:00
|
|
|
if (this.narrowed && !this.empty() && this.selected_id() === -1) {
|
2013-09-22 16:41:33 +02:00
|
|
|
// And also select the newly arrived message.
|
2020-07-23 00:25:12 +02:00
|
|
|
this.select_id(this.selected_id(), {then_scroll: true, use_closest: true});
|
2013-09-22 16:41:33 +02:00
|
|
|
}
|
message scrolling: Fix "Scroll down to view" warning.
We recently added a feature to warn users that they
may need to scroll down to view messages that they
just sent, but it was broken due to various complexities
in the rendering code path.
Now we compute it a bit more rigorously.
It requires us to pass some info about rendering up
and down the stack, which is why it's kind of a long
commit, but the bulk of the logic is in these JS files:
* message_list_view.js
* notifications.js
I choose to pass structs around instead of booleans,
because I anticipate we may eventually add more metadata
about rendering to it, plus bools are just kinda brittle.
(The exceptions are that `_maybe_autoscroll`, which
is at the bottom of the stack, just passes back a simple
boolean, and `notify_local_mixes`, also at the bottom
of the stack, just accepts a simple boolean.)
This errs on the side of warning the user, even if the
new message is partially visible.
Fixes #11138
2019-01-07 21:00:03 +01:00
|
|
|
|
|
|
|
return render_info;
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-09-22 16:41:33 +02:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
get(id) {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.data.get(id);
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2012-12-05 23:54:49 +01:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
num_items() {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.data.num_items();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-08-16 17:10:22 +02:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
empty() {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.data.empty();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2012-12-05 23:54:49 +01:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
first() {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.data.first();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2012-12-05 23:54:49 +01:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
last() {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.data.last();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2012-12-05 23:54:49 +01:00
|
|
|
|
2021-08-05 00:59:03 +02:00
|
|
|
ids_greater_or_equal_than(id) {
|
|
|
|
return this.data.ids_greater_or_equal_than(id);
|
|
|
|
}
|
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
prev() {
|
2018-05-26 12:29:38 +02:00
|
|
|
return this.data.prev();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2018-05-26 12:29:38 +02:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
next() {
|
2018-05-26 12:29:38 +02:00
|
|
|
return this.data.next();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2018-05-26 12:29:38 +02:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
is_at_end() {
|
2018-05-29 00:18:27 +02:00
|
|
|
return this.data.is_at_end();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2018-05-29 00:18:27 +02:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
nth_most_recent_id(n) {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.data.nth_most_recent_id(n);
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-08-14 23:04:24 +02:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
is_search() {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.data.is_search();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2018-05-04 18:51:09 +02:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
can_mark_messages_read() {
|
2021-08-05 00:59:03 +02:00
|
|
|
/* Automatically marking messages as read can be disabled for
|
|
|
|
two different reasons:
|
|
|
|
* The view is structurally a search view, encoded in the
|
|
|
|
properties of the message_list_data object.
|
|
|
|
* The user recently marked messages in the view as unread, and
|
|
|
|
we don't want to lose that state.
|
|
|
|
*/
|
|
|
|
return this.data.can_mark_messages_read() && !this.reading_prevented;
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2019-07-10 02:03:41 +02:00
|
|
|
|
2021-03-24 21:44:43 +01:00
|
|
|
clear({clear_selected_id = true} = {}) {
|
2018-05-04 12:44:28 +02:00
|
|
|
this.data.clear();
|
2013-08-16 17:10:22 +02:00
|
|
|
this.view.clear_rendering_state(true);
|
2013-02-22 20:48:31 +01:00
|
|
|
|
2021-03-24 21:44:43 +01:00
|
|
|
if (clear_selected_id) {
|
2018-05-04 12:44:28 +02:00
|
|
|
this.data.clear_selected_id();
|
2013-02-22 20:48:31 +01:00
|
|
|
}
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-02-22 20:48:31 +01:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
selected_id() {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.data.selected_id();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-02-20 18:26:50 +01:00
|
|
|
|
2020-07-23 00:25:12 +02:00
|
|
|
select_id(id, opts) {
|
2020-02-09 04:15:38 +01:00
|
|
|
opts = {
|
2018-05-06 21:43:17 +02:00
|
|
|
then_scroll: false,
|
|
|
|
target_scroll_offset: undefined,
|
|
|
|
use_closest: false,
|
|
|
|
empty_ok: false,
|
|
|
|
mark_read: true,
|
|
|
|
force_rerender: false,
|
2020-02-09 04:15:38 +01:00
|
|
|
...opts,
|
2020-07-20 22:18:43 +02:00
|
|
|
id,
|
2018-05-06 21:43:17 +02:00
|
|
|
msg_list: this,
|
2020-09-13 18:29:24 +02:00
|
|
|
previously_selected_id: this.data.selected_id(),
|
2020-02-09 04:15:38 +01:00
|
|
|
};
|
2013-07-03 19:15:07 +02:00
|
|
|
|
2020-07-23 00:25:12 +02:00
|
|
|
const convert_id = (str_id) => {
|
2020-10-07 09:17:30 +02:00
|
|
|
const id = Number.parseFloat(str_id);
|
|
|
|
if (Number.isNaN(id)) {
|
2020-10-07 11:51:57 +02:00
|
|
|
throw new TypeError("Bad message id " + str_id);
|
2017-03-27 19:26:30 +02:00
|
|
|
}
|
|
|
|
return id;
|
2020-07-23 00:25:12 +02:00
|
|
|
};
|
2013-08-07 20:28:50 +02:00
|
|
|
|
2017-03-27 19:26:30 +02:00
|
|
|
id = convert_id(id);
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const closest_id = this.closest_id(id);
|
2013-09-27 21:18:54 +02:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
let error_data;
|
2017-06-15 23:46:41 +02:00
|
|
|
|
2013-09-27 21:18:54 +02:00
|
|
|
// The name "use_closest" option is a bit legacy. We
|
|
|
|
// are always gonna move to the closest visible id; the flag
|
|
|
|
// just says whether we call blueslip.error or not. The caller
|
|
|
|
// sets use_closest to true when it expects us to move the
|
|
|
|
// pointer as needed, so only generate an error if the flag is
|
|
|
|
// false.
|
|
|
|
if (!opts.use_closest && closest_id !== id) {
|
2017-06-15 23:46:41 +02:00
|
|
|
error_data = {
|
2017-06-15 22:33:38 +02:00
|
|
|
table_name: this.table_name,
|
2020-07-20 22:18:43 +02:00
|
|
|
id,
|
|
|
|
closest_id,
|
2017-06-15 22:33:38 +02:00
|
|
|
};
|
2020-07-15 00:34:28 +02:00
|
|
|
blueslip.error("Selected message id not in MessageList", error_data);
|
2013-02-20 18:33:04 +01:00
|
|
|
}
|
2013-03-13 18:48:02 +01:00
|
|
|
|
2013-12-18 19:06:55 +01:00
|
|
|
if (closest_id === -1 && !opts.empty_ok) {
|
2017-06-15 23:46:41 +02:00
|
|
|
error_data = {
|
2013-12-02 21:40:49 +01:00
|
|
|
table_name: this.table_name,
|
2020-07-20 22:18:43 +02:00
|
|
|
id,
|
2018-05-04 12:44:28 +02:00
|
|
|
items_length: this.data.num_items(),
|
2013-12-02 21:40:49 +01:00
|
|
|
};
|
2021-04-28 08:25:48 +02:00
|
|
|
blueslip.error("Cannot select id -1", error_data);
|
|
|
|
throw new Error("Cannot select id -1");
|
2013-10-30 18:38:16 +01:00
|
|
|
}
|
|
|
|
|
2013-09-27 21:18:54 +02:00
|
|
|
id = closest_id;
|
|
|
|
opts.id = id;
|
2018-05-04 12:44:28 +02:00
|
|
|
this.data.set_selected_id(id);
|
2013-09-27 21:18:54 +02:00
|
|
|
|
2014-01-22 22:20:36 +01:00
|
|
|
if (opts.force_rerender) {
|
|
|
|
this.rerender();
|
|
|
|
} else if (!opts.from_rendering) {
|
2013-08-16 17:10:22 +02:00
|
|
|
this.view.maybe_rerender();
|
2013-07-03 19:53:27 +02:00
|
|
|
}
|
2013-03-04 20:22:09 +01:00
|
|
|
|
2020-12-11 04:26:23 +01:00
|
|
|
$(document).trigger(new $.Event("message_selected.zulip", opts));
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-02-20 18:33:04 +01:00
|
|
|
|
2020-07-23 00:25:12 +02:00
|
|
|
selected_message() {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.get(this.data.selected_id());
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-02-14 23:48:37 +01:00
|
|
|
|
2020-07-23 00:25:12 +02:00
|
|
|
selected_row() {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.get_row(this.data.selected_id());
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-02-20 00:49:21 +01:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
closest_id(id) {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.data.closest_id(id);
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-07-24 22:33:06 +02:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
advance_past_messages(msg_ids) {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.data.advance_past_messages(msg_ids);
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2012-12-05 23:54:49 +01:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
selected_idx() {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.data.selected_idx();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-04-10 18:30:36 +02:00
|
|
|
|
2013-04-10 23:38:30 +02:00
|
|
|
// Maintains a trailing bookend element explaining any changes in
|
|
|
|
// your subscribed/unsubscribed status at the bottom of the
|
|
|
|
// message list.
|
2020-07-23 00:25:12 +02:00
|
|
|
update_trailing_bookend() {
|
2013-08-16 17:10:22 +02:00
|
|
|
this.view.clear_trailing_bookend();
|
2013-04-10 23:38:30 +02:00
|
|
|
if (!this.narrowed) {
|
|
|
|
return;
|
|
|
|
}
|
2019-11-02 00:06:25 +01:00
|
|
|
const stream_name = narrow_state.stream();
|
2017-09-15 10:55:40 +02:00
|
|
|
if (stream_name === undefined) {
|
2013-04-10 23:38:30 +02:00
|
|
|
return;
|
|
|
|
}
|
2021-11-04 07:11:19 +01:00
|
|
|
|
2021-11-03 09:41:43 +01:00
|
|
|
let deactivated = false;
|
|
|
|
let just_unsubscribed = false;
|
2021-11-23 00:37:45 +01:00
|
|
|
const subscribed = stream_data.is_subscribed_by_name(stream_name);
|
2020-08-22 23:29:23 +02:00
|
|
|
const sub = stream_data.get_sub(stream_name);
|
2021-11-16 03:05:40 +01:00
|
|
|
const can_toggle_subscription =
|
|
|
|
sub !== undefined && stream_data.can_toggle_subscription(sub);
|
2020-08-22 23:29:23 +02:00
|
|
|
if (sub === undefined) {
|
2021-11-03 09:41:43 +01:00
|
|
|
deactivated = true;
|
|
|
|
} else if (!subscribed && !this.last_message_historical) {
|
|
|
|
just_unsubscribed = true;
|
2013-04-10 23:38:30 +02:00
|
|
|
}
|
2021-11-03 09:41:43 +01:00
|
|
|
this.view.render_trailing_bookend(
|
|
|
|
stream_name,
|
|
|
|
subscribed,
|
|
|
|
deactivated,
|
|
|
|
just_unsubscribed,
|
2021-11-16 03:05:40 +01:00
|
|
|
can_toggle_subscription,
|
2021-11-04 07:11:19 +01:00
|
|
|
page_params.is_spectator,
|
2021-11-03 09:41:43 +01:00
|
|
|
);
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-04-10 23:38:30 +02:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
unmuted_messages(messages) {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.data.unmuted_messages(messages);
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-09-19 00:43:59 +02:00
|
|
|
|
2020-07-23 00:25:12 +02:00
|
|
|
append(messages, opts) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const viewable_messages = this.data.append(messages);
|
2018-05-13 23:03:16 +02:00
|
|
|
this.append_to_view(viewable_messages, opts);
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2018-05-13 23:03:16 +02:00
|
|
|
|
2021-03-24 21:44:43 +01:00
|
|
|
append_to_view(messages, {messages_are_new = false} = {}) {
|
2013-08-14 23:48:28 +02:00
|
|
|
this.num_appends += 1;
|
2021-03-24 21:44:43 +01:00
|
|
|
const render_info = this.view.append(messages, messages_are_new);
|
message scrolling: Fix "Scroll down to view" warning.
We recently added a feature to warn users that they
may need to scroll down to view messages that they
just sent, but it was broken due to various complexities
in the rendering code path.
Now we compute it a bit more rigorously.
It requires us to pass some info about rendering up
and down the stack, which is why it's kind of a long
commit, but the bulk of the logic is in these JS files:
* message_list_view.js
* notifications.js
I choose to pass structs around instead of booleans,
because I anticipate we may eventually add more metadata
about rendering to it, plus bools are just kinda brittle.
(The exceptions are that `_maybe_autoscroll`, which
is at the bottom of the stack, just passes back a simple
boolean, and `notify_local_mixes`, also at the bottom
of the stack, just accepts a simple boolean.)
This errs on the side of warning the user, even if the
new message is partially visible.
Fixes #11138
2019-01-07 21:00:03 +01:00
|
|
|
return render_info;
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2012-12-05 23:54:49 +01:00
|
|
|
|
2020-11-12 22:03:45 +01:00
|
|
|
remove_and_rerender(message_ids) {
|
|
|
|
this.data.remove(message_ids);
|
2014-02-25 22:45:11 +01:00
|
|
|
this.rerender();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-12-17 20:50:11 +01:00
|
|
|
|
2022-01-25 11:36:19 +01:00
|
|
|
show_edit_message($row, edit_obj) {
|
|
|
|
if ($row.find(".message_edit_form form").length !== 0) {
|
2020-05-21 12:23:29 +02:00
|
|
|
return;
|
|
|
|
}
|
2022-01-25 11:36:19 +01:00
|
|
|
$row.find(".message_edit_form").append(edit_obj.$form);
|
|
|
|
$row.find(".message_content, .status-message, .message_controls").hide();
|
2022-10-06 09:10:57 +02:00
|
|
|
$row.find(".sender-status").toggleClass("sender-status-edit");
|
2022-11-28 04:09:16 +01:00
|
|
|
$row.find(".messagebox-content").addClass("content_edit_mode");
|
2022-01-25 11:36:19 +01:00
|
|
|
$row.find(".message_edit").css("display", "block");
|
|
|
|
autosize($row.find(".message_edit_content"));
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-05-15 00:22:16 +02:00
|
|
|
|
2022-01-25 11:36:19 +01:00
|
|
|
hide_edit_message($row) {
|
|
|
|
$row.find(".message_content, .status-message, .message_controls").show();
|
2022-10-06 09:10:57 +02:00
|
|
|
$row.find(".sender-status").toggleClass("sender-status-edit");
|
2022-01-25 11:36:19 +01:00
|
|
|
$row.find(".message_edit_form").empty();
|
2022-11-28 04:09:16 +01:00
|
|
|
$row.find(".messagebox-content").removeClass("content_edit_mode");
|
2022-01-25 11:36:19 +01:00
|
|
|
$row.find(".message_edit").hide();
|
|
|
|
$row.trigger("mouseleave");
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-05-15 00:22:16 +02:00
|
|
|
|
2022-01-25 11:36:19 +01:00
|
|
|
show_edit_topic_on_recipient_row($recipient_row, $form) {
|
|
|
|
$recipient_row.find(".topic_edit_form").append($form);
|
|
|
|
$recipient_row.find(".on_hover_topic_edit").hide();
|
2022-10-03 12:15:07 +02:00
|
|
|
$recipient_row.find(".edit_message_button").hide();
|
2022-01-25 11:36:19 +01:00
|
|
|
$recipient_row.find(".stream_topic").hide();
|
|
|
|
$recipient_row.find(".topic_edit").show();
|
|
|
|
$recipient_row.find(".always_visible_topic_edit").hide();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-08-16 23:45:13 +02:00
|
|
|
|
2022-01-25 11:36:19 +01:00
|
|
|
hide_edit_topic_on_recipient_row($recipient_row) {
|
|
|
|
$recipient_row.find(".stream_topic").show();
|
|
|
|
$recipient_row.find(".on_hover_topic_edit").show();
|
2022-10-03 12:15:07 +02:00
|
|
|
$recipient_row.find(".edit_message_button").show();
|
2022-01-25 11:36:19 +01:00
|
|
|
$recipient_row.find(".topic_edit_form").empty();
|
|
|
|
$recipient_row.find(".topic_edit").hide();
|
|
|
|
$recipient_row.find(".always_visible_topic_edit").show();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-08-16 23:45:13 +02:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
show_message_as_read(message, options) {
|
2022-01-25 11:36:19 +01:00
|
|
|
const $row = this.get_row(message.id);
|
2020-07-15 01:29:15 +02:00
|
|
|
if (options.from === "pointer" || options.from === "server") {
|
2022-01-25 11:36:19 +01:00
|
|
|
$row.find(".unread_marker").addClass("fast_fade");
|
2013-07-26 17:19:13 +02:00
|
|
|
} else {
|
2022-01-25 11:36:19 +01:00
|
|
|
$row.find(".unread_marker").addClass("slow_fade");
|
2013-07-26 17:19:13 +02:00
|
|
|
}
|
2022-01-25 11:36:19 +01:00
|
|
|
$row.removeClass("unread");
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-07-01 23:28:27 +02:00
|
|
|
|
2022-09-23 02:09:42 +02:00
|
|
|
reselect_selected_id() {
|
|
|
|
const selected_id = this.data.selected_id();
|
|
|
|
|
|
|
|
if (selected_id !== -1) {
|
2022-09-23 02:13:03 +02:00
|
|
|
this.select_id(this.data.selected_id(), {from_rendering: true, mark_read: false});
|
2022-09-23 02:09:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-26 18:37:01 +02:00
|
|
|
rerender_view() {
|
|
|
|
this.view.rerender_preserving_scrolltop();
|
2022-09-23 02:09:42 +02:00
|
|
|
this.reselect_selected_id();
|
2020-07-26 18:37:01 +02:00
|
|
|
}
|
|
|
|
|
2020-07-23 00:25:12 +02:00
|
|
|
rerender() {
|
2013-05-14 21:18:11 +02:00
|
|
|
// We need to clear the rendering state, rather than just
|
2013-08-16 17:10:22 +02:00
|
|
|
// doing clear_table, since we want to potentially recollapse
|
2013-05-14 21:18:11 +02:00
|
|
|
// things.
|
2018-05-04 12:44:28 +02:00
|
|
|
this.data.reset_select_to_closest();
|
2013-08-16 17:10:22 +02:00
|
|
|
this.view.clear_rendering_state(false);
|
|
|
|
this.view.update_render_window(this.selected_idx(), false);
|
2018-04-14 00:36:14 +02:00
|
|
|
|
2022-09-07 09:06:25 +02:00
|
|
|
if (this.narrowed) {
|
2018-04-14 00:36:14 +02:00
|
|
|
if (this.empty()) {
|
2021-03-26 15:21:47 +01:00
|
|
|
narrow_banner.show_empty_narrow_message();
|
2018-04-14 00:36:14 +02:00
|
|
|
} else {
|
2021-03-26 15:21:47 +01:00
|
|
|
narrow_banner.hide_empty_narrow_message();
|
2018-04-14 00:36:14 +02:00
|
|
|
}
|
|
|
|
}
|
2020-07-26 18:37:01 +02:00
|
|
|
this.rerender_view();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2018-05-04 12:44:28 +02:00
|
|
|
|
message lists: Don't allow user/topic mute message filtering independently.
This basically reverts 4bd7ec7c3699b08655fb3d6ae2a00a19c8a086db and
3a9dfc02e6414089de8ed5cbc85eb69f60454013.
The plan earlier was to have compeletely different codepaths
for user and topic muting, so that we could call seperate
functions in the message list class on receiving the respective
events.
However, this cannot be done, because if we, for example, on
receiving a `muted_users` event, filter `_all_items` based on
just user mutes, and store the result in `_items`, then, that
result may still contain topic-muted messages, which is
undesirable. Hence whenever we filter messages, we must do so
based on both user as well as topic muting.
(The code for the former will be added in further commits.)
So, we will have a single function which will handle updating
the message lists for muting.
2021-05-07 22:13:03 +02:00
|
|
|
update_muting_and_rerender() {
|
|
|
|
this.data.update_items_for_muting();
|
2021-04-30 07:44:43 +02:00
|
|
|
// We need to rerender whether or not the narrow hides muted
|
|
|
|
// topics, because we need to update recipient bars for topics
|
|
|
|
// we've muted when we are displaying those topics.
|
|
|
|
//
|
|
|
|
// 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
|
|
|
|
// processing topic edits, since that's the only place we'll
|
|
|
|
// call this frequently anyway.
|
2021-05-07 22:34:38 +02:00
|
|
|
//
|
|
|
|
// But in any case, we need to rerender the list for user muting,
|
|
|
|
// to make sure only the right messages are hidden.
|
2021-04-30 07:44:43 +02:00
|
|
|
this.rerender();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-09-19 00:43:59 +02:00
|
|
|
|
2020-07-23 00:25:12 +02:00
|
|
|
all_messages() {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.data.all_messages();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-08-14 22:00:32 +02:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
first_unread_message_id() {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.data.first_unread_message_id();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2017-08-02 22:37:13 +02:00
|
|
|
|
2022-02-16 01:30:23 +01:00
|
|
|
has_unread_messages() {
|
|
|
|
return this.data.has_unread_messages();
|
|
|
|
}
|
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
message_range(start, end) {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.data.message_range(start, end);
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2014-01-31 22:06:07 +01:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
get_row(id) {
|
2013-08-16 17:10:22 +02:00
|
|
|
return this.view.get_row(id);
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2013-08-22 19:57:48 +02:00
|
|
|
|
2020-07-23 00:25:12 +02:00
|
|
|
change_message_id(old_id, new_id) {
|
2020-11-12 22:43:04 +01:00
|
|
|
const require_rerender = this.data.change_message_id(old_id, new_id);
|
|
|
|
if (require_rerender) {
|
|
|
|
this.rerender_view();
|
|
|
|
}
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
2016-11-23 05:06:34 +01:00
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
get_last_message_sent_by_me() {
|
2018-05-04 12:44:28 +02:00
|
|
|
return this.data.get_last_message_sent_by_me();
|
2020-07-23 00:25:12 +02:00
|
|
|
}
|
|
|
|
}
|