2013-04-05 23:52:13 +02:00
|
|
|
// zephyr/lib/minify.py will look for this comment in order to tell when it's
|
|
|
|
// producing app.js:
|
|
|
|
//
|
|
|
|
// MINIFY-FILE-ID: zephyr.js
|
|
|
|
|
2013-02-26 23:30:13 +01:00
|
|
|
var all_msg_list = new MessageList();
|
2013-04-25 21:13:06 +02:00
|
|
|
var home_msg_list = new MessageList('zhome', new narrow.Filter([["in", "home"]]));
|
2013-02-12 20:01:24 +01:00
|
|
|
var narrowed_msg_list;
|
2013-02-22 20:48:31 +01:00
|
|
|
var current_msg_list = home_msg_list;
|
2012-10-12 19:14:59 +02:00
|
|
|
var subject_dict = {};
|
2013-01-14 22:18:30 +01:00
|
|
|
var people_dict = {};
|
2013-04-10 22:24:15 +02:00
|
|
|
var recent_subjects = {};
|
2012-09-22 01:11:54 +02:00
|
|
|
|
2013-03-04 23:44:07 +01:00
|
|
|
var queued_mark_as_read = [];
|
|
|
|
var queued_flag_timer;
|
|
|
|
|
2013-05-16 23:16:15 +02:00
|
|
|
var respond_to_cursor = false;
|
|
|
|
|
2012-10-16 22:06:03 +02:00
|
|
|
var get_updates_params = {
|
2013-03-28 18:09:27 +01:00
|
|
|
pointer: -1
|
2012-10-17 23:45:03 +02:00
|
|
|
};
|
2012-11-27 21:06:17 +01:00
|
|
|
var get_updates_failures = 0;
|
2012-10-17 23:45:03 +02:00
|
|
|
|
2012-11-27 23:17:30 +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().
|
|
|
|
var have_scrolled_away_from_top = true;
|
|
|
|
|
2013-02-12 22:32:14 +01:00
|
|
|
// Toggles re-centering the pointer in the window
|
|
|
|
// when Home is next clicked by the user
|
|
|
|
var recenter_pointer_on_display = false;
|
|
|
|
var suppress_scroll_pointer_update = false;
|
2013-03-28 19:16:48 +01:00
|
|
|
// Includes both scroll and arrow events. Negative means scroll up,
|
|
|
|
// positive means scroll down.
|
|
|
|
var last_viewport_movement_direction = 1;
|
2013-02-12 22:32:14 +01:00
|
|
|
|
2013-02-20 20:49:49 +01:00
|
|
|
var furthest_read = -1;
|
|
|
|
var server_furthest_read = -1;
|
2013-03-13 21:53:36 +01:00
|
|
|
var pointer_update_in_flight = false;
|
2013-02-20 20:49:49 +01:00
|
|
|
|
2013-01-14 22:18:30 +01:00
|
|
|
function add_person(person) {
|
2013-03-25 23:26:14 +01:00
|
|
|
page_params.people_list.push(person);
|
2013-01-14 22:18:30 +01:00
|
|
|
people_dict[person.email] = person;
|
|
|
|
}
|
|
|
|
|
2013-03-29 18:22:23 +01:00
|
|
|
function remove_person(person) {
|
|
|
|
var i;
|
|
|
|
for (i = 0; i < page_params.people_list.length; i++) {
|
|
|
|
if (page_params.people_list[i].email === person.email) {
|
|
|
|
page_params.people_list.splice(i, 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delete people_dict[person.email];
|
|
|
|
}
|
|
|
|
|
2013-01-14 22:18:30 +01:00
|
|
|
$(function () {
|
2013-03-25 23:26:14 +01:00
|
|
|
$.each(page_params.people_list, function (idx, person) {
|
2013-01-14 22:18:30 +01:00
|
|
|
people_dict[person.email] = person;
|
|
|
|
});
|
2013-02-23 18:49:23 +01:00
|
|
|
// The special account feedback@humbughq.com is used for in-app
|
|
|
|
// feedback and should always show up as an autocomplete option.
|
|
|
|
typeahead_helper.update_your_recipients([{"email": "feedback@humbughq.com",
|
|
|
|
"full_name": "Humbug Feedback Bot"}]);
|
2013-04-03 22:00:02 +02:00
|
|
|
|
|
|
|
$.each(page_params.initial_presences, function (email, presence) {
|
2013-05-06 17:14:59 +02:00
|
|
|
activity.set_user_status(email, presence, page_params.initial_servertime);
|
2013-04-03 22:00:02 +02:00
|
|
|
});
|
|
|
|
|
2013-01-14 22:18:30 +01:00
|
|
|
});
|
|
|
|
|
2013-05-07 17:13:55 +02:00
|
|
|
function within_viewport(row_offset, row_height) {
|
2013-05-22 18:06:40 +02:00
|
|
|
// Returns true if a message is fully within the effectively visible
|
2013-05-20 19:01:35 +02:00
|
|
|
// part of the viewport.
|
2013-05-07 17:13:55 +02:00
|
|
|
var message_top = row_offset.top;
|
|
|
|
var message_bottom = message_top + row_height;
|
2013-06-06 16:10:12 +02:00
|
|
|
var info = viewport.message_viewport_info();
|
2013-05-20 19:01:35 +02:00
|
|
|
var viewport_top = info.visible_top;
|
|
|
|
var viewport_bottom = viewport_top + info.visible_height;
|
2013-03-23 05:09:41 +01:00
|
|
|
return (message_top > viewport_top) && (message_bottom < viewport_bottom);
|
|
|
|
}
|
|
|
|
|
2013-06-05 17:56:43 +02:00
|
|
|
function keep_pointer_in_view() {
|
|
|
|
// See recenter_view() for related logic to keep the pointer onscreen.
|
|
|
|
// This function mostly comes into place for mouse scrollers, and it
|
|
|
|
// keeps the pointer in view. For people who purely scroll with the
|
|
|
|
// mouse, the pointer is kind of meaningless to them, but keyboard
|
|
|
|
// users will occasionally do big mouse scrolls, so this gives them
|
|
|
|
// a pointer reasonably close to the middle of the screen.
|
|
|
|
var candidate;
|
|
|
|
var next_row = current_msg_list.selected_row();
|
|
|
|
|
|
|
|
if (next_row.length === 0)
|
|
|
|
return;
|
|
|
|
|
2013-06-06 16:10:12 +02:00
|
|
|
var info = viewport.message_viewport_info();
|
2013-06-05 17:56:43 +02:00
|
|
|
var top_threshold = info.visible_top + (1/10 * info.visible_height);
|
|
|
|
var bottom_threshold = info.visible_top + (9/10 * info.visible_height);
|
|
|
|
|
|
|
|
function above_view_threshold() {
|
|
|
|
var bottom_offset = next_row.offset().top + next_row.outerHeight(true);
|
|
|
|
return bottom_offset < top_threshold;
|
|
|
|
}
|
|
|
|
|
|
|
|
function below_view_threshold() {
|
|
|
|
return next_row.offset().top > bottom_threshold;
|
|
|
|
}
|
|
|
|
|
|
|
|
function adjust(past_threshold, at_end, advance) {
|
|
|
|
if (!past_threshold(next_row) || at_end())
|
|
|
|
return false; // try other side
|
|
|
|
while (past_threshold(next_row)) {
|
|
|
|
candidate = advance(next_row);
|
|
|
|
if (candidate.length === 0)
|
|
|
|
break;
|
|
|
|
next_row = candidate;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! adjust(above_view_threshold, viewport.at_top, rows.next_visible))
|
|
|
|
adjust(below_view_threshold, viewport.at_bottom, rows.prev_visible);
|
|
|
|
|
|
|
|
current_msg_list.select_id(rows.id(next_row), {from_scroll: true});
|
|
|
|
}
|
|
|
|
|
2013-04-03 19:33:10 +02:00
|
|
|
function recenter_view(message, from_scroll) {
|
2013-06-03 17:04:01 +02:00
|
|
|
// Barnowl-style recentering: if the pointer is too high, move it to
|
2013-06-04 22:08:52 +02:00
|
|
|
// the 1/2 marks. If the pointer is too low, move it to the 1/7 mark.
|
2013-06-03 17:04:01 +02:00
|
|
|
// See keep_pointer_in_view() for related logic to keep the pointer onscreen.
|
2012-10-05 16:31:10 +02:00
|
|
|
|
2013-06-06 16:10:12 +02:00
|
|
|
var viewport_info = viewport.message_viewport_info();
|
2013-06-04 22:08:52 +02:00
|
|
|
var top_threshold = viewport_info.visible_top;
|
2013-05-30 20:39:28 +02:00
|
|
|
|
2013-06-04 22:08:52 +02:00
|
|
|
var bottom_threshold = viewport_info.visible_top + viewport_info.visible_height;
|
2013-03-28 19:16:48 +01:00
|
|
|
|
2013-05-30 20:39:28 +02:00
|
|
|
var message_top = message.offset().top;
|
2013-06-05 17:28:10 +02:00
|
|
|
var message_height = message.outerHeight(true);
|
|
|
|
var message_bottom = message_top + message_height;
|
|
|
|
|
2013-05-30 20:39:28 +02:00
|
|
|
var is_above = message_top < top_threshold;
|
2013-06-04 22:08:52 +02:00
|
|
|
var is_below = message_bottom > bottom_threshold;
|
2013-05-30 20:39:28 +02:00
|
|
|
|
|
|
|
if (from_scroll) {
|
2013-03-28 19:16:48 +01:00
|
|
|
// If the message you're trying to center on is already in view AND
|
|
|
|
// you're already trying to move in the direction of that message,
|
|
|
|
// don't try to recenter. This avoids disorienting jumps when the
|
|
|
|
// pointer has gotten itself outside the threshold (e.g. by
|
|
|
|
// autoscrolling).
|
2013-05-30 20:39:28 +02:00
|
|
|
if (is_above && last_viewport_movement_direction >= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (is_below && last_viewport_movement_direction <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
2013-03-28 19:16:48 +01:00
|
|
|
}
|
|
|
|
|
2013-05-30 20:39:28 +02:00
|
|
|
if (is_above) {
|
2013-06-05 17:28:10 +02:00
|
|
|
viewport.set_message_position(message_top, message_height, viewport_info, 1/2);
|
2013-05-30 20:39:28 +02:00
|
|
|
} else if (is_below) {
|
2013-06-05 17:28:10 +02:00
|
|
|
viewport.set_message_position(message_top, message_height, viewport_info, 1/7);
|
2012-10-05 16:31:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function scroll_to_selected() {
|
2013-02-14 23:48:37 +01:00
|
|
|
var selected_row = current_msg_list.selected_row();
|
|
|
|
if (selected_row && (selected_row.length !== 0))
|
|
|
|
recenter_view(selected_row);
|
2012-09-07 20:44:55 +02:00
|
|
|
}
|
|
|
|
|
2013-02-12 22:32:14 +01:00
|
|
|
function maybe_scroll_to_selected() {
|
|
|
|
// If we have been previously instructed to re-center to the
|
|
|
|
// selected message, then do so
|
|
|
|
if (recenter_pointer_on_display) {
|
|
|
|
scroll_to_selected();
|
|
|
|
recenter_pointer_on_display = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-03 19:49:12 +01:00
|
|
|
function get_private_message_recipient(message, attr) {
|
2012-09-13 22:00:11 +02:00
|
|
|
var recipient, i;
|
2012-10-15 21:57:41 +02:00
|
|
|
var other_recipients = $.grep(message.display_recipient,
|
|
|
|
function (element, index) {
|
2013-03-25 23:26:14 +01:00
|
|
|
return element.email !== page_params.email;
|
2012-10-15 21:57:41 +02:00
|
|
|
});
|
2012-10-17 23:07:46 +02:00
|
|
|
if (other_recipients.length === 0) {
|
2012-12-03 19:49:12 +01:00
|
|
|
// private message with oneself
|
2012-10-27 02:31:46 +02:00
|
|
|
return message.display_recipient[0][attr];
|
2012-10-17 23:07:46 +02:00
|
|
|
}
|
2012-09-13 22:00:11 +02:00
|
|
|
|
2012-10-27 02:31:46 +02:00
|
|
|
recipient = other_recipients[0][attr];
|
2012-10-15 21:57:41 +02:00
|
|
|
for (i = 1; i < other_recipients.length; i++) {
|
2012-10-27 02:31:46 +02:00
|
|
|
recipient += ', ' + other_recipients[i][attr];
|
2012-09-26 21:25:49 +02:00
|
|
|
}
|
|
|
|
return recipient;
|
|
|
|
}
|
|
|
|
|
2013-05-20 23:35:41 +02:00
|
|
|
function respond_to_message(opts) {
|
2012-10-18 20:17:55 +02:00
|
|
|
var message, msg_type;
|
2013-04-05 16:44:46 +02:00
|
|
|
// Before initiating a reply to a message, if there's an
|
|
|
|
// in-progress composition, snapshot it.
|
|
|
|
compose.snapshot_message();
|
|
|
|
|
2013-02-14 23:48:37 +01:00
|
|
|
message = current_msg_list.selected_message();
|
2012-10-18 20:17:55 +02:00
|
|
|
|
2013-02-22 19:07:26 +01:00
|
|
|
if (message === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-10-18 20:17:55 +02:00
|
|
|
var stream = '';
|
|
|
|
var subject = '';
|
2012-10-10 23:09:16 +02:00
|
|
|
if (message.type === "stream") {
|
2013-04-18 17:13:43 +02:00
|
|
|
stream = message.stream;
|
2012-10-18 20:17:55 +02:00
|
|
|
subject = message.subject;
|
2012-10-09 20:18:55 +02:00
|
|
|
}
|
2012-10-18 20:17:55 +02:00
|
|
|
|
2012-11-08 00:38:21 +01:00
|
|
|
var pm_recipient = message.reply_to;
|
2013-05-20 23:35:41 +02:00
|
|
|
if (opts.reply_type === "personal" && message.type === "private") {
|
2012-12-03 19:49:12 +01:00
|
|
|
// reply_to for private messages is everyone involved, so for
|
|
|
|
// personals replies we need to set the the private message
|
|
|
|
// recipient to just the sender
|
2012-11-08 00:38:21 +01:00
|
|
|
pm_recipient = message.sender_email;
|
2012-10-09 15:51:12 +02:00
|
|
|
}
|
2013-05-20 23:35:41 +02:00
|
|
|
if (opts.reply_type === 'personal' || message.type === 'private') {
|
2012-11-08 00:38:21 +01:00
|
|
|
msg_type = 'private';
|
|
|
|
} else {
|
2012-10-18 20:17:55 +02:00
|
|
|
msg_type = message.type;
|
2012-09-12 16:23:55 +02:00
|
|
|
}
|
2012-10-19 21:59:03 +02:00
|
|
|
compose.start(msg_type, {'stream': stream, 'subject': subject,
|
2013-02-13 21:30:17 +01:00
|
|
|
'private_message_recipient': pm_recipient,
|
2013-05-20 23:35:41 +02:00
|
|
|
'replying_to_message': message,
|
|
|
|
'trigger': opts.trigger});
|
2013-05-16 23:16:15 +02:00
|
|
|
|
2012-09-12 16:23:55 +02:00
|
|
|
}
|
|
|
|
|
2013-05-02 02:46:36 +02:00
|
|
|
// Returns messages from the given message list in the specified range, inclusive
|
2013-02-12 20:01:24 +01:00
|
|
|
function message_range(msg_list, start, end) {
|
2013-05-02 02:46:36 +02:00
|
|
|
if (start === -1) {
|
|
|
|
blueslip.error("message_range given a start of -1");
|
2013-02-12 20:41:01 +01:00
|
|
|
}
|
|
|
|
|
2013-05-02 02:46:36 +02:00
|
|
|
var all = msg_list.all();
|
|
|
|
var compare = function (a, b) { return a.id < b; };
|
|
|
|
|
|
|
|
var start_idx = util.lower_bound(all, start, compare);
|
|
|
|
var end_idx = util.lower_bound(all, end, compare);
|
|
|
|
return all.slice(start_idx, end_idx + 1);
|
2013-02-12 20:41:01 +01:00
|
|
|
}
|
|
|
|
|
2013-03-04 23:44:07 +01:00
|
|
|
function send_queued_flags() {
|
|
|
|
if (queued_mark_as_read.length === 0) {
|
|
|
|
return;
|
|
|
|
}
|
2013-02-12 20:41:01 +01:00
|
|
|
|
2013-05-02 21:14:17 +02:00
|
|
|
function on_success(data, status, jqXHR) {
|
|
|
|
if (data === undefined || data.messages === undefined) return;
|
|
|
|
|
|
|
|
queued_mark_as_read = $.grep(queued_mark_as_read, function (message, idx) {
|
|
|
|
return data.messages.indexOf(message) === -1;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-03-04 23:44:07 +01:00
|
|
|
$.ajax({
|
|
|
|
type: 'POST',
|
|
|
|
url: '/json/update_message_flags',
|
|
|
|
data: {messages: JSON.stringify(queued_mark_as_read),
|
|
|
|
op: 'add',
|
|
|
|
flag: 'read'},
|
2013-05-02 21:14:17 +02:00
|
|
|
dataType: 'json',
|
|
|
|
success: on_success});
|
2013-03-04 23:44:07 +01:00
|
|
|
}
|
2013-02-12 22:32:14 +01:00
|
|
|
|
2013-05-14 03:58:07 +02:00
|
|
|
function update_unread_counts() {
|
|
|
|
// Pure computation:
|
2013-05-17 21:32:26 +02:00
|
|
|
var res = unread.get_counts();
|
2013-05-14 03:58:07 +02:00
|
|
|
|
|
|
|
// Side effects from here down:
|
|
|
|
// This updates some DOM elements directly, so try to
|
|
|
|
// avoid excessive calls to this.
|
2013-05-22 18:06:40 +02:00
|
|
|
stream_list.update_dom_with_unread_counts(res);
|
Clean up code for unread counts and notifications.
The core simplification here is that zephyr.js no longer has:
* the global home_unread_messages
* the function unread_in_current_view() [which used the global]
The logic that used to be in zephyr is now in its proper home
of unread.js, which has these changes:
* the structure returned from unread.get_counts() includes
a new member called unread_in_current_view
* there's a helper function unread.num_unread_current_messages()
Deprecating zephyr.unread_in_current_view() affected two callers:
* notifications.update_title_count()
* notifications_bar.update()
The above functions used to call back to zephyr to get counts, but
there was no nice way to enforce that they were getting counts
at the right time in the code flow, because they depended on
functions like process_visible_unread_messages() to orchestrate
updating internal unread counts before pushing out counts to the DOM.
Now both of those function take a parameter with the unread count,
and we then had to change all of their callers appropriately. This
went hand in hand with another goal, which is that we want all the
unread-counts logic to funnel though basically one place, which
is zephyr.update_unread_counts(). So now that function always
calls notifications_bar.update() [NEW] as well as calling into
the modules unread.js, stream_list.js, and notifications.js [OLD].
Adding the call to notifications_bar.update() in update_unread_counts()
made it so that some other places in the code no longer needed to call
notifications_bar.update(), so you'll see some lines of code
removed. There are also cases where notifications.update_title_count()
was called redundantly, since the callers were already reaching
update_unread_counts() via other calls.
Finally, in ui.resizehandler, you'll see a simple case where the call
to notifications_bar.update() is preceded by an explicit call
to unread.get_counts().
(imported from commit ce84b9c8076c1f9bb20a61209913f0cb0dae098c)
2013-06-05 21:04:06 +02:00
|
|
|
notifications.update_title_count(res.unread_in_current_view);
|
|
|
|
notifications_bar.update(res.unread_in_current_view);
|
2013-03-04 23:44:07 +01:00
|
|
|
}
|
|
|
|
|
2013-03-15 20:07:38 +01:00
|
|
|
function mark_all_as_read(cont) {
|
2013-03-04 23:44:07 +01:00
|
|
|
$.each(all_msg_list.all(), function (idx, msg) {
|
|
|
|
msg.flags = msg.flags || [];
|
|
|
|
msg.flags.push('read');
|
|
|
|
});
|
2013-05-17 21:32:26 +02:00
|
|
|
unread.declare_bankruptcy();
|
2013-03-04 23:44:07 +01:00
|
|
|
update_unread_counts();
|
|
|
|
|
|
|
|
$.ajax({
|
|
|
|
type: 'POST',
|
|
|
|
url: '/json/update_message_flags',
|
|
|
|
data: {messages: JSON.stringify([]),
|
|
|
|
all: true,
|
|
|
|
op: 'add',
|
|
|
|
flag: 'read'},
|
2013-03-15 20:07:38 +01:00
|
|
|
dataType: 'json',
|
|
|
|
success: cont});
|
2013-03-04 23:44:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function process_loaded_for_unread(messages) {
|
2013-05-17 21:32:26 +02:00
|
|
|
unread.process_loaded_messages(messages);
|
2013-03-04 23:44:07 +01:00
|
|
|
update_unread_counts();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Takes a list of messages and marks them as read
|
|
|
|
function process_read_messages(messages) {
|
|
|
|
var processed = [];
|
|
|
|
$.each(messages, function (idx, message) {
|
|
|
|
|
|
|
|
message.flags = message.flags || [];
|
|
|
|
message.flags.push('read');
|
|
|
|
processed.push(message.id);
|
2013-05-17 21:32:26 +02:00
|
|
|
unread.process_read_message(message);
|
2013-03-04 23:44:07 +01:00
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
if (processed.length > 0) {
|
|
|
|
queued_mark_as_read = queued_mark_as_read.concat(processed);
|
|
|
|
|
|
|
|
if (queued_flag_timer !== undefined) {
|
|
|
|
clearTimeout(queued_flag_timer);
|
|
|
|
}
|
|
|
|
|
|
|
|
queued_flag_timer = setTimeout(send_queued_flags, 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
update_unread_counts();
|
2013-02-12 20:41:01 +01:00
|
|
|
}
|
|
|
|
|
2013-05-08 21:31:26 +02:00
|
|
|
// If we ever materially change the algorithm for this function, we
|
|
|
|
// may need to update notifications.received_messages as well.
|
2013-05-16 23:16:15 +02:00
|
|
|
function process_visible_unread_messages(update_cursor) {
|
2013-03-04 23:44:07 +01:00
|
|
|
// For any messages visible on the screen, make sure they have been marked
|
|
|
|
// as unread.
|
|
|
|
if (! notifications.window_has_focus()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var selected = current_msg_list.selected_message();
|
2013-06-06 16:10:12 +02:00
|
|
|
var vp = viewport.message_viewport_info();
|
2013-05-16 20:13:42 +02:00
|
|
|
var top = vp.visible_top;
|
|
|
|
var height = vp.visible_height;
|
2013-03-04 23:44:07 +01:00
|
|
|
|
|
|
|
// Being simplistic about this, the smallest message is 30 px high.
|
|
|
|
var selected_row = rows.get(current_msg_list.selected_id(), current_msg_list.table_name);
|
2013-03-20 21:20:31 +01:00
|
|
|
var num_neighbors = Math.floor(height / 30);
|
2013-03-04 23:44:07 +01:00
|
|
|
var candidates = $.merge(selected_row.prevAll("tr.message_row[zid]:lt(" + num_neighbors + ")"),
|
|
|
|
selected_row.nextAll("tr.message_row[zid]:lt(" + num_neighbors + ")"));
|
|
|
|
|
|
|
|
var visible_messages = candidates.filter(function (idx, message) {
|
|
|
|
var row = $(message);
|
2013-05-07 17:13:55 +02:00
|
|
|
var row_offset = row.offset();
|
|
|
|
var row_height = row.height();
|
2013-03-20 21:20:31 +01:00
|
|
|
// Mark very tall messages as read once we've gotten past them
|
2013-05-07 17:13:55 +02:00
|
|
|
return (row_height > height && row_offset.top > top) || within_viewport(row_offset, row_height);
|
2013-03-04 23:44:07 +01:00
|
|
|
});
|
|
|
|
|
2013-05-16 23:16:15 +02:00
|
|
|
if (update_cursor) {
|
|
|
|
//save the state of respond_to_cursor, and reapply it every time we move the cursor
|
|
|
|
var probably_from_sent_message = respond_to_cursor;
|
|
|
|
$.map(visible_messages, function(msg) {
|
|
|
|
if ((current_msg_list.get(rows.id($(msg))).sender_email === page_params.email) &&
|
|
|
|
(current_msg_list.selected_message().id < rows.id($(msg)))) {
|
|
|
|
// every time we move the cursor, we set respond_to_cursor to false. This should only
|
|
|
|
// happen if the user initiated the cursor move, not us, so we reset it when processing
|
|
|
|
// these messages
|
|
|
|
current_msg_list.select_id(rows.id($(msg)), {then_scroll: true,
|
|
|
|
from_scroll: true});
|
|
|
|
respond_to_cursor = probably_from_sent_message;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-03-04 23:44:07 +01:00
|
|
|
var mark_as_read = $.map(visible_messages, function(msg) {
|
|
|
|
var message = current_msg_list.get(rows.id($(msg)));
|
2013-05-17 21:32:26 +02:00
|
|
|
if (! unread.message_unread(message)) {
|
2013-03-04 23:44:07 +01:00
|
|
|
return undefined;
|
|
|
|
} else {
|
|
|
|
return message;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-05-17 21:32:26 +02:00
|
|
|
if (unread.message_unread(selected)) {
|
2013-03-04 23:44:07 +01:00
|
|
|
mark_as_read.push(selected);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mark_as_read.length > 0) {
|
|
|
|
process_read_messages(mark_as_read);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-23 17:58:02 +02:00
|
|
|
function mark_read_between(msg_list, start_id, end_id) {
|
|
|
|
var mark_as_read = [];
|
|
|
|
$.each(message_range(msg_list, start_id, end_id),
|
|
|
|
function (idx, msg) {
|
2013-05-17 21:32:26 +02:00
|
|
|
if (unread.message_unread(msg)) {
|
2013-04-23 17:58:02 +02:00
|
|
|
mark_as_read.push(msg);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
process_read_messages(mark_as_read);
|
|
|
|
}
|
|
|
|
|
2013-02-20 04:19:18 +01:00
|
|
|
function send_pointer_update() {
|
2013-03-13 21:53:36 +01:00
|
|
|
if (!pointer_update_in_flight &&
|
|
|
|
furthest_read > server_furthest_read) {
|
|
|
|
pointer_update_in_flight = true;
|
|
|
|
$.ajax({
|
|
|
|
type: 'POST',
|
|
|
|
url: '/json/update_pointer',
|
|
|
|
data: {pointer: furthest_read},
|
|
|
|
dataType: 'json',
|
|
|
|
success: function () {
|
|
|
|
server_furthest_read = furthest_read;
|
|
|
|
pointer_update_in_flight = false;
|
|
|
|
},
|
|
|
|
error: function () {
|
|
|
|
pointer_update_in_flight = false;
|
|
|
|
}
|
|
|
|
});
|
2013-02-20 04:19:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-20 20:49:49 +01:00
|
|
|
$(function () {
|
2013-03-25 23:26:14 +01:00
|
|
|
furthest_read = page_params.initial_pointer;
|
|
|
|
server_furthest_read = page_params.initial_pointer;
|
2012-09-06 19:32:02 +02:00
|
|
|
|
2013-02-20 20:49:49 +01:00
|
|
|
// We only send pointer updates when the user has been idle for a
|
|
|
|
// short while to avoid hammering the server
|
|
|
|
$(document).idle({idle: 1000,
|
|
|
|
onIdle: send_pointer_update,
|
|
|
|
keepTracking: true});
|
2012-10-23 22:48:20 +02:00
|
|
|
|
2013-02-20 20:49:49 +01:00
|
|
|
$(document).on('message_selected.zephyr', function (event) {
|
2013-05-16 23:16:15 +02:00
|
|
|
|
2013-02-20 20:49:49 +01:00
|
|
|
// Narrowing is a temporary view on top of the home view and
|
|
|
|
// doesn't affect your pointer in the home view.
|
2013-02-22 20:48:31 +01:00
|
|
|
if (event.msg_list === home_msg_list
|
2013-02-20 20:49:49 +01:00
|
|
|
&& event.id > furthest_read)
|
|
|
|
{
|
|
|
|
furthest_read = event.id;
|
|
|
|
}
|
2013-05-02 21:31:31 +02:00
|
|
|
|
2013-05-16 23:16:15 +02:00
|
|
|
// If we move the pointer, we don't want to respond to what's at the pointer
|
|
|
|
if (event.previously_selected !== event.id) {
|
|
|
|
respond_to_cursor = false;
|
|
|
|
}
|
|
|
|
|
2013-05-02 21:31:31 +02:00
|
|
|
if (event.previously_selected !== -1) {
|
|
|
|
// Mark messages between old pointer and new pointer as read
|
|
|
|
if (event.id < event.previously_selected) {
|
|
|
|
mark_read_between(event.msg_list, event.id, event.previously_selected);
|
|
|
|
} else {
|
|
|
|
mark_read_between(event.msg_list, event.previously_selected, event.id);
|
|
|
|
}
|
|
|
|
}
|
2013-02-20 20:49:49 +01:00
|
|
|
});
|
|
|
|
});
|
2012-08-31 17:10:19 +02:00
|
|
|
|
2012-11-07 20:31:45 +01:00
|
|
|
function case_insensitive_find(term, array) {
|
|
|
|
var lowered_term = term.toLowerCase();
|
|
|
|
return $.grep(array, function (elt) {
|
|
|
|
return elt.toLowerCase() === lowered_term;
|
|
|
|
}).length !== 0;
|
|
|
|
}
|
|
|
|
|
2013-05-14 21:18:11 +02:00
|
|
|
function process_message_for_recent_subjects(message, remove_message) {
|
2013-04-10 22:24:15 +02:00
|
|
|
var current_timestamp = 0;
|
2013-04-12 18:47:41 +02:00
|
|
|
var max_subjects = 5;
|
2013-05-14 21:18:11 +02:00
|
|
|
var count = 0;
|
2013-05-22 15:45:31 +02:00
|
|
|
var canon_stream = subs.canonicalized_name(message.stream);
|
|
|
|
var canon_subject = subs.canonicalized_name(message.subject);
|
2013-04-10 22:24:15 +02:00
|
|
|
|
2013-05-22 15:45:31 +02:00
|
|
|
if (! recent_subjects.hasOwnProperty(canon_stream)) {
|
|
|
|
recent_subjects[canon_stream] = [];
|
2013-04-10 22:24:15 +02:00
|
|
|
} else {
|
2013-05-22 15:45:31 +02:00
|
|
|
recent_subjects[canon_stream] =
|
|
|
|
$.grep(recent_subjects[canon_stream], function (item) {
|
|
|
|
var is_duplicate = (item.canon_subject.toLowerCase() === canon_subject.toLowerCase());
|
2013-05-03 20:28:23 +02:00
|
|
|
if (is_duplicate) {
|
2013-04-10 22:24:15 +02:00
|
|
|
current_timestamp = item.timestamp;
|
2013-05-14 21:18:11 +02:00
|
|
|
count = item.count;
|
2013-04-10 22:24:15 +02:00
|
|
|
}
|
|
|
|
|
2013-05-03 20:28:23 +02:00
|
|
|
return !is_duplicate;
|
2013-04-10 22:24:15 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-05-22 15:45:31 +02:00
|
|
|
var recents = recent_subjects[canon_stream];
|
2013-05-14 21:18:11 +02:00
|
|
|
|
|
|
|
if (remove_message !== undefined) {
|
|
|
|
count = count - 1;
|
|
|
|
} else {
|
|
|
|
count = count + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count !== 0) {
|
|
|
|
recents.push({subject: message.subject,
|
2013-05-22 15:45:31 +02:00
|
|
|
canon_subject: canon_subject,
|
2013-05-14 21:18:11 +02:00
|
|
|
count: count,
|
|
|
|
timestamp: Math.max(message.timestamp, current_timestamp)});
|
|
|
|
}
|
2013-04-10 22:24:15 +02:00
|
|
|
|
|
|
|
recents.sort(function (a, b) {
|
2013-04-12 18:12:43 +02:00
|
|
|
return b.timestamp - a.timestamp;
|
2013-04-10 22:24:15 +02:00
|
|
|
});
|
|
|
|
|
2013-04-12 18:47:41 +02:00
|
|
|
recents = recents.slice(0, max_subjects);
|
|
|
|
|
2013-05-22 15:45:31 +02:00
|
|
|
recent_subjects[canon_stream] = recents;
|
2013-04-10 22:24:15 +02:00
|
|
|
}
|
|
|
|
|
2013-05-16 21:09:58 +02:00
|
|
|
var msg_metadata_cache = {};
|
2012-11-20 19:50:28 +01:00
|
|
|
function add_message_metadata(message, dummy) {
|
2013-05-16 21:09:58 +02:00
|
|
|
var cached_msg = msg_metadata_cache[message.id];
|
2013-04-29 22:56:50 +02: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;
|
2012-11-20 19:50:28 +01:00
|
|
|
}
|
2012-12-14 18:12:54 +01:00
|
|
|
get_updates_params.last = Math.max(get_updates_params.last || 0, message.id);
|
2012-10-25 23:32:20 +02:00
|
|
|
|
2012-10-12 17:26:04 +02:00
|
|
|
var involved_people;
|
|
|
|
|
2013-03-27 18:29:44 +01:00
|
|
|
message.flags = message.flags || [];
|
2013-04-03 23:30:06 +02:00
|
|
|
message.historical = (message.flags !== undefined &&
|
|
|
|
message.flags.indexOf('historical') !== -1);
|
2013-03-27 18:29:44 +01:00
|
|
|
message.starred = message.flags.indexOf("starred") !== -1;
|
2013-05-31 16:15:27 +02:00
|
|
|
message.mentioned = message.flags.indexOf("mentioned") !== -1 ||
|
|
|
|
message.flags.indexOf("wildcard_mentioned") !== -1;
|
2013-05-08 23:17:49 +02:00
|
|
|
message.collapsed = message.flags.indexOf("collapsed") !== -1;
|
2013-03-27 18:29:44 +01:00
|
|
|
|
2012-10-10 16:34:42 +02:00
|
|
|
switch (message.type) {
|
2012-10-10 22:57:21 +02:00
|
|
|
case 'stream':
|
2012-10-10 23:31:26 +02:00
|
|
|
message.is_stream = true;
|
2013-04-18 17:13:43 +02:00
|
|
|
message.stream = message.display_recipient;
|
|
|
|
if (! subject_dict.hasOwnProperty(message.stream)) {
|
|
|
|
subject_dict[message.stream] = [];
|
2012-10-12 19:14:59 +02:00
|
|
|
}
|
2013-04-18 17:13:43 +02:00
|
|
|
if (! case_insensitive_find(message.subject, subject_dict[message.stream])) {
|
|
|
|
subject_dict[message.stream].push(message.subject);
|
|
|
|
subject_dict[message.stream].sort();
|
2012-10-12 19:14:59 +02:00
|
|
|
// We don't need to update the autocomplete after this because
|
|
|
|
// the subject box's source is a function
|
2012-09-04 20:31:23 +02:00
|
|
|
}
|
2012-10-10 16:34:42 +02:00
|
|
|
message.reply_to = message.sender_email;
|
2012-10-12 17:26:04 +02:00
|
|
|
|
2013-04-10 22:24:15 +02:00
|
|
|
process_message_for_recent_subjects(message);
|
|
|
|
|
2012-10-13 00:32:59 +02:00
|
|
|
involved_people = [{'full_name': message.sender_full_name,
|
2012-10-12 17:26:04 +02:00
|
|
|
'email': message.sender_email}];
|
2013-05-21 00:00:28 +02:00
|
|
|
|
2013-05-21 17:35:07 +02:00
|
|
|
if ((message.subject === compose.empty_subject_placeholder()) &&
|
|
|
|
(message.sender_email === page_params.email)) {
|
|
|
|
// You can only edit messages you sent, so only show the edit hint
|
|
|
|
// for empty subjects on messages you sent.
|
|
|
|
message.your_empty_subject = true;
|
2013-05-21 00:00:28 +02:00
|
|
|
} else {
|
2013-05-21 17:35:07 +02:00
|
|
|
message.your_empty_subject = false;
|
2013-05-21 00:00:28 +02:00
|
|
|
}
|
2012-09-26 19:57:19 +02:00
|
|
|
break;
|
|
|
|
|
2012-12-03 19:49:12 +01:00
|
|
|
case 'private':
|
|
|
|
message.is_private = true;
|
2013-03-04 23:42:30 +01:00
|
|
|
message.reply_to = get_private_message_recipient(message, 'email').replace(/ /g, "");
|
2012-12-03 19:49:12 +01:00
|
|
|
message.display_reply_to = get_private_message_recipient(message, 'full_name');
|
2012-10-12 17:26:04 +02:00
|
|
|
|
|
|
|
involved_people = message.display_recipient;
|
2012-11-19 18:36:53 +01:00
|
|
|
|
2013-03-25 23:26:14 +01:00
|
|
|
if (message.sender_email === page_params.email) {
|
2012-11-26 19:24:34 +01:00
|
|
|
typeahead_helper.update_your_recipients(involved_people);
|
2012-11-19 18:36:53 +01:00
|
|
|
} else {
|
2012-11-26 19:24:34 +01:00
|
|
|
typeahead_helper.update_all_recipients(involved_people);
|
2012-11-19 18:36:53 +01:00
|
|
|
}
|
2012-09-26 19:57:19 +02:00
|
|
|
break;
|
2012-10-12 17:26:04 +02:00
|
|
|
}
|
|
|
|
|
2012-10-13 00:17:48 +02:00
|
|
|
// Add new people involved in this message to the people list
|
|
|
|
$.each(involved_people, function (idx, person) {
|
|
|
|
// Do the hasOwnProperty() call via the prototype to avoid problems
|
|
|
|
// with keys like "hasOwnProperty"
|
2013-05-23 16:39:47 +02:00
|
|
|
if (people_dict[person.email] === undefined) {
|
2013-01-14 22:18:30 +01:00
|
|
|
add_person(person);
|
2013-05-23 16:39:47 +02:00
|
|
|
if (!typeahead_helper.known_to_typeahead(person)) {
|
|
|
|
typeahead_helper.autocomplete_needs_update(true);
|
|
|
|
}
|
2012-09-04 20:31:23 +02:00
|
|
|
}
|
2012-10-13 00:17:48 +02:00
|
|
|
});
|
2012-08-30 20:24:29 +02:00
|
|
|
|
2013-05-16 21:09:58 +02:00
|
|
|
msg_metadata_cache[message.id] = message;
|
2012-11-20 19:50:28 +01:00
|
|
|
return message;
|
2012-08-29 17:12:21 +02:00
|
|
|
}
|
|
|
|
|
2013-02-28 23:00:03 +01:00
|
|
|
function add_messages_helper(messages, msg_list, predicate) {
|
2013-02-27 22:51:37 +01:00
|
|
|
var top_messages = [];
|
|
|
|
var bottom_messages = [];
|
2013-04-10 18:30:36 +02:00
|
|
|
var interior_messages = [];
|
2013-02-27 22:51:37 +01:00
|
|
|
|
|
|
|
// If we're initially populating the list, save the messages in
|
|
|
|
// bottom_messages regardless
|
|
|
|
if (msg_list.selected_id() === -1 && msg_list.empty()) {
|
|
|
|
bottom_messages = $.grep(messages, predicate);
|
|
|
|
} else {
|
|
|
|
$.each(messages, function (idx, msg) {
|
|
|
|
// Filter out duplicates that are already in msg_list, and all messages
|
|
|
|
// that fail our filter predicate
|
|
|
|
if (! (msg_list.get(msg.id) === undefined && predicate(msg))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Put messages in correct order on either side of the message list
|
|
|
|
if (msg_list.empty() || (msg.id < msg_list.first().id)) {
|
|
|
|
top_messages.push(msg);
|
|
|
|
} else if (msg.id > msg_list.last().id) {
|
|
|
|
bottom_messages.push(msg);
|
|
|
|
} else {
|
2013-04-10 18:30:36 +02:00
|
|
|
interior_messages.push(msg);
|
2013-02-27 22:51:37 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2013-02-12 20:01:24 +01:00
|
|
|
|
2013-04-10 18:30:36 +02:00
|
|
|
if (interior_messages.length > 0) {
|
|
|
|
msg_list.add_and_rerender(top_messages.concat(interior_messages).concat(bottom_messages));
|
|
|
|
return true;
|
|
|
|
}
|
2013-02-28 23:00:03 +01:00
|
|
|
msg_list.prepend(top_messages);
|
|
|
|
msg_list.append(bottom_messages);
|
2013-02-08 19:57:16 +01:00
|
|
|
return top_messages.length > 0;
|
|
|
|
}
|
|
|
|
|
2013-04-10 16:18:09 +02:00
|
|
|
function add_messages(messages, msg_list) {
|
2012-11-20 19:50:28 +01:00
|
|
|
var prepended = false;
|
2012-10-25 00:42:45 +02:00
|
|
|
if (!messages)
|
2012-09-28 19:27:52 +02:00
|
|
|
return;
|
|
|
|
|
2013-01-16 19:50:18 +01:00
|
|
|
util.destroy_loading_indicator($('#page_loading_indicator'));
|
2013-02-12 19:28:21 +01:00
|
|
|
util.destroy_first_run_message();
|
2012-11-20 17:35:30 +01:00
|
|
|
|
2013-04-25 21:22:48 +02:00
|
|
|
if (add_messages_helper(messages, msg_list, msg_list.filter.predicate())) {
|
2013-02-12 20:01:24 +01:00
|
|
|
prepended = true;
|
2012-10-01 21:02:39 +02:00
|
|
|
}
|
|
|
|
|
2013-03-07 17:30:42 +01:00
|
|
|
if ((msg_list === narrowed_msg_list) && !msg_list.empty() &&
|
|
|
|
(msg_list.selected_id() === -1)) {
|
2013-02-23 19:38:25 +01: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.
|
2013-03-29 19:55:28 +01:00
|
|
|
narrow.hide_empty_narrow_message();
|
2013-03-07 17:30:42 +01:00
|
|
|
// And also select the newly arrived message.
|
|
|
|
msg_list.select_id(msg_list.selected_id(), {then_scroll: true, use_closest: true});
|
2013-02-23 19:38:25 +01:00
|
|
|
}
|
|
|
|
|
2012-09-29 01:38:03 +02:00
|
|
|
// If we prepended messages, then we need to scroll back to the pointer.
|
|
|
|
// This will mess with the user's scrollwheel use; possibly we should be
|
|
|
|
// more clever here. However (for now) we only prepend on page load,
|
|
|
|
// so maybe it's okay.
|
2012-10-03 00:39:21 +02:00
|
|
|
//
|
|
|
|
// We also need to re-select the message by ID, because we might have
|
|
|
|
// removed and re-added the row as part of prepend collapsing.
|
2013-02-22 20:48:31 +01:00
|
|
|
//
|
|
|
|
// We select the closest id as a fallback in case the previously selected
|
|
|
|
// message is no longer in the list
|
2013-02-20 18:26:50 +01:00
|
|
|
if (prepended && (msg_list.selected_id() >= 0)) {
|
2013-02-22 20:48:31 +01:00
|
|
|
msg_list.select_id(msg_list.selected_id(), {then_scroll: true, use_closest: true});
|
2012-10-03 00:39:21 +02:00
|
|
|
}
|
2012-09-29 01:38:03 +02:00
|
|
|
|
2012-11-19 01:09:40 +01:00
|
|
|
if (typeahead_helper.autocomplete_needs_update()) {
|
|
|
|
typeahead_helper.update_autocomplete();
|
2012-11-01 18:01:33 +01:00
|
|
|
}
|
2013-01-30 21:49:56 +01:00
|
|
|
|
Clean up code for unread counts and notifications.
The core simplification here is that zephyr.js no longer has:
* the global home_unread_messages
* the function unread_in_current_view() [which used the global]
The logic that used to be in zephyr is now in its proper home
of unread.js, which has these changes:
* the structure returned from unread.get_counts() includes
a new member called unread_in_current_view
* there's a helper function unread.num_unread_current_messages()
Deprecating zephyr.unread_in_current_view() affected two callers:
* notifications.update_title_count()
* notifications_bar.update()
The above functions used to call back to zephyr to get counts, but
there was no nice way to enforce that they were getting counts
at the right time in the code flow, because they depended on
functions like process_visible_unread_messages() to orchestrate
updating internal unread counts before pushing out counts to the DOM.
Now both of those function take a parameter with the unread count,
and we then had to change all of their callers appropriately. This
went hand in hand with another goal, which is that we want all the
unread-counts logic to funnel though basically one place, which
is zephyr.update_unread_counts(). So now that function always
calls notifications_bar.update() [NEW] as well as calling into
the modules unread.js, stream_list.js, and notifications.js [OLD].
Adding the call to notifications_bar.update() in update_unread_counts()
made it so that some other places in the code no longer needed to call
notifications_bar.update(), so you'll see some lines of code
removed. There are also cases where notifications.update_title_count()
was called redundantly, since the callers were already reaching
update_unread_counts() via other calls.
Finally, in ui.resizehandler, you'll see a simple case where the call
to notifications_bar.update() is preceded by an explicit call
to unread.get_counts().
(imported from commit ce84b9c8076c1f9bb20a61209913f0cb0dae098c)
2013-06-05 21:04:06 +02:00
|
|
|
// There are some other common tasks that happen when adding messages, but these
|
|
|
|
// happen higher up in the stack, notably logic related to unread counts.
|
2012-09-24 21:21:23 +02:00
|
|
|
}
|
|
|
|
|
2013-03-07 22:31:21 +01:00
|
|
|
function deduplicate_messages(messages) {
|
|
|
|
var new_message_ids = {};
|
|
|
|
return $.grep(messages, function (msg, idx) {
|
|
|
|
if (new_message_ids[msg.id] === undefined
|
|
|
|
&& all_msg_list.get(msg.id) === undefined)
|
|
|
|
{
|
|
|
|
new_message_ids[msg.id] = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-04-25 19:38:21 +02:00
|
|
|
function maybe_add_narrowed_messages(messages, msg_list) {
|
|
|
|
var ids = [];
|
|
|
|
$.each(messages, function (idx, elem) {
|
|
|
|
ids.push(elem.id);
|
|
|
|
});
|
|
|
|
|
|
|
|
$.ajax({
|
|
|
|
type: 'POST',
|
|
|
|
url: '/json/messages_in_narrow',
|
|
|
|
data: {msg_ids: JSON.stringify(ids),
|
|
|
|
narrow: JSON.stringify(narrow.public_operators())},
|
|
|
|
dataType: 'json',
|
|
|
|
timeout: 5000,
|
|
|
|
success: function (data) {
|
|
|
|
if (msg_list !== current_msg_list) {
|
|
|
|
// We unnarrowed in the mean time
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-05-01 22:00:07 +02:00
|
|
|
var new_messages = [];
|
|
|
|
$.each(messages, function (idx, 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);
|
|
|
|
}
|
2013-04-25 19:38:21 +02:00
|
|
|
});
|
|
|
|
|
2013-05-13 21:57:13 +02:00
|
|
|
new_messages = $.map(new_messages, add_message_metadata);
|
2013-05-01 22:00:07 +02:00
|
|
|
add_messages(new_messages, msg_list);
|
2013-04-25 19:38:21 +02:00
|
|
|
process_visible_unread_messages();
|
|
|
|
compose.update_faded_messages();
|
|
|
|
},
|
|
|
|
error: function (xhr) {
|
|
|
|
// We might want to be more clever here
|
|
|
|
$('#connection-error').show();
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}, 5000);
|
|
|
|
}});
|
|
|
|
}
|
|
|
|
|
2013-05-14 21:18:11 +02:00
|
|
|
function update_messages(events) {
|
|
|
|
$.each(events, function (idx, event) {
|
|
|
|
var msg = all_msg_list.get(event.message_id);
|
2013-06-04 21:38:42 +02:00
|
|
|
if (msg === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-05-14 21:18:11 +02:00
|
|
|
if (event.rendered_content !== undefined) {
|
|
|
|
msg.content = event.rendered_content;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event.subject !== undefined) {
|
|
|
|
// Remove the recent subjects entry for the old subject;
|
|
|
|
// must be called before we update msg.subject
|
|
|
|
process_message_for_recent_subjects(msg, true);
|
|
|
|
// Update the unread counts; again, this must be called
|
|
|
|
// before we update msg.subject
|
2013-05-17 21:32:26 +02:00
|
|
|
unread.update_unread_subjects(msg, event);
|
2013-05-14 21:18:11 +02:00
|
|
|
|
|
|
|
msg.subject = event.subject;
|
2013-05-21 00:00:28 +02:00
|
|
|
if (msg.subject === compose.empty_subject_placeholder()) {
|
2013-05-21 17:35:07 +02:00
|
|
|
msg.your_empty_subject = true;
|
2013-05-21 00:00:28 +02:00
|
|
|
} else {
|
2013-05-21 17:35:07 +02:00
|
|
|
msg.your_empty_subject = false;
|
2013-05-21 00:00:28 +02:00
|
|
|
}
|
2013-05-14 21:18:11 +02:00
|
|
|
// Add the recent subjects entry for the new subject; must
|
|
|
|
// be called after we update msg.subject
|
|
|
|
process_message_for_recent_subjects(msg);
|
|
|
|
}
|
2013-05-22 18:19:24 +02:00
|
|
|
|
2013-06-04 21:39:51 +02:00
|
|
|
var row = rows.get(event.message_id, current_msg_list.table_name);
|
|
|
|
if (row.length > 0) {
|
|
|
|
message_edit.end(row);
|
2013-05-22 18:19:24 +02:00
|
|
|
}
|
2013-05-21 17:48:46 +02:00
|
|
|
|
|
|
|
msg.last_edit_timestamp = event.edit_timestamp;
|
|
|
|
delete msg.last_edit_timestr;
|
2013-05-14 21:18:11 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
home_msg_list.rerender();
|
|
|
|
if (current_msg_list === narrowed_msg_list) {
|
|
|
|
narrowed_msg_list.rerender();
|
|
|
|
}
|
|
|
|
compose.update_faded_messages();
|
|
|
|
update_unread_counts();
|
|
|
|
stream_list.update_streams_sidebar();
|
|
|
|
}
|
|
|
|
|
2012-10-10 20:21:22 +02:00
|
|
|
var get_updates_xhr;
|
|
|
|
var get_updates_timeout;
|
2012-11-20 23:15:50 +01:00
|
|
|
function get_updates(options) {
|
|
|
|
var defaults = {dont_block: false};
|
|
|
|
options = $.extend({}, defaults, options);
|
|
|
|
|
2012-10-29 22:02:10 +01:00
|
|
|
get_updates_params.pointer = furthest_read;
|
2012-11-27 21:06:17 +01:00
|
|
|
get_updates_params.dont_block = options.dont_block || get_updates_failures > 0;
|
2013-03-28 18:09:27 +01:00
|
|
|
if (get_updates_params.queue_id === undefined) {
|
|
|
|
get_updates_params.queue_id = page_params.event_queue_id;
|
|
|
|
get_updates_params.last_event_id = page_params.last_event_id;
|
|
|
|
}
|
2012-10-17 23:10:34 +02:00
|
|
|
|
2012-10-10 20:21:22 +02:00
|
|
|
get_updates_xhr = $.ajax({
|
2012-08-31 21:33:04 +02:00
|
|
|
type: 'POST',
|
2013-03-26 18:07:40 +01:00
|
|
|
url: '/json/get_events',
|
2012-10-16 22:06:03 +02:00
|
|
|
data: get_updates_params,
|
2012-08-31 21:33:04 +02:00
|
|
|
dataType: 'json',
|
2013-03-25 23:26:14 +01:00
|
|
|
timeout: page_params.poll_timeout,
|
2012-08-31 21:33:04 +02:00
|
|
|
success: function (data) {
|
2012-10-23 17:00:00 +02:00
|
|
|
if (! data) {
|
2013-02-14 22:03:10 +01:00
|
|
|
// The server occasionally returns no data during a
|
2012-10-23 17:00:00 +02:00
|
|
|
// restart. Ignore those responses so the page keeps
|
|
|
|
// working
|
|
|
|
get_updates_timeout = setTimeout(get_updates, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-11-27 21:06:17 +01:00
|
|
|
get_updates_failures = 0;
|
2012-09-05 22:33:04 +02:00
|
|
|
$('#connection-error').hide();
|
|
|
|
|
2013-03-26 18:07:40 +01:00
|
|
|
var messages = [];
|
2013-05-14 21:18:11 +02:00
|
|
|
var messages_to_update = [];
|
2013-03-26 18:07:40 +01:00
|
|
|
var new_pointer;
|
|
|
|
|
|
|
|
$.each(data.events, function (idx, event) {
|
2013-03-28 18:09:27 +01:00
|
|
|
get_updates_params.last_event_id = Math.max(get_updates_params.last_event_id,
|
|
|
|
event.id);
|
2013-03-26 18:07:40 +01:00
|
|
|
|
|
|
|
switch (event.type) {
|
|
|
|
case 'message':
|
2013-05-17 19:50:12 +02:00
|
|
|
var msg = event.message;
|
|
|
|
msg.flags = event.flags;
|
|
|
|
messages.push(msg);
|
2013-03-26 18:07:40 +01:00
|
|
|
break;
|
|
|
|
case 'pointer':
|
2013-03-20 23:53:46 +01:00
|
|
|
new_pointer = event.pointer;
|
|
|
|
break;
|
|
|
|
case 'restart':
|
|
|
|
reload.initiate();
|
2013-03-26 18:07:40 +01:00
|
|
|
break;
|
2013-05-08 17:24:29 +02:00
|
|
|
case 'onboarding_steps':
|
|
|
|
onboarding.set_step_info(event.steps);
|
|
|
|
break;
|
2013-05-14 21:18:11 +02:00
|
|
|
case 'update_message':
|
|
|
|
messages_to_update.push(event);
|
|
|
|
break;
|
2013-03-29 18:22:23 +01:00
|
|
|
case 'realm_user':
|
|
|
|
if (event.op === 'add') {
|
|
|
|
add_person(event.person);
|
|
|
|
typeahead_helper.update_all_recipients([event.person]);
|
|
|
|
} else if (event.op === 'remove') {
|
|
|
|
remove_person(event.person);
|
|
|
|
typeahead_helper.remove_recipient([event.person]);
|
|
|
|
}
|
|
|
|
typeahead_helper.autocomplete_needs_update(true);
|
|
|
|
break;
|
2013-03-29 20:49:05 +01:00
|
|
|
case 'subscription':
|
|
|
|
if (event.op === 'add') {
|
|
|
|
$(document).trigger($.Event('subscription_add.zephyr',
|
|
|
|
{subscription: event.subscription}));
|
|
|
|
} else if (event.op === 'remove') {
|
|
|
|
$(document).trigger($.Event('subscription_remove.zephyr',
|
|
|
|
{subscription: event.subscription}));
|
|
|
|
}
|
|
|
|
break;
|
2013-04-03 22:00:02 +02:00
|
|
|
case 'presence':
|
2013-05-06 17:14:59 +02:00
|
|
|
activity.set_user_status(event.email, event.presence, event.server_timestamp);
|
2013-04-03 22:00:02 +02:00
|
|
|
break;
|
2013-03-26 18:07:40 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-03-29 18:22:23 +01:00
|
|
|
if (typeahead_helper.autocomplete_needs_update()) {
|
|
|
|
typeahead_helper.update_autocomplete();
|
|
|
|
}
|
|
|
|
|
2013-03-26 18:07:40 +01:00
|
|
|
if (messages.length !== 0) {
|
2013-03-07 22:31:21 +01:00
|
|
|
// There is a known bug (#1062) in our backend
|
|
|
|
// whereby duplicate messages are delivered during a
|
|
|
|
// server update. Once that bug is fixed, this
|
|
|
|
// should no longer be needed
|
2013-03-26 18:07:40 +01:00
|
|
|
messages = deduplicate_messages(messages);
|
2013-05-13 21:57:13 +02:00
|
|
|
messages = $.map(messages, add_message_metadata);
|
2013-03-07 22:31:21 +01:00
|
|
|
|
2013-05-13 21:57:13 +02:00
|
|
|
// You must add add messages to home_msg_list BEFORE
|
|
|
|
// calling process_loaded_for_unread.
|
2013-05-03 20:04:30 +02:00
|
|
|
add_messages(messages, home_msg_list);
|
2013-05-13 21:57:13 +02:00
|
|
|
process_loaded_for_unread(messages);
|
|
|
|
|
2013-05-13 17:33:05 +02:00
|
|
|
add_messages(messages, all_msg_list);
|
2013-05-03 20:04:30 +02:00
|
|
|
|
2013-02-22 20:48:31 +01:00
|
|
|
if (narrow.active()) {
|
2013-04-25 19:38:21 +02:00
|
|
|
if (narrow.filter().can_apply_locally()) {
|
|
|
|
add_messages(messages, narrowed_msg_list);
|
|
|
|
} else {
|
|
|
|
maybe_add_narrowed_messages(messages, narrowed_msg_list);
|
|
|
|
}
|
2013-02-12 20:01:24 +01:00
|
|
|
}
|
2013-05-03 20:04:30 +02:00
|
|
|
|
2013-05-08 21:31:26 +02:00
|
|
|
// notifications.received_messages uses values set by
|
|
|
|
// process_visible_unread_messages and thus must
|
|
|
|
// be called after it
|
2013-05-16 23:16:15 +02:00
|
|
|
var i;
|
|
|
|
var update_cursor = false;
|
|
|
|
// check if we need to update the cursor, and do so if needed.
|
|
|
|
for (i = 0; i < messages.length; i++) {
|
|
|
|
if (messages[i].sender_email === page_params.email && narrow.narrowed_by_reply()) {
|
|
|
|
update_cursor = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
process_visible_unread_messages(update_cursor);
|
2013-03-07 22:31:21 +01:00
|
|
|
notifications.received_messages(messages);
|
2013-03-04 16:59:09 +01:00
|
|
|
compose.update_faded_messages();
|
2013-05-06 02:54:15 +02:00
|
|
|
stream_list.update_streams_sidebar();
|
2012-10-17 23:10:34 +02:00
|
|
|
}
|
|
|
|
|
2013-03-26 18:07:40 +01:00
|
|
|
if (new_pointer !== undefined
|
|
|
|
&& new_pointer > furthest_read)
|
2012-10-29 22:02:10 +01:00
|
|
|
{
|
2013-03-26 18:07:40 +01:00
|
|
|
furthest_read = new_pointer;
|
|
|
|
server_furthest_read = new_pointer;
|
|
|
|
home_msg_list.select_id(new_pointer, {then_scroll: true, use_closest: true});
|
2013-02-14 23:48:37 +01:00
|
|
|
}
|
|
|
|
|
2013-02-22 20:48:31 +01:00
|
|
|
if ((home_msg_list.selected_id() === -1) && !home_msg_list.empty()) {
|
|
|
|
home_msg_list.select_id(home_msg_list.first().id, {then_scroll: false});
|
2012-10-29 22:02:10 +01:00
|
|
|
}
|
2012-10-17 23:10:34 +02:00
|
|
|
|
2013-05-14 21:18:11 +02:00
|
|
|
if (messages_to_update.length !== 0) {
|
|
|
|
update_messages(messages_to_update);
|
|
|
|
}
|
|
|
|
|
2012-12-11 18:17:56 +01:00
|
|
|
get_updates_timeout = setTimeout(get_updates, 0);
|
2012-08-31 21:33:04 +02:00
|
|
|
},
|
2012-09-05 22:17:14 +02:00
|
|
|
error: function (xhr, error_type, exn) {
|
2013-03-23 00:11:09 +01:00
|
|
|
// If we are old enough to have messages outside of the
|
|
|
|
// Tornado cache or if we're old enough that our message
|
|
|
|
// queue has been garbage collected, immediately reload.
|
2013-01-23 17:33:07 +01:00
|
|
|
if ((xhr.status === 400) &&
|
2013-03-23 00:11:09 +01:00
|
|
|
($.parseJSON(xhr.responseText).msg.indexOf("too old") !== -1 ||
|
|
|
|
$.parseJSON(xhr.responseText).msg.indexOf("Bad event queue id") !== -1)) {
|
2013-01-23 17:33:07 +01:00
|
|
|
reload.initiate({immediate: true});
|
|
|
|
}
|
|
|
|
|
2012-09-07 20:35:15 +02:00
|
|
|
if (error_type === 'timeout') {
|
2012-09-05 22:17:14 +02:00
|
|
|
// Retry indefinitely on timeout.
|
2012-11-27 21:06:17 +01:00
|
|
|
get_updates_failures = 0;
|
2012-09-05 22:33:04 +02:00
|
|
|
$('#connection-error').hide();
|
2012-09-05 22:17:14 +02:00
|
|
|
} else {
|
2012-11-27 21:06:17 +01:00
|
|
|
get_updates_failures += 1;
|
2012-09-05 22:17:14 +02:00
|
|
|
}
|
|
|
|
|
2012-11-27 21:06:17 +01:00
|
|
|
if (get_updates_failures >= 5) {
|
2012-08-31 21:33:04 +02:00
|
|
|
$('#connection-error').show();
|
|
|
|
} else {
|
2012-09-05 22:33:04 +02:00
|
|
|
$('#connection-error').hide();
|
2012-08-31 21:33:04 +02:00
|
|
|
}
|
2012-09-05 22:33:04 +02:00
|
|
|
|
2012-11-27 21:06:17 +01:00
|
|
|
var retry_sec = Math.min(90, Math.exp(get_updates_failures/2));
|
2012-10-10 20:21:22 +02:00
|
|
|
get_updates_timeout = setTimeout(get_updates, retry_sec*1000);
|
2012-08-31 21:33:04 +02:00
|
|
|
}
|
|
|
|
});
|
2012-08-29 17:12:21 +02:00
|
|
|
}
|
2012-09-04 20:31:23 +02:00
|
|
|
|
2013-03-14 19:42:37 +01:00
|
|
|
function load_old_messages(opts) {
|
|
|
|
opts = $.extend({}, {
|
|
|
|
cont_will_add_messages: false
|
|
|
|
}, opts);
|
2012-11-09 01:06:07 +01:00
|
|
|
|
2013-03-14 19:42:37 +01:00
|
|
|
var data = {anchor: opts.anchor,
|
|
|
|
num_before: opts.num_before,
|
|
|
|
num_after: opts.num_after};
|
2012-12-19 23:58:02 +01:00
|
|
|
|
2013-04-10 17:42:14 +02:00
|
|
|
if (opts.msg_list.narrowed && narrow.active())
|
2013-02-05 19:25:30 +01:00
|
|
|
data.narrow = JSON.stringify(narrow.public_operators());
|
2012-12-19 23:58:02 +01:00
|
|
|
|
2013-01-07 23:35:00 +01:00
|
|
|
function process_result(messages) {
|
|
|
|
$('#connection-error').hide();
|
|
|
|
|
2013-02-23 19:38:25 +01:00
|
|
|
if ((messages.length === 0) && (current_msg_list === narrowed_msg_list) &&
|
|
|
|
narrowed_msg_list.empty()) {
|
|
|
|
// Even after trying to load more messages, we have no
|
|
|
|
// messages to display in this narrow.
|
2013-03-29 19:55:28 +01:00
|
|
|
narrow.show_empty_narrow_message();
|
2013-02-23 19:38:25 +01:00
|
|
|
}
|
|
|
|
|
2013-05-13 21:57:13 +02:00
|
|
|
messages = $.map(messages, add_message_metadata);
|
|
|
|
|
2013-02-22 20:48:31 +01:00
|
|
|
// If we're loading more messages into the home view, save them to
|
|
|
|
// the all_msg_list as well, as the home_msg_list is reconstructed
|
|
|
|
// from all_msg_list.
|
2013-03-14 19:42:37 +01:00
|
|
|
if (opts.msg_list === home_msg_list) {
|
2013-05-13 21:57:13 +02:00
|
|
|
process_loaded_for_unread(messages);
|
2013-04-10 16:18:09 +02:00
|
|
|
add_messages(messages, all_msg_list);
|
2013-02-22 20:48:31 +01:00
|
|
|
}
|
|
|
|
|
2013-03-14 19:42:37 +01:00
|
|
|
if (messages.length !== 0 && !opts.cont_will_add_messages) {
|
|
|
|
add_messages(messages, opts.msg_list);
|
2013-01-07 23:35:00 +01:00
|
|
|
}
|
|
|
|
|
2013-05-06 02:54:15 +02:00
|
|
|
stream_list.update_streams_sidebar();
|
2013-05-02 02:59:15 +02:00
|
|
|
|
2013-03-14 19:42:37 +01:00
|
|
|
if (opts.cont !== undefined) {
|
|
|
|
opts.cont(messages);
|
2013-01-07 23:35:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-25 00:42:45 +02:00
|
|
|
$.ajax({
|
|
|
|
type: 'POST',
|
|
|
|
url: '/json/get_old_messages',
|
2012-12-19 23:58:02 +01:00
|
|
|
data: data,
|
2012-10-25 00:42:45 +02:00
|
|
|
dataType: 'json',
|
|
|
|
success: function (data) {
|
2013-04-10 17:42:14 +02:00
|
|
|
if (opts.msg_list.narrowed && opts.msg_list !== current_msg_list) {
|
2013-03-15 20:57:37 +01:00
|
|
|
// We unnarrowed before receiving new messages so
|
|
|
|
// don't bother processing the newly arrived messages.
|
|
|
|
return;
|
|
|
|
}
|
2012-10-25 00:42:45 +02:00
|
|
|
if (! data) {
|
|
|
|
// The server occationally returns no data during a
|
|
|
|
// restart. Ignore those responses and try again
|
|
|
|
setTimeout(function () {
|
2013-03-14 19:42:37 +01:00
|
|
|
load_old_messages(opts);
|
2012-10-25 00:42:45 +02:00
|
|
|
}, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-01-07 23:35:00 +01:00
|
|
|
process_result(data.messages);
|
2012-10-25 00:42:45 +02:00
|
|
|
},
|
|
|
|
error: function (xhr, error_type, exn) {
|
2013-04-10 17:42:14 +02:00
|
|
|
if (opts.msg_list.narrowed && opts.msg_list !== current_msg_list) {
|
2013-03-15 20:57:37 +01:00
|
|
|
// We unnarrowed before getting an error so don't
|
|
|
|
// bother trying again or doing further processing.
|
|
|
|
return;
|
|
|
|
}
|
2013-01-07 23:35:00 +01:00
|
|
|
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([]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-10-25 00:42:45 +02:00
|
|
|
// We might want to be more clever here
|
|
|
|
$('#connection-error').show();
|
|
|
|
setTimeout(function () {
|
2013-03-14 19:42:37 +01:00
|
|
|
load_old_messages(opts);
|
2012-10-25 00:42:45 +02:00
|
|
|
}, 5000);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the initial message list
|
|
|
|
$(function () {
|
2012-10-26 19:30:42 +02:00
|
|
|
function load_more(messages) {
|
2013-02-14 23:48:37 +01:00
|
|
|
// 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.
|
2013-02-22 20:48:31 +01:00
|
|
|
//
|
|
|
|
// 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) {
|
2013-03-25 23:26:14 +01:00
|
|
|
home_msg_list.select_id(page_params.initial_pointer,
|
|
|
|
{then_scroll: true, use_closest: true});
|
2013-02-14 23:48:37 +01:00
|
|
|
}
|
|
|
|
|
2012-10-25 00:42:45 +02:00
|
|
|
// catch the user up
|
2013-04-23 19:53:11 +02:00
|
|
|
if (messages.length !== 0) {
|
2012-10-25 00:42:45 +02:00
|
|
|
var latest_id = messages[messages.length-1].id;
|
2013-04-23 19:53:11 +02:00
|
|
|
if (latest_id < page_params.max_message_id) {
|
|
|
|
load_old_messages({
|
|
|
|
anchor: latest_id,
|
|
|
|
num_before: 0,
|
|
|
|
num_after: 400,
|
|
|
|
msg_list: home_msg_list,
|
|
|
|
cont: load_more
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2012-10-25 00:42:45 +02:00
|
|
|
}
|
2012-10-26 00:06:40 +02:00
|
|
|
// now start subscribing to updates
|
|
|
|
get_updates();
|
2013-03-12 22:16:37 +01:00
|
|
|
|
|
|
|
// backfill more messages after the user is idle
|
|
|
|
var backfill_batch_size = 1000;
|
|
|
|
$(document).idle({'idle': 1000*10,
|
|
|
|
'onIdle': function () {
|
|
|
|
var first_id = all_msg_list.first().id;
|
2013-03-14 19:42:37 +01:00
|
|
|
load_old_messages({
|
|
|
|
anchor: first_id,
|
|
|
|
num_before: backfill_batch_size,
|
|
|
|
num_after: 0,
|
|
|
|
msg_list: home_msg_list
|
|
|
|
});
|
2013-03-12 22:16:37 +01:00
|
|
|
}});
|
2012-10-25 00:42:45 +02:00
|
|
|
}
|
|
|
|
|
2013-03-25 23:26:14 +01:00
|
|
|
if (page_params.have_initial_messages) {
|
2013-03-14 19:42:37 +01:00
|
|
|
load_old_messages({
|
2013-03-25 23:26:14 +01:00
|
|
|
anchor: page_params.initial_pointer,
|
2013-03-14 19:42:37 +01:00
|
|
|
num_before: 200,
|
|
|
|
num_after: 200,
|
|
|
|
msg_list: home_msg_list,
|
|
|
|
cont: load_more
|
|
|
|
});
|
2012-11-05 23:56:27 +01:00
|
|
|
} else {
|
|
|
|
get_updates();
|
|
|
|
}
|
2012-10-25 00:42:45 +02:00
|
|
|
});
|
|
|
|
|
2012-11-20 23:15:50 +01:00
|
|
|
function restart_get_updates(options) {
|
2012-10-19 21:51:36 +02:00
|
|
|
if (get_updates_xhr !== undefined)
|
|
|
|
get_updates_xhr.abort();
|
|
|
|
|
|
|
|
if (get_updates_timeout !== undefined)
|
|
|
|
clearTimeout(get_updates_timeout);
|
|
|
|
|
2012-11-20 23:15:50 +01:00
|
|
|
get_updates(options);
|
2012-10-19 21:51:36 +02:00
|
|
|
}
|
|
|
|
|
2012-11-27 23:17:30 +01:00
|
|
|
function reset_load_more_status() {
|
|
|
|
load_more_enabled = true;
|
|
|
|
have_scrolled_away_from_top = true;
|
2013-03-15 20:57:37 +01:00
|
|
|
ui.hide_loading_more_messages_indicator();
|
2012-11-27 23:17:30 +01:00
|
|
|
}
|
|
|
|
|
2013-02-12 20:01:24 +01:00
|
|
|
function load_more_messages(msg_list) {
|
2012-11-06 19:17:38 +01:00
|
|
|
var batch_size = 400;
|
2013-02-12 20:01:24 +01:00
|
|
|
var oldest_message_id;
|
2012-11-27 23:17:30 +01:00
|
|
|
if (!load_more_enabled) {
|
|
|
|
return;
|
|
|
|
}
|
2013-01-16 19:50:18 +01:00
|
|
|
ui.show_loading_more_messages_indicator();
|
2012-11-27 23:17:30 +01:00
|
|
|
load_more_enabled = false;
|
2013-02-12 20:01:24 +01:00
|
|
|
if (msg_list.first() === undefined) {
|
2013-03-25 23:26:14 +01:00
|
|
|
oldest_message_id = page_params.initial_pointer;
|
2013-02-12 20:01:24 +01:00
|
|
|
} else {
|
|
|
|
oldest_message_id = msg_list.first().id;
|
2012-12-04 20:04:36 +01:00
|
|
|
}
|
2013-03-14 19:42:37 +01:00
|
|
|
load_old_messages({
|
|
|
|
anchor: oldest_message_id,
|
|
|
|
num_before: batch_size,
|
|
|
|
num_after: 0,
|
|
|
|
msg_list: msg_list,
|
|
|
|
cont: function (messages) {
|
|
|
|
ui.hide_loading_more_messages_indicator();
|
2013-04-23 19:26:29 +02:00
|
|
|
if (messages.length >= batch_size) {
|
2013-03-14 19:42:37 +01:00
|
|
|
load_more_enabled = true;
|
|
|
|
}
|
2013-04-10 17:42:14 +02:00
|
|
|
}
|
2013-03-14 19:42:37 +01:00
|
|
|
});
|
2012-10-19 22:28:57 +02:00
|
|
|
}
|
|
|
|
|
2012-10-10 20:21:22 +02:00
|
|
|
var watchdog_time = $.now();
|
2012-11-20 21:37:31 +01:00
|
|
|
setInterval(function () {
|
2012-10-10 20:21:22 +02:00
|
|
|
var new_time = $.now();
|
|
|
|
if ((new_time - watchdog_time) > 20000) { // 20 seconds.
|
|
|
|
// Our app's JS wasn't running (the machine was probably
|
|
|
|
// asleep). Now that we're running again, immediately poll for
|
|
|
|
// new updates.
|
2012-11-27 21:06:17 +01:00
|
|
|
get_updates_failures = 0;
|
2012-11-27 21:13:32 +01:00
|
|
|
restart_get_updates({dont_block: true});
|
2012-10-10 20:21:22 +02:00
|
|
|
}
|
|
|
|
watchdog_time = new_time;
|
|
|
|
}, 5000);
|
|
|
|
|
2012-10-10 20:20:31 +02:00
|
|
|
// The idea here is when you've scrolled to the very
|
|
|
|
// bottom of the page, e.g., the scroll handler isn't
|
|
|
|
// going to fire anymore. But if I continue to use
|
|
|
|
// the scrollwheel, the selection should advance until
|
|
|
|
// I'm at the very top or the very bottom of the page.
|
2012-10-16 18:16:26 +02:00
|
|
|
function move_pointer_at_page_top_and_bottom(delta) {
|
2013-05-28 22:36:48 +02:00
|
|
|
if (delta !== 0 && (viewport.at_top() || viewport.at_bottom())) {
|
2013-02-14 23:48:37 +01:00
|
|
|
var next_row = current_msg_list.selected_row();
|
2012-10-16 18:16:26 +02:00
|
|
|
if (delta > 0) {
|
|
|
|
// Scrolling up (want older messages)
|
2013-02-14 23:48:37 +01:00
|
|
|
next_row = rows.prev_visible(next_row);
|
2012-10-16 18:16:26 +02:00
|
|
|
} else {
|
|
|
|
// We're scrolling down (we want more recent messages)
|
2013-02-14 23:48:37 +01:00
|
|
|
next_row = rows.next_visible(next_row);
|
2012-10-16 18:16:26 +02:00
|
|
|
}
|
2013-02-14 23:48:37 +01:00
|
|
|
if (next_row.length !== 0) {
|
2013-02-20 20:49:49 +01:00
|
|
|
current_msg_list.select_id(rows.id(next_row));
|
2012-10-16 18:16:26 +02:00
|
|
|
}
|
2012-09-26 20:44:41 +02:00
|
|
|
}
|
|
|
|
}
|
2013-02-20 04:19:18 +01:00
|
|
|
|
2013-03-07 20:07:41 +01:00
|
|
|
function fast_forward_pointer(btn) {
|
|
|
|
btn = $(btn);
|
|
|
|
|
|
|
|
btn.attr('disabled', 'disabled');
|
|
|
|
|
|
|
|
btn.after($("<div>").addClass("alert alert-info settings_committed")
|
|
|
|
.text("Working…"));
|
|
|
|
|
2013-02-20 04:19:18 +01:00
|
|
|
$.ajax({
|
|
|
|
type: 'POST',
|
|
|
|
url: '/json/get_profile',
|
2013-03-25 23:26:14 +01:00
|
|
|
data: {email: page_params.email},
|
2013-02-20 04:19:18 +01:00
|
|
|
dataType: 'json',
|
|
|
|
success: function (data) {
|
2013-03-15 20:07:38 +01:00
|
|
|
mark_all_as_read(function () {
|
|
|
|
furthest_read = data.max_message_id;
|
|
|
|
send_pointer_update();
|
|
|
|
ui.change_tab_to('#home');
|
|
|
|
reload.initiate({immediate: true});
|
|
|
|
});
|
2013-02-20 04:19:18 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|