2014-01-31 16:27:24 +01:00
|
|
|
var message_store = (function () {
|
|
|
|
|
|
|
|
var exports = {};
|
2014-01-31 22:57:54 +01:00
|
|
|
var stored_messages = {};
|
2014-01-31 16:27:24 +01:00
|
|
|
|
|
|
|
var load_more_enabled = true;
|
|
|
|
// If the browser hasn't scrolled away from the top of the page
|
|
|
|
// since the last time that we ran load_more_messages(), we do
|
|
|
|
// not load_more_messages().
|
|
|
|
|
2015-11-25 18:41:32 +01:00
|
|
|
exports.recent_private_messages = [];
|
2014-05-06 06:55:33 +02:00
|
|
|
|
2014-01-31 22:02:57 +01:00
|
|
|
exports.get = function get(message_id) {
|
2014-01-31 22:57:54 +01:00
|
|
|
return stored_messages[message_id];
|
2014-01-31 22:02:57 +01:00
|
|
|
};
|
|
|
|
|
2016-05-25 13:53:23 +02:00
|
|
|
exports.get_private_message_recipient = function (message, attr, fallback_attr) {
|
2016-12-02 17:09:31 +01:00
|
|
|
var recipient;
|
|
|
|
var i;
|
2016-05-25 13:53:23 +02:00
|
|
|
var other_recipients = _.filter(message.display_recipient,
|
|
|
|
function (element) {
|
2016-06-08 05:54:07 +02:00
|
|
|
return !util.is_current_user(element.email);
|
2016-05-25 13:53:23 +02:00
|
|
|
});
|
|
|
|
if (other_recipients.length === 0) {
|
|
|
|
// private message with oneself
|
|
|
|
return message.display_recipient[0][attr];
|
|
|
|
}
|
|
|
|
|
|
|
|
recipient = other_recipients[0][attr];
|
|
|
|
if (recipient === undefined && fallback_attr !== undefined) {
|
|
|
|
recipient = other_recipients[0][fallback_attr];
|
|
|
|
}
|
2016-11-30 19:05:04 +01:00
|
|
|
for (i = 1; i < other_recipients.length; i += 1) {
|
2016-05-25 13:53:23 +02:00
|
|
|
var attr_value = other_recipients[i][attr];
|
|
|
|
if (attr_value === undefined && fallback_attr !== undefined) {
|
|
|
|
attr_value = other_recipients[i][fallback_attr];
|
|
|
|
}
|
|
|
|
recipient += ', ' + attr_value;
|
|
|
|
}
|
|
|
|
return recipient;
|
|
|
|
};
|
|
|
|
|
2016-12-02 15:16:33 +01:00
|
|
|
exports.process_message_for_recent_private_messages =
|
|
|
|
function process_message_for_recent_private_messages(message, remove_message) {
|
2015-11-25 18:41:32 +01:00
|
|
|
var current_timestamp = 0;
|
|
|
|
|
2016-11-18 15:48:53 +01:00
|
|
|
var user_ids_string = people.emails_strings_to_user_ids_string(message.reply_to);
|
|
|
|
|
|
|
|
if (!user_ids_string) {
|
|
|
|
blueslip.warn('Unknown reply_to in message: ' + user_ids_string);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-11-25 18:41:32 +01:00
|
|
|
// If this conversation is already tracked, we'll replace with new timestamp,
|
|
|
|
// so remove it from the current list.
|
2016-12-02 15:16:33 +01:00
|
|
|
exports.recent_private_messages = _.filter(exports.recent_private_messages,
|
|
|
|
function (recent_pm) {
|
2016-11-18 15:48:53 +01:00
|
|
|
return recent_pm.user_ids_string !== user_ids_string;
|
2015-11-25 18:41:32 +01:00
|
|
|
});
|
|
|
|
|
2016-11-18 15:48:53 +01:00
|
|
|
var new_conversation = {user_ids_string: user_ids_string,
|
2015-11-25 18:41:32 +01:00
|
|
|
display_reply_to: message.display_reply_to,
|
|
|
|
timestamp: Math.max(message.timestamp, current_timestamp)};
|
|
|
|
|
|
|
|
exports.recent_private_messages.push(new_conversation);
|
|
|
|
exports.recent_private_messages.sort(function (a, b) {
|
|
|
|
return b.timestamp - a.timestamp;
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-01-31 16:27:24 +01:00
|
|
|
function set_topic_edit_properties(message) {
|
|
|
|
message.always_visible_topic_edit = false;
|
|
|
|
message.on_hover_topic_edit = false;
|
2016-06-21 21:34:41 +02:00
|
|
|
if (!page_params.realm_allow_message_editing) {
|
2014-01-31 16:27:24 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Messages with no topics should always have an edit icon visible
|
|
|
|
// to encourage updating them. Admins can also edit any topic.
|
2016-08-27 04:47:53 +02:00
|
|
|
if (message.subject === compose.empty_topic_placeholder()) {
|
2014-01-31 16:27:24 +01:00
|
|
|
message.always_visible_topic_edit = true;
|
|
|
|
} else if (page_params.is_admin) {
|
|
|
|
message.on_hover_topic_edit = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function add_message_metadata(message) {
|
2014-02-03 19:31:18 +01:00
|
|
|
var cached_msg = stored_messages[message.id];
|
2014-01-31 16:27:24 +01:00
|
|
|
if (cached_msg !== undefined) {
|
|
|
|
// Copy the match subject and content over if they exist on
|
|
|
|
// the new message
|
|
|
|
if (message.match_subject !== undefined) {
|
|
|
|
cached_msg.match_subject = message.match_subject;
|
|
|
|
cached_msg.match_content = message.match_content;
|
|
|
|
}
|
|
|
|
return cached_msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
var involved_people;
|
|
|
|
|
2016-06-08 05:54:07 +02:00
|
|
|
message.sent_by_me = util.is_current_user(message.sender_email);
|
2014-01-31 16:27:24 +01:00
|
|
|
|
|
|
|
message.flags = message.flags || [];
|
|
|
|
message.historical = (message.flags !== undefined &&
|
|
|
|
message.flags.indexOf('historical') !== -1);
|
|
|
|
message.starred = message.flags.indexOf("starred") !== -1;
|
|
|
|
message.mentioned = message.flags.indexOf("mentioned") !== -1 ||
|
|
|
|
message.flags.indexOf("wildcard_mentioned") !== -1;
|
2016-08-30 10:51:50 +02:00
|
|
|
message.mentioned_me_directly = message.flags.indexOf("mentioned") !== -1;
|
2014-01-31 16:27:24 +01:00
|
|
|
message.collapsed = message.flags.indexOf("collapsed") !== -1;
|
|
|
|
message.alerted = message.flags.indexOf("has_alert_word") !== -1;
|
|
|
|
message.is_me_message = message.flags.indexOf("is_me_message") !== -1;
|
|
|
|
|
|
|
|
switch (message.type) {
|
|
|
|
case 'stream':
|
|
|
|
message.is_stream = true;
|
|
|
|
message.stream = message.display_recipient;
|
|
|
|
composebox_typeahead.add_topic(message.stream, message.subject);
|
|
|
|
message.reply_to = message.sender_email;
|
|
|
|
|
2016-10-28 17:31:18 +02:00
|
|
|
stream_data.process_message_for_recent_topics(message);
|
2014-01-31 16:27:24 +01:00
|
|
|
|
|
|
|
involved_people = [{'full_name': message.sender_full_name,
|
2016-11-03 22:25:18 +01:00
|
|
|
'user_id': message.sender_id,
|
2014-01-31 16:27:24 +01:00
|
|
|
'email': message.sender_email}];
|
|
|
|
set_topic_edit_properties(message);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'private':
|
|
|
|
message.is_private = true;
|
|
|
|
message.reply_to = util.normalize_recipients(
|
2016-05-25 13:53:23 +02:00
|
|
|
exports.get_private_message_recipient(message, 'email'));
|
|
|
|
message.display_reply_to = exports.get_private_message_recipient(message, 'full_name', 'email');
|
2014-01-31 16:27:24 +01:00
|
|
|
|
2015-11-25 18:41:32 +01:00
|
|
|
exports.process_message_for_recent_private_messages(message);
|
2014-01-31 16:27:24 +01:00
|
|
|
involved_people = message.display_recipient;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add new people involved in this message to the people list
|
|
|
|
_.each(involved_people, function (person) {
|
2016-11-03 20:12:38 +01:00
|
|
|
if (!person.unknown_local_echo_user) {
|
|
|
|
if (! people.get_by_email(person.email)) {
|
2016-11-03 22:25:18 +01:00
|
|
|
people.add({
|
|
|
|
email: person.email,
|
|
|
|
user_id: person.user_id || person.id,
|
|
|
|
full_name: person.full_name,
|
|
|
|
is_admin: person.is_realm_admin || false,
|
|
|
|
is_bot: person.is_bot || false
|
|
|
|
});
|
2016-11-03 20:12:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (message.type === 'private' && message.sent_by_me) {
|
|
|
|
// Track the number of PMs we've sent to this person to improve autocomplete
|
|
|
|
people.incr_recipient_count(person.email);
|
|
|
|
}
|
2014-01-31 16:27:24 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
alert_words.process_message(message);
|
2014-01-31 22:57:54 +01:00
|
|
|
stored_messages[message.id] = message;
|
2014-01-31 16:27:24 +01:00
|
|
|
return message;
|
|
|
|
}
|
|
|
|
|
|
|
|
exports.add_messages = function add_messages(messages, msg_list, opts) {
|
|
|
|
if (!messages) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
opts = _.extend({messages_are_new: false, delay_render: false}, opts);
|
|
|
|
|
2014-03-13 15:03:01 +01:00
|
|
|
loading.destroy_indicator($('#page_loading_indicator'));
|
2014-03-13 16:47:25 +01:00
|
|
|
$('#first_run_message').remove();
|
2014-01-31 16:27:24 +01:00
|
|
|
|
|
|
|
msg_list.add_messages(messages, opts);
|
|
|
|
|
|
|
|
if (msg_list === home_msg_list && opts.messages_are_new) {
|
|
|
|
_.each(messages, function (message) {
|
|
|
|
if (message.local_id === undefined) {
|
2014-01-31 22:08:58 +01:00
|
|
|
compose.report_as_received(message);
|
2014-01-31 16:27:24 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
function maybe_add_narrowed_messages(messages, msg_list, messages_are_new) {
|
|
|
|
var ids = [];
|
|
|
|
_.each(messages, function (elem) {
|
|
|
|
ids.push(elem.id);
|
|
|
|
});
|
|
|
|
|
|
|
|
channel.post({
|
|
|
|
url: '/json/messages_in_narrow',
|
|
|
|
idempotent: true,
|
|
|
|
data: {msg_ids: JSON.stringify(ids),
|
2014-02-11 17:22:23 +01:00
|
|
|
narrow: JSON.stringify(narrow.public_operators())},
|
2014-01-31 16:27:24 +01:00
|
|
|
timeout: 5000,
|
|
|
|
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)) {
|
|
|
|
elem.match_subject = data.messages[elem.id].match_subject;
|
|
|
|
elem.match_content = data.messages[elem.id].match_content;
|
|
|
|
new_messages.push(elem);
|
|
|
|
} else {
|
|
|
|
elsewhere_messages.push(elem);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
new_messages = _.map(new_messages, add_message_metadata);
|
|
|
|
exports.add_messages(new_messages, msg_list, {messages_are_new: messages_are_new});
|
|
|
|
unread.process_visible();
|
|
|
|
notifications.possibly_notify_new_messages_outside_viewport(new_messages);
|
|
|
|
notifications.notify_messages_outside_current_search(elsewhere_messages);
|
|
|
|
},
|
|
|
|
error: function (xhr) {
|
|
|
|
// 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
|
|
|
|
maybe_add_narrowed_messages(messages, msg_list, messages_are_new);
|
|
|
|
}
|
|
|
|
}, 5000);
|
|
|
|
}});
|
|
|
|
}
|
|
|
|
|
|
|
|
exports.update_messages = function update_messages(events) {
|
2014-02-11 16:19:42 +01:00
|
|
|
var msgs_to_rerender = [];
|
2014-02-26 18:37:30 +01:00
|
|
|
var topic_edited = false;
|
2014-03-11 19:52:39 +01:00
|
|
|
var changed_narrow = false;
|
2014-02-11 16:19:42 +01:00
|
|
|
|
2014-01-31 16:27:24 +01:00
|
|
|
_.each(events, function (event) {
|
2014-02-03 19:31:18 +01:00
|
|
|
var msg = stored_messages[event.message_id];
|
2014-01-31 16:27:24 +01:00
|
|
|
if (msg === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
2014-02-11 16:19:42 +01:00
|
|
|
msgs_to_rerender.push(msg);
|
2014-01-31 16:27:24 +01:00
|
|
|
|
|
|
|
msg.alerted = event.flags.indexOf("has_alert_word") !== -1;
|
|
|
|
msg.mentioned = event.flags.indexOf("mentioned") !== -1 ||
|
|
|
|
event.flags.indexOf("wildcard_mentioned") !== -1;
|
|
|
|
|
2014-03-13 20:22:09 +01:00
|
|
|
condense.un_cache_message_content_height(msg.id);
|
2014-01-31 16:27:24 +01:00
|
|
|
|
|
|
|
if (event.rendered_content !== undefined) {
|
|
|
|
msg.content = event.rendered_content;
|
|
|
|
}
|
|
|
|
|
2014-03-12 14:37:58 +01:00
|
|
|
var row = current_msg_list.get_row(event.message_id);
|
|
|
|
if (row.length > 0) {
|
|
|
|
message_edit.end(row);
|
|
|
|
}
|
|
|
|
|
2014-01-31 16:27:24 +01:00
|
|
|
if (event.subject !== undefined) {
|
|
|
|
// 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.
|
2014-02-26 18:37:30 +01:00
|
|
|
topic_edited = true;
|
For topic changes, set the compose subject outside the loop.
If we get a topic change, we can change the subject outside the
loop, since we are passed in event.orig_subject. Doing it inside
the loop was mostly harmless, since after you encountered the first
message with the old topic, the condition to change the subject
evaluated to false, but it was still technically O(N), and it was
kind of confusing.
This commit changes behavior in the edge case that you have the
compose box open for a changing subject, but you are in a narrow
that does not have any of the affected messages. After this commit,
the topic in the compose box will still change, which I believe
is the correct behavior.
(imported from commit 2363e432ebe7ae8e07379324ee0bfb52051428e6)
2014-03-11 14:07:23 +01:00
|
|
|
|
2014-03-11 19:52:39 +01:00
|
|
|
var going_forward_change = _.indexOf(['change_later', 'change_all'], event.propagate_mode) >= 0;
|
|
|
|
|
2014-03-11 19:13:27 +01:00
|
|
|
var stream_name = stream_data.get_sub_by_id(event.stream_id).name;
|
|
|
|
var compose_stream_name = compose.stream_name();
|
|
|
|
|
2014-03-12 14:53:39 +01:00
|
|
|
if (going_forward_change && stream_name && compose_stream_name) {
|
2014-03-11 19:13:27 +01:00
|
|
|
if (stream_name.toLowerCase() === compose_stream_name.toLowerCase()) {
|
|
|
|
if (event.orig_subject === compose.subject()) {
|
|
|
|
compose.subject(event.subject);
|
|
|
|
}
|
|
|
|
}
|
For topic changes, set the compose subject outside the loop.
If we get a topic change, we can change the subject outside the
loop, since we are passed in event.orig_subject. Doing it inside
the loop was mostly harmless, since after you encountered the first
message with the old topic, the condition to change the subject
evaluated to false, but it was still technically O(N), and it was
kind of confusing.
This commit changes behavior in the edge case that you have the
compose box open for a changing subject, but you are in a narrow
that does not have any of the affected messages. After this commit,
the topic in the compose box will still change, which I believe
is the correct behavior.
(imported from commit 2363e432ebe7ae8e07379324ee0bfb52051428e6)
2014-03-11 14:07:23 +01:00
|
|
|
}
|
|
|
|
|
2014-03-11 19:52:39 +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) {
|
|
|
|
var current_filter = narrow.filter();
|
|
|
|
if (current_filter && stream_name) {
|
|
|
|
if (current_filter.has_topic(stream_name, event.orig_subject)) {
|
|
|
|
var new_filter = current_filter.filter_with_new_topic(event.subject);
|
|
|
|
var operators = new_filter.operators();
|
|
|
|
var opts = {
|
|
|
|
trigger: 'topic change',
|
|
|
|
then_select_id: current_id
|
|
|
|
};
|
|
|
|
narrow.activate(operators, opts);
|
|
|
|
changed_narrow = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-31 16:27:24 +01:00
|
|
|
_.each(event.message_ids, function (id) {
|
2014-01-31 22:14:57 +01:00
|
|
|
var msg = message_store.get(id);
|
2014-01-31 16:27:24 +01:00
|
|
|
if (msg === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-27 04:32:09 +02:00
|
|
|
// Remove the recent topics entry for the old topics;
|
2014-01-31 16:27:24 +01:00
|
|
|
// must be called before we update msg.subject
|
2016-10-28 17:31:18 +02:00
|
|
|
stream_data.process_message_for_recent_topics(msg, true);
|
2014-01-31 16:27:24 +01:00
|
|
|
// Update the unread counts; again, this must be called
|
|
|
|
// before we update msg.subject
|
2016-08-27 03:29:32 +02:00
|
|
|
unread.update_unread_topics(msg, event);
|
2014-01-31 16:27:24 +01:00
|
|
|
|
|
|
|
msg.subject = event.subject;
|
|
|
|
msg.subject_links = event.subject_links;
|
|
|
|
set_topic_edit_properties(msg);
|
2016-08-27 04:32:09 +02:00
|
|
|
// Add the recent topics entry for the new topics; must
|
2014-01-31 16:27:24 +01:00
|
|
|
// be called after we update msg.subject
|
2016-10-28 17:31:18 +02:00
|
|
|
stream_data.process_message_for_recent_topics(msg);
|
2014-01-31 16:27:24 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
msg.last_edit_timestamp = event.edit_timestamp;
|
|
|
|
delete msg.last_edit_timestr;
|
|
|
|
|
|
|
|
notifications.received_messages([msg]);
|
|
|
|
alert_words.process_message(msg);
|
|
|
|
});
|
|
|
|
|
2014-02-26 18:37:30 +01:00
|
|
|
// If a topic was edited, we re-render the whole view to get any propagated edits
|
|
|
|
// to be updated
|
|
|
|
if (topic_edited) {
|
2014-03-11 19:52:39 +01:00
|
|
|
if (!changed_narrow) {
|
|
|
|
home_msg_list.rerender();
|
2016-04-25 23:45:25 +02:00
|
|
|
if (current_msg_list === message_list.narrowed) {
|
|
|
|
message_list.narrowed.rerender();
|
2014-03-11 19:52:39 +01:00
|
|
|
}
|
2014-02-26 18:37:30 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
home_msg_list.view.rerender_messages(msgs_to_rerender);
|
2016-04-25 23:45:25 +02:00
|
|
|
if (current_msg_list === message_list.narrowed) {
|
|
|
|
message_list.narrowed.view.rerender_messages(msgs_to_rerender);
|
2014-02-26 18:37:30 +01:00
|
|
|
}
|
2014-01-31 16:27:24 +01:00
|
|
|
}
|
|
|
|
unread.update_unread_counts();
|
|
|
|
stream_list.update_streams_sidebar();
|
2016-11-11 12:33:51 +01:00
|
|
|
pm_list.update_private_messages();
|
2014-01-31 16:27:24 +01:00
|
|
|
};
|
|
|
|
|
2016-11-05 19:04:29 +01:00
|
|
|
|
|
|
|
// This function could probably benefit from some refactoring
|
|
|
|
exports.do_unread_count_updates = function do_unread_count_updates(messages) {
|
|
|
|
unread.process_loaded_messages(messages);
|
|
|
|
unread.update_unread_counts();
|
|
|
|
resize.resize_page_components();
|
|
|
|
};
|
|
|
|
|
2014-01-31 16:27:24 +01:00
|
|
|
exports.insert_new_messages = function insert_new_messages(messages) {
|
|
|
|
messages = _.map(messages, add_message_metadata);
|
|
|
|
|
|
|
|
// You must add add messages to home_msg_list BEFORE
|
|
|
|
// calling unread.process_loaded_messages.
|
|
|
|
exports.add_messages(messages, home_msg_list, {messages_are_new: true});
|
2016-04-21 22:49:23 +02:00
|
|
|
exports.add_messages(messages, message_list.all, {messages_are_new: true});
|
2014-01-31 16:27:24 +01:00
|
|
|
|
|
|
|
if (narrow.active()) {
|
|
|
|
if (narrow.filter().can_apply_locally()) {
|
2016-04-25 23:45:25 +02:00
|
|
|
exports.add_messages(messages, message_list.narrowed, {messages_are_new: true});
|
2014-01-31 16:27:24 +01:00
|
|
|
notifications.possibly_notify_new_messages_outside_viewport(messages);
|
|
|
|
} else {
|
|
|
|
// if we cannot apply locally, we have to wait for this callback to happen to notify
|
2016-04-25 23:45:25 +02:00
|
|
|
maybe_add_narrowed_messages(messages, message_list.narrowed, true);
|
2014-01-31 16:27:24 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
notifications.possibly_notify_new_messages_outside_viewport(messages);
|
|
|
|
}
|
|
|
|
|
2016-11-11 18:27:53 +01:00
|
|
|
activity.process_loaded_messages(messages);
|
2016-11-05 19:04:29 +01:00
|
|
|
exports.do_unread_count_updates(messages);
|
2014-01-31 16:27:24 +01:00
|
|
|
|
|
|
|
if (narrow.narrowed_by_reply()) {
|
|
|
|
// 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.
|
2016-11-30 19:05:04 +01:00
|
|
|
for (i = messages.length-1; i>=0; i -= 1) {
|
2014-01-31 16:27:24 +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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unread.process_visible();
|
|
|
|
notifications.received_messages(messages);
|
|
|
|
stream_list.update_streams_sidebar();
|
2016-11-11 12:33:51 +01:00
|
|
|
pm_list.update_private_messages();
|
2014-01-31 16:27:24 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
function process_result(messages, opts) {
|
|
|
|
$('#get_old_messages_error').hide();
|
|
|
|
|
2016-04-25 23:45:25 +02:00
|
|
|
if ((messages.length === 0) && (current_msg_list === message_list.narrowed) &&
|
|
|
|
message_list.narrowed.empty()) {
|
2014-01-31 16:27:24 +01:00
|
|
|
// Even after trying to load more messages, we have no
|
|
|
|
// messages to display in this narrow.
|
|
|
|
narrow.show_empty_narrow_message();
|
|
|
|
}
|
|
|
|
|
|
|
|
messages = _.map(messages, add_message_metadata);
|
|
|
|
|
|
|
|
// If we're loading more messages into the home view, save them to
|
2016-04-21 22:49:23 +02:00
|
|
|
// the message_list.all as well, as the home_msg_list is reconstructed
|
|
|
|
// from message_list.all.
|
2014-01-31 16:27:24 +01:00
|
|
|
if (opts.msg_list === home_msg_list) {
|
2016-11-05 19:04:29 +01:00
|
|
|
exports.do_unread_count_updates(messages);
|
2016-04-21 22:49:23 +02:00
|
|
|
exports.add_messages(messages, message_list.all, {messages_are_new: false});
|
2014-01-31 16:27:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (messages.length !== 0 && !opts.cont_will_add_messages) {
|
|
|
|
exports.add_messages(messages, opts.msg_list, {messages_are_new: false});
|
|
|
|
}
|
|
|
|
|
2016-11-11 18:27:53 +01:00
|
|
|
activity.process_loaded_messages(messages);
|
2014-01-31 16:27:24 +01:00
|
|
|
stream_list.update_streams_sidebar();
|
2016-11-11 12:33:51 +01:00
|
|
|
pm_list.update_private_messages();
|
2014-01-31 16:27:24 +01:00
|
|
|
|
|
|
|
if (opts.cont !== undefined) {
|
|
|
|
opts.cont(messages);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function get_old_messages_success(data, opts) {
|
|
|
|
if (tutorial.is_running()) {
|
|
|
|
// Don't actually process the messages until the tutorial is
|
|
|
|
// finished, but do disable the loading indicator so it isn't
|
|
|
|
// distracting in the background
|
2014-03-13 15:03:01 +01:00
|
|
|
loading.destroy_indicator($('#page_loading_indicator'));
|
2014-01-31 16:27:24 +01:00
|
|
|
tutorial.defer(function () { get_old_messages_success(data, opts); });
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opts.msg_list.narrowed && opts.msg_list !== current_msg_list) {
|
|
|
|
// We unnarrowed before receiving new messages so
|
|
|
|
// don't bother processing the newly arrived messages.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (! data) {
|
|
|
|
// The server occationally returns no data during a
|
|
|
|
// restart. Ignore those responses and try again
|
|
|
|
setTimeout(function () {
|
|
|
|
exports.load_old_messages(opts);
|
|
|
|
}, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
process_result(data.messages, opts);
|
2014-03-13 19:03:31 +01:00
|
|
|
resize.resize_bottom_whitespace();
|
2014-01-31 16:27:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
exports.load_old_messages = function load_old_messages(opts) {
|
|
|
|
opts = _.extend({cont_will_add_messages: false}, opts);
|
|
|
|
|
|
|
|
var data = {anchor: opts.anchor,
|
|
|
|
num_before: opts.num_before,
|
|
|
|
num_after: opts.num_after};
|
|
|
|
|
|
|
|
if (opts.msg_list.narrowed && narrow.active()) {
|
|
|
|
var operators = narrow.public_operators();
|
|
|
|
if (page_params.narrow !== undefined) {
|
|
|
|
operators = operators.concat(page_params.narrow);
|
|
|
|
}
|
2014-02-11 17:22:23 +01:00
|
|
|
data.narrow = JSON.stringify(operators);
|
2014-01-31 16:27:24 +01:00
|
|
|
}
|
|
|
|
if (opts.msg_list === home_msg_list && page_params.narrow_stream !== undefined) {
|
2014-02-11 17:22:23 +01:00
|
|
|
data.narrow = JSON.stringify(page_params.narrow);
|
2014-01-31 16:27:24 +01:00
|
|
|
}
|
2014-01-31 20:19:12 +01:00
|
|
|
if (opts.use_first_unread_anchor) {
|
|
|
|
data.use_first_unread_anchor = true;
|
|
|
|
}
|
2014-01-31 16:27:24 +01:00
|
|
|
|
2016-04-01 21:55:17 +02:00
|
|
|
channel.get({
|
|
|
|
url: '/json/messages',
|
2014-01-31 16:27:24 +01:00
|
|
|
data: data,
|
|
|
|
idempotent: true,
|
|
|
|
success: function (data) {
|
|
|
|
get_old_messages_success(data, opts);
|
|
|
|
},
|
|
|
|
error: function (xhr, error_type, exn) {
|
|
|
|
if (opts.msg_list.narrowed && opts.msg_list !== current_msg_list) {
|
|
|
|
// We unnarrowed before getting an error so don't
|
|
|
|
// bother trying again or doing further processing.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (xhr.status === 400) {
|
|
|
|
// Bad request: We probably specified a narrow operator
|
|
|
|
// for a nonexistent stream or something. We shouldn't
|
|
|
|
// retry or display a connection error.
|
|
|
|
//
|
|
|
|
// FIXME: Warn the user when this has happened?
|
|
|
|
process_result([], opts);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We might want to be more clever here
|
|
|
|
$('#get_old_messages_error').show();
|
|
|
|
setTimeout(function () {
|
|
|
|
exports.load_old_messages(opts);
|
|
|
|
}, 5000);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.reset_load_more_status = function reset_load_more_status() {
|
|
|
|
load_more_enabled = true;
|
2016-04-10 14:15:47 +02:00
|
|
|
ui.have_scrolled_away_from_top = true;
|
2014-01-31 16:27:24 +01:00
|
|
|
ui.hide_loading_more_messages_indicator();
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.load_more_messages = function load_more_messages(msg_list) {
|
|
|
|
var batch_size = 100;
|
|
|
|
var oldest_message_id;
|
|
|
|
if (!load_more_enabled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ui.show_loading_more_messages_indicator();
|
|
|
|
load_more_enabled = false;
|
|
|
|
if (msg_list.first() === undefined) {
|
|
|
|
oldest_message_id = page_params.initial_pointer;
|
|
|
|
} else {
|
|
|
|
oldest_message_id = msg_list.first().id;
|
|
|
|
}
|
|
|
|
exports.load_old_messages({
|
|
|
|
anchor: oldest_message_id.toFixed(),
|
|
|
|
num_before: batch_size,
|
|
|
|
num_after: 0,
|
|
|
|
msg_list: msg_list,
|
|
|
|
cont: function (messages) {
|
|
|
|
ui.hide_loading_more_messages_indicator();
|
|
|
|
if (messages.length >= batch_size) {
|
|
|
|
load_more_enabled = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-02-05 19:49:18 +01:00
|
|
|
exports.clear = function clear() {
|
|
|
|
this.stored_messages = {};
|
|
|
|
};
|
|
|
|
|
2014-03-07 00:44:15 +01:00
|
|
|
util.execute_early(function () {
|
2014-01-31 16:27:24 +01:00
|
|
|
// get the initial message list
|
|
|
|
function load_more(messages) {
|
|
|
|
|
|
|
|
// If we received the initially selected message, select it on the client side,
|
|
|
|
// but not if the user has already selected another one during load.
|
|
|
|
//
|
|
|
|
// We fall back to the closest selected id, as the user may have removed
|
|
|
|
// a stream from the home before already
|
|
|
|
if (home_msg_list.selected_id() === -1 && !home_msg_list.empty()) {
|
|
|
|
home_msg_list.select_id(page_params.initial_pointer,
|
2014-02-04 22:02:26 +01:00
|
|
|
{then_scroll: true, use_closest: true,
|
|
|
|
target_scroll_offset: page_params.initial_offset});
|
2014-01-31 16:27:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// catch the user up
|
|
|
|
if (messages.length !== 0) {
|
|
|
|
var latest_id = messages[messages.length-1].id;
|
|
|
|
if (latest_id < page_params.max_message_id) {
|
|
|
|
exports.load_old_messages({
|
|
|
|
anchor: latest_id.toFixed(),
|
|
|
|
num_before: 0,
|
|
|
|
num_after: 400,
|
|
|
|
msg_list: home_msg_list,
|
|
|
|
cont: load_more
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
server_events.home_view_loaded();
|
|
|
|
|
|
|
|
// backfill more messages after the user is idle
|
|
|
|
var backfill_batch_size = 1000;
|
|
|
|
$(document).idle({'idle': 1000*10,
|
|
|
|
'onIdle': function () {
|
2016-04-21 22:49:23 +02:00
|
|
|
var first_id = message_list.all.first().id;
|
2014-01-31 16:27:24 +01:00
|
|
|
exports.load_old_messages({
|
|
|
|
anchor: first_id,
|
|
|
|
num_before: backfill_batch_size,
|
|
|
|
num_after: 0,
|
|
|
|
msg_list: home_msg_list
|
|
|
|
});
|
|
|
|
}});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (page_params.have_initial_messages) {
|
|
|
|
exports.load_old_messages({
|
|
|
|
anchor: page_params.initial_pointer,
|
|
|
|
num_before: 200,
|
|
|
|
num_after: 200,
|
|
|
|
msg_list: home_msg_list,
|
|
|
|
cont: load_more
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
server_events.home_view_loaded();
|
|
|
|
}
|
|
|
|
|
|
|
|
$(document).on('message_id_changed', function (event) {
|
2016-12-02 17:09:31 +01:00
|
|
|
var old_id = event.old_id;
|
|
|
|
var new_id = event.new_id;
|
2016-04-12 17:38:47 +02:00
|
|
|
if (pointer.furthest_read === old_id) {
|
|
|
|
pointer.furthest_read = new_id;
|
2014-01-31 16:27:24 +01:00
|
|
|
}
|
2014-01-31 22:57:54 +01:00
|
|
|
if (stored_messages[old_id]) {
|
|
|
|
stored_messages[new_id] = stored_messages[old_id];
|
|
|
|
delete stored_messages[old_id];
|
2014-01-31 16:27:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// This handler cannot be in the MessageList constructor, which is the logical place
|
|
|
|
// If it's there, the event handler creates a closure with a reference to the message
|
|
|
|
// list itself. When narrowing, the old narrow message list is discarded and a new one
|
|
|
|
// created, but due to the closure, the old list is not garbage collected. This also leads
|
|
|
|
// to the old list receiving the change id events, and throwing errors as it does not
|
|
|
|
// have the messages that you would expect in its internal data structures.
|
2016-04-25 23:45:25 +02:00
|
|
|
_.each([message_list.all, home_msg_list, message_list.narrowed], function (msg_list) {
|
2014-01-31 16:27:24 +01:00
|
|
|
if (msg_list !== undefined) {
|
|
|
|
msg_list.change_message_id(old_id, new_id);
|
|
|
|
|
|
|
|
if (msg_list.view !== undefined) {
|
|
|
|
msg_list.view.change_message_id(old_id, new_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-11-18 19:08:41 +01:00
|
|
|
// This is for testing.
|
|
|
|
exports._add_message_metadata = add_message_metadata;
|
|
|
|
|
2014-01-31 16:27:24 +01:00
|
|
|
return exports;
|
|
|
|
|
|
|
|
}());
|
|
|
|
if (typeof module !== 'undefined') {
|
|
|
|
module.exports = message_store;
|
|
|
|
}
|