2017-03-19 20:23:48 +01:00
|
|
|
var message_events = (function () {
|
|
|
|
|
|
|
|
var exports = {};
|
|
|
|
|
2019-01-08 01:28:01 +01:00
|
|
|
function maybe_add_narrowed_messages(messages, msg_list) {
|
2017-03-19 20:23:48 +01:00
|
|
|
var ids = [];
|
|
|
|
_.each(messages, function (elem) {
|
|
|
|
ids.push(elem.id);
|
|
|
|
});
|
|
|
|
|
2017-07-31 21:09:55 +02:00
|
|
|
channel.get({
|
2018-12-18 19:34:45 +01:00
|
|
|
url: '/json/messages/matches_narrow',
|
|
|
|
data: {msg_ids: JSON.stringify(ids),
|
|
|
|
narrow: JSON.stringify(narrow_state.public_operators())},
|
|
|
|
timeout: 5000,
|
2017-03-19 20:23:48 +01:00
|
|
|
success: function (data) {
|
|
|
|
if (msg_list !== current_msg_list) {
|
|
|
|
// We unnarrowed in the mean time
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var new_messages = [];
|
|
|
|
var elsewhere_messages = [];
|
|
|
|
_.each(messages, function (elem) {
|
|
|
|
if (data.messages.hasOwnProperty(elem.id)) {
|
2018-11-15 16:59:41 +01:00
|
|
|
util.set_match_data(elem, data.messages[elem.id]);
|
2017-03-19 20:23:48 +01:00
|
|
|
new_messages.push(elem);
|
|
|
|
} else {
|
|
|
|
elsewhere_messages.push(elem);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-04-28 23:32:37 +02:00
|
|
|
// This second call to add_message_metadata in the
|
|
|
|
// insert_new_messages code path helps in very rare race
|
|
|
|
// conditions, where e.g. the current user's name was
|
|
|
|
// edited in between when they sent the message and when
|
|
|
|
// we hear back from the server and can echo the new
|
|
|
|
// message. Arguably, it's counterproductive complexity.
|
2017-03-19 20:23:48 +01:00
|
|
|
new_messages = _.map(new_messages, message_store.add_message_metadata);
|
2018-04-28 23:32:37 +02:00
|
|
|
|
2019-01-08 01:26:02 +01:00
|
|
|
message_util.add_new_messages(new_messages, msg_list);
|
2017-03-19 20:23:48 +01:00
|
|
|
unread_ops.process_visible();
|
|
|
|
notifications.notify_messages_outside_current_search(elsewhere_messages);
|
|
|
|
},
|
|
|
|
error: function () {
|
|
|
|
// We might want to be more clever here
|
|
|
|
setTimeout(function () {
|
|
|
|
if (msg_list === current_msg_list) {
|
|
|
|
// Don't actually try again if we unnarrowed
|
|
|
|
// while waiting
|
2019-01-08 01:28:01 +01:00
|
|
|
maybe_add_narrowed_messages(messages, msg_list);
|
2017-03-19 20:23:48 +01:00
|
|
|
}
|
|
|
|
}, 5000);
|
|
|
|
}});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-17 16:52:57 +02:00
|
|
|
exports.insert_new_messages = function insert_new_messages(messages, locally_echoed) {
|
2017-03-19 20:23:48 +01:00
|
|
|
messages = _.map(messages, message_store.add_message_metadata);
|
|
|
|
|
2017-08-04 05:54:02 +02:00
|
|
|
unread.process_loaded_messages(messages);
|
|
|
|
|
2019-01-07 21:40:03 +01:00
|
|
|
// message_list.all is a data-only list that we use to populate
|
|
|
|
// other lists, so we always update this
|
2019-01-08 01:26:02 +01:00
|
|
|
message_util.add_new_messages(messages, message_list.all);
|
2017-03-19 20:23:48 +01: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
|
|
|
var render_info;
|
|
|
|
|
2017-04-25 15:25:31 +02:00
|
|
|
if (narrow_state.active()) {
|
2019-01-07 21:40:03 +01:00
|
|
|
// We do this NOW even though the home view is not active,
|
|
|
|
// because we want the home view to load fast later.
|
2019-01-08 01:26:02 +01:00
|
|
|
message_util.add_new_messages(messages, home_msg_list);
|
2019-01-07 21:40:03 +01:00
|
|
|
|
2017-04-25 15:25:31 +02:00
|
|
|
if (narrow_state.filter().can_apply_locally()) {
|
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
|
|
|
render_info = message_util.add_new_messages(messages, message_list.narrowed);
|
2017-03-19 20:23:48 +01:00
|
|
|
} else {
|
|
|
|
// if we cannot apply locally, we have to wait for this callback to happen to notify
|
2019-01-08 01:28:01 +01:00
|
|
|
maybe_add_narrowed_messages(messages, message_list.narrowed);
|
2017-03-19 20:23:48 +01:00
|
|
|
}
|
2019-01-07 21:40:03 +01:00
|
|
|
} else {
|
|
|
|
// we're in the home view, so update its list
|
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
|
|
|
render_info = message_util.add_new_messages(messages, home_msg_list);
|
2017-07-18 20:03:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-17 16:52:57 +02:00
|
|
|
if (locally_echoed) {
|
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
|
|
|
var need_user_to_scroll = render_info && render_info.need_user_to_scroll;
|
|
|
|
notifications.notify_local_mixes(messages, need_user_to_scroll);
|
2017-03-19 20:23:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
activity.process_loaded_messages(messages);
|
2017-08-04 05:48:43 +02:00
|
|
|
|
|
|
|
unread_ui.update_unread_counts();
|
|
|
|
resize.resize_page_components();
|
|
|
|
|
2017-07-18 20:14:30 +02:00
|
|
|
exports.maybe_advance_to_recently_sent_message(messages);
|
|
|
|
unread_ops.process_visible();
|
|
|
|
notifications.received_messages(messages);
|
|
|
|
stream_list.update_streams_sidebar();
|
|
|
|
pm_list.update_private_messages();
|
|
|
|
};
|
2017-03-19 20:23:48 +01:00
|
|
|
|
2017-07-18 20:14:30 +02:00
|
|
|
exports.maybe_advance_to_recently_sent_message = function (messages) {
|
2017-04-25 15:25:31 +02:00
|
|
|
if (narrow_state.narrowed_by_reply()) {
|
2017-03-19 20:23:48 +01:00
|
|
|
// If you send a message when narrowed to a recipient, move the
|
|
|
|
// pointer to it.
|
|
|
|
|
|
|
|
var i;
|
|
|
|
var selected_id = current_msg_list.selected_id();
|
|
|
|
|
|
|
|
// Iterate backwards to find the last message sent_by_me, stopping at
|
|
|
|
// the pointer position.
|
2018-06-04 21:13:07 +02:00
|
|
|
for (i = messages.length - 1; i >= 0; i -= 1) {
|
2017-03-19 20:23:48 +01:00
|
|
|
var id = messages[i].id;
|
|
|
|
if (id <= selected_id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (messages[i].sent_by_me && current_msg_list.get(id) !== undefined) {
|
|
|
|
// If this is a reply we just sent, advance the pointer to it.
|
|
|
|
current_msg_list.select_id(messages[i].id, {then_scroll: true,
|
|
|
|
from_scroll: true});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.update_messages = function update_messages(events) {
|
|
|
|
var msgs_to_rerender = [];
|
|
|
|
var topic_edited = false;
|
|
|
|
var changed_narrow = false;
|
2018-04-14 01:29:21 +02:00
|
|
|
var changed_compose = false;
|
2017-04-21 20:27:45 +02:00
|
|
|
var message_content_edited = false;
|
2017-03-19 20:23:48 +01:00
|
|
|
|
|
|
|
_.each(events, function (event) {
|
|
|
|
var msg = message_store.get(event.message_id);
|
|
|
|
if (msg === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
msgs_to_rerender.push(msg);
|
|
|
|
|
2017-12-21 18:32:38 +01:00
|
|
|
message_store.update_booleans(msg, event.flags);
|
2017-03-19 20:23:48 +01:00
|
|
|
|
|
|
|
condense.un_cache_message_content_height(msg.id);
|
|
|
|
|
|
|
|
if (event.rendered_content !== undefined) {
|
|
|
|
msg.content = event.rendered_content;
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:27:36 +01:00
|
|
|
if (event.is_me_message !== undefined) {
|
|
|
|
msg.is_me_message = event.is_me_message;
|
|
|
|
}
|
|
|
|
|
2017-03-19 20:23:48 +01:00
|
|
|
var row = current_msg_list.get_row(event.message_id);
|
|
|
|
if (row.length > 0) {
|
|
|
|
message_edit.end(row);
|
|
|
|
}
|
|
|
|
|
2018-12-22 17:45:18 +01:00
|
|
|
var new_topic = util.get_edit_event_topic(event);
|
|
|
|
|
|
|
|
if (new_topic !== undefined) {
|
2017-03-19 20:23:48 +01:00
|
|
|
// A topic edit may affect multiple messages, listed in
|
|
|
|
// event.message_ids. event.message_id is still the first message
|
|
|
|
// where the user initiated the edit.
|
|
|
|
topic_edited = true;
|
|
|
|
|
|
|
|
var going_forward_change = _.indexOf(['change_later', 'change_all'], event.propagate_mode) >= 0;
|
|
|
|
|
|
|
|
var stream_name = stream_data.get_sub_by_id(event.stream_id).name;
|
2017-04-15 01:15:59 +02:00
|
|
|
var compose_stream_name = compose_state.stream_name();
|
2018-12-22 17:39:37 +01:00
|
|
|
var orig_topic = util.get_edit_event_orig_topic(event);
|
2017-03-19 20:23:48 +01:00
|
|
|
|
|
|
|
if (going_forward_change && stream_name && compose_stream_name) {
|
|
|
|
if (stream_name.toLowerCase() === compose_stream_name.toLowerCase()) {
|
2018-12-22 17:39:37 +01:00
|
|
|
if (orig_topic === compose_state.topic()) {
|
2018-04-14 01:29:21 +02:00
|
|
|
changed_compose = true;
|
2018-12-22 17:45:18 +01:00
|
|
|
compose_state.topic(new_topic);
|
2018-04-14 01:29:21 +02:00
|
|
|
compose_fade.set_focused_recipient("stream");
|
2017-03-19 20:23:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (going_forward_change) {
|
|
|
|
var current_id = current_msg_list.selected_id();
|
|
|
|
var selection_changed_topic = _.indexOf(event.message_ids, current_id) >= 0;
|
|
|
|
|
|
|
|
if (selection_changed_topic) {
|
2017-04-25 15:25:31 +02:00
|
|
|
var current_filter = narrow_state.filter();
|
2017-03-19 20:23:48 +01:00
|
|
|
if (current_filter && stream_name) {
|
2018-12-22 17:39:37 +01:00
|
|
|
if (current_filter.has_topic(stream_name, orig_topic)) {
|
2018-12-22 17:45:18 +01:00
|
|
|
var new_filter = current_filter.filter_with_new_topic(new_topic);
|
2017-03-19 20:23:48 +01:00
|
|
|
var operators = new_filter.operators();
|
|
|
|
var opts = {
|
|
|
|
trigger: 'topic change',
|
|
|
|
then_select_id: current_id,
|
|
|
|
};
|
|
|
|
narrow.activate(operators, opts);
|
|
|
|
changed_narrow = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_.each(event.message_ids, function (id) {
|
|
|
|
var msg = message_store.get(id);
|
|
|
|
if (msg === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove the recent topics entry for the old topics;
|
2018-12-22 18:44:54 +01:00
|
|
|
// must be called before we call set_message_topic.
|
2017-07-26 14:05:25 +02:00
|
|
|
topic_data.remove_message({
|
|
|
|
stream_id: msg.stream_id,
|
2018-12-22 21:16:32 +01:00
|
|
|
topic_name: util.get_message_topic(msg),
|
2017-07-26 14:05:25 +02:00
|
|
|
});
|
|
|
|
|
2017-03-19 20:23:48 +01:00
|
|
|
// Update the unread counts; again, this must be called
|
2018-12-22 18:44:54 +01:00
|
|
|
// before we call set_message_topic.
|
2017-03-19 20:23:48 +01:00
|
|
|
unread.update_unread_topics(msg, event);
|
|
|
|
|
2018-12-22 18:44:54 +01:00
|
|
|
util.set_message_topic(msg, new_topic);
|
2018-11-15 16:41:21 +01:00
|
|
|
util.set_topic_links(msg, util.get_topic_links(event));
|
2017-07-26 14:05:25 +02:00
|
|
|
|
2017-03-19 20:23:48 +01:00
|
|
|
// Add the recent topics entry for the new topics; must
|
2018-12-22 18:44:54 +01:00
|
|
|
// be called after we call set_message_topic.
|
2017-07-26 14:05:25 +02:00
|
|
|
topic_data.add_message({
|
|
|
|
stream_id: msg.stream_id,
|
2018-12-22 21:16:32 +01:00
|
|
|
topic_name: util.get_message_topic(msg),
|
2017-07-26 14:05:25 +02:00
|
|
|
message_id: msg.id,
|
|
|
|
});
|
2017-03-19 20:23:48 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event.orig_content !== undefined) {
|
2017-07-16 11:00:44 +02:00
|
|
|
if (page_params.realm_allow_edit_history) {
|
|
|
|
// Most correctly, we should do this for topic edits as
|
|
|
|
// well; but we don't use the data except for content
|
|
|
|
// edits anyway.
|
|
|
|
var edit_history_entry = {
|
|
|
|
edited_by: event.edited_by,
|
|
|
|
prev_content: event.orig_content,
|
|
|
|
prev_rendered_content: event.orig_rendered_content,
|
|
|
|
prev_rendered_content_version: event.prev_rendered_content_version,
|
|
|
|
timestamp: event.edit_timestamp,
|
|
|
|
};
|
|
|
|
// Add message's edit_history in message dict
|
|
|
|
// For messages that are edited, edit_history needs to
|
|
|
|
// be added to message in frontend.
|
|
|
|
if (msg.edit_history === undefined) {
|
|
|
|
msg.edit_history = [];
|
|
|
|
}
|
|
|
|
msg.edit_history = [edit_history_entry].concat(msg.edit_history);
|
2017-03-19 20:23:48 +01:00
|
|
|
}
|
2017-04-21 20:27:45 +02:00
|
|
|
message_content_edited = true;
|
2017-06-06 18:51:05 +02:00
|
|
|
|
|
|
|
// Update raw_content, so that editing a few times in a row is fast.
|
|
|
|
msg.raw_content = event.content;
|
2017-03-19 20:23:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
msg.last_edit_timestamp = event.edit_timestamp;
|
|
|
|
delete msg.last_edit_timestr;
|
|
|
|
|
|
|
|
notifications.received_messages([msg]);
|
|
|
|
alert_words.process_message(msg);
|
|
|
|
});
|
|
|
|
|
|
|
|
// If a topic was edited, we re-render the whole view to get any
|
|
|
|
// propagated edits to be updated (since the topic edits can have
|
|
|
|
// changed the correct grouping of messages).
|
|
|
|
if (topic_edited) {
|
2018-04-27 17:37:09 +02:00
|
|
|
home_msg_list.update_muting_and_rerender();
|
2017-03-19 20:23:48 +01:00
|
|
|
// However, we don't need to rerender message_list.narrowed if
|
|
|
|
// we just changed the narrow earlier in this function.
|
|
|
|
if (!changed_narrow && current_msg_list === message_list.narrowed) {
|
2018-04-27 17:37:09 +02:00
|
|
|
message_list.narrowed.update_muting_and_rerender();
|
2017-03-19 20:23:48 +01:00
|
|
|
}
|
|
|
|
} else {
|
2017-04-21 20:27:45 +02:00
|
|
|
// If the content of the message was edited, we do a special animation.
|
|
|
|
current_msg_list.view.rerender_messages(msgs_to_rerender, message_content_edited);
|
2017-03-19 20:23:48 +01:00
|
|
|
if (current_msg_list === message_list.narrowed) {
|
2017-04-21 20:27:45 +02:00
|
|
|
home_msg_list.view.rerender_messages(msgs_to_rerender);
|
2017-03-19 20:23:48 +01:00
|
|
|
}
|
|
|
|
}
|
2018-04-14 01:29:21 +02:00
|
|
|
|
|
|
|
if (changed_compose) {
|
|
|
|
// We need to do this after we rerender the message list, to
|
|
|
|
// produce correct results.
|
|
|
|
compose_fade.update_message_list();
|
|
|
|
}
|
|
|
|
|
2017-03-19 20:23:48 +01:00
|
|
|
unread_ui.update_unread_counts();
|
|
|
|
stream_list.update_streams_sidebar();
|
|
|
|
pm_list.update_private_messages();
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return exports;
|
|
|
|
|
|
|
|
}());
|
|
|
|
if (typeof module !== 'undefined') {
|
|
|
|
module.exports = message_events;
|
|
|
|
}
|
2018-05-28 08:04:36 +02:00
|
|
|
window.message_events = message_events;
|