Extracted model code out to unread.js.

Most of the model logic pertaining to unread counts had been in
zephyr.js, along with a couple global variables.  Now the code
is encapsulated in unread.js.  It was a pretty straightforward
extraction with some minor method name changes.  Also, a small
bit of the logic had also been in stream_list.js.

Conflicts:
	tools/jslint/check-all.js

(imported from commit f0abdd48f26ab20c5beaef203479eb5a70dacfff)
This commit is contained in:
Steve Howell 2013-05-17 15:32:26 -04:00 committed by Leo Franchi
parent c587a18646
commit 62d6360a48
7 changed files with 176 additions and 148 deletions

View File

@ -282,6 +282,7 @@ PIPELINE_JS = {
'js/setup.js',
'js/viewport.js',
'js/rows.js',
'js/unread.js',
'js/stream_list.js',
'js/narrow.js',
'js/reload.js',

View File

@ -17,7 +17,7 @@ var globals =
// Modules, defined in their respective files.
+ ' compose rows hotkeys narrow reload notifications_bar search subs'
+ ' composebox_typeahead typeahead_helper notifications hashchange'
+ ' invite ui util activity timerender MessageList blueslip stream_list'
+ ' invite ui util activity timerender MessageList blueslip unread stream_list'
+ ' onboarding message_edit tab_bar'
// colorspace.js

View File

@ -386,7 +386,7 @@ exports.activate = function (operators, opts) {
if (opts.select_first_unread) {
then_select_id = narrowed_msg_list.last().id;
$.each(narrowed_msg_list.all(), function (idx, msg) {
if (message_unread(msg)) {
if (unread.message_unread(msg)) {
then_select_id = msg.id;
return false;
}

View File

@ -232,7 +232,7 @@ exports.received_messages = function (messages) {
// marked as read by process_visible_unread_messages
// (which occurs if the message arrived onscreen while the
// window had focus).
if (!(message_is_notifiable(message) && message_unread(message))) {
if (!(message_is_notifiable(message) && unread.message_unread(message))) {
return;
}
if (page_params.desktop_notifications_enabled &&

View File

@ -160,16 +160,12 @@ function rebuild_recent_subjects(stream, subject) {
var stream_li = get_filter_li('stream', stream);
var subjects = recent_subjects[stream] || [];
$.each(subjects, function (idx, subject_obj) {
var unread = 0;
if (unread_subjects[stream] !== undefined &&
unread_subjects[stream][subject_obj.subject] !== undefined) {
unread = Object.keys(unread_subjects[stream][subject_obj.subject]).length;
}
subject_obj.unread = unread;
subject_obj.has_unread = unread !== 0;
var num_unread = unread.num_unread_for_subject(stream, subject_obj.subject);
subject_obj.unread = num_unread;
subject_obj.has_unread = num_unread !== 0;
});
stream_li.append(templates.render('sidebar_subject_list',
{subjects: subjects,
stream: stream}));

155
zephyr/static/js/unread.js Normal file
View File

@ -0,0 +1,155 @@
var unread = (function () {
var exports = {};
var unread_counts = {'stream': {}, 'private': {}};
var unread_subjects = {};
exports.message_unread = function (message) {
// This is the only halfway interesting function in this module.
// Everything else is just slinging hashes around.
if (message === undefined) {
return false;
}
var sent_by_human = ['website', 'iphone', 'android']
.indexOf(message.client.toLowerCase()) !== -1;
if (message.sender_email === page_params.email && sent_by_human) {
return false;
}
return message.flags === undefined ||
message.flags.indexOf('read') === -1;
};
exports.update_unread_subjects = function (msg, event) {
if (event.subject !== undefined &&
unread_subjects[msg.stream] !== undefined &&
unread_subjects[msg.stream][msg.subject] !== undefined &&
unread_subjects[msg.stream][msg.subject][msg.id]) {
// Move the unread subject count to the new subject
delete unread_subjects[msg.stream][msg.subject][msg.id];
if (unread_subjects[msg.stream][msg.subject].length === 0) {
delete unread_subjects[msg.stream][msg.subject];
}
if (unread_subjects[msg.stream][event.subject] === undefined) {
unread_subjects[msg.stream][event.subject] = {};
}
unread_subjects[msg.stream][event.subject][msg.id] = true;
}
};
exports.process_loaded_messages = function (messages) {
$.each(messages, function (idx, message) {
var unread = exports.message_unread(message);
if (!unread) {
return;
}
var hashkey = unread_hashkey(message);
unread_counts[message.type][hashkey][message.id] = true;
if (message.type === 'stream') {
unread_subjects[hashkey][message.subject][message.id] = true;
}
});
};
exports.process_read_message = function (message) {
var hashkey = unread_hashkey(message);
delete unread_counts[message.type][hashkey][message.id];
if (message.type === 'stream') {
delete unread_subjects[message.stream][message.subject][message.id];
}
};
function unread_hashkey(message) {
var hashkey;
if (message.type === 'stream') {
hashkey = message.stream;
} else {
hashkey = message.display_reply_to;
}
if (unread_counts[message.type][hashkey] === undefined) {
unread_counts[message.type][hashkey] = {};
}
if (message.type === 'stream') {
if (unread_subjects[hashkey] === undefined) {
unread_subjects[hashkey] = {};
}
if (unread_subjects[hashkey][message.subject] === undefined) {
unread_subjects[hashkey][message.subject] = {};
}
}
return hashkey;
}
exports.declare_bankruptcy = function () {
unread_counts = {'stream': {}, 'private': {}};
};
exports.get_counts = function () {
var res = {};
// Return a data structure with various counts. This function should be
// pretty cheap, even if you don't care about all the counts, and you
// should strive to keep it free of side effects on globals or DOM.
res.private_message_count = 0;
res.home_unread_messages = 0;
res.stream_count = {}; // hash by stream -> count
res.subject_count = {}; // hash of hashes (stream, then subject -> count)
function only_in_home_view(msgids) {
return $.grep(msgids, function (msgid) {
return home_msg_list.get(msgid) !== undefined;
});
}
$.each(unread_counts.stream, function(stream, msgs) {
if (! subs.have(stream)) {
return true;
}
var count = Object.keys(msgs).length;
res.stream_count[stream]= count;
if (narrow.stream_in_home(stream)) {
res.home_unread_messages += only_in_home_view(Object.keys(msgs)).length;
}
if (unread_subjects[stream] !== undefined) {
res.subject_count[stream] = {};
$.each(unread_subjects[stream], function (subject, msgs) {
res.subject_count[stream][subject] = Object.keys(msgs).length;
});
}
});
var pm_count = 0;
$.each(unread_counts["private"], function(index, obj) {
pm_count += Object.keys(obj).length;
});
res.private_message_count = pm_count;
res.home_unread_messages += pm_count;
return res;
};
exports.num_unread_for_subject = function (stream, subject) {
var num_unread = 0;
if (unread_subjects[stream] !== undefined &&
unread_subjects[stream][subject] !== undefined) {
num_unread = Object.keys(unread_subjects[stream][subject]).length;
}
return num_unread;
};
return exports;
}());

View File

@ -243,91 +243,25 @@ function send_queued_flags() {
success: on_success});
}
var unread_counts = {'stream': {}, 'private': {}};
var unread_subjects = {};
var home_unread_messages = 0;
function unread_in_current_view() {
var unread = 0;
var num_unread = 0;
if (!narrow.active()) {
unread = home_unread_messages;
num_unread = home_unread_messages;
} else {
$.each(current_msg_list.all(), function (idx, msg) {
if (message_unread(msg) && msg.id > current_msg_list.selected_id()) {
unread += 1;
if (unread.message_unread(msg) && msg.id > current_msg_list.selected_id()) {
num_unread += 1;
}
});
}
return unread;
}
function message_unread(message) {
if (message === undefined) {
return false;
}
var sent_by_human = ['website', 'iphone', 'android']
.indexOf(message.client.toLowerCase()) !== -1;
if (message.sender_email === page_params.email && sent_by_human) {
return false;
}
return message.flags === undefined ||
message.flags.indexOf('read') === -1;
}
function get_unread_counts() {
var res = {};
// Return a data structure with various counts. This function should be
// pretty cheap, even if you don't care about all the counts, and you
// should strive to keep it free of side effects on globals or DOM.
res.private_message_count = 0;
res.home_unread_messages = 0;
res.stream_count = {}; // hash by stream -> count
res.subject_count = {}; // hash of hashes (stream, then subject -> count)
function only_in_home_view(msgids) {
return $.grep(msgids, function (msgid) {
return home_msg_list.get(msgid) !== undefined;
});
}
$.each(unread_counts.stream, function(stream, msgs) {
if (! subs.have(stream)) {
return true;
}
var count = Object.keys(msgs).length;
res.stream_count[stream]= count;
if (narrow.stream_in_home(stream)) {
res.home_unread_messages += only_in_home_view(Object.keys(msgs)).length;
}
if (unread_subjects[stream] !== undefined) {
res.subject_count[stream] = {};
$.each(unread_subjects[stream], function (subject, msgs) {
res.subject_count[stream][subject] = Object.keys(msgs).length;
});
}
});
var pm_count = 0;
$.each(unread_counts["private"], function(index, obj) {
pm_count += Object.keys(obj).length;
});
res.private_message_count = pm_count;
res.home_unread_messages += pm_count;
return res;
return num_unread;
}
function update_unread_counts() {
// Pure computation:
var res = get_unread_counts();
var res = unread.get_counts();
// Side effects from here down:
@ -346,7 +280,7 @@ function mark_all_as_read(cont) {
msg.flags = msg.flags || [];
msg.flags.push('read');
});
unread_counts = {'stream': {}, 'private': {}};
unread.declare_bankruptcy();
update_unread_counts();
$.ajax({
@ -360,45 +294,8 @@ function mark_all_as_read(cont) {
success: cont});
}
function unread_hashkey(message) {
var hashkey;
if (message.type === 'stream') {
hashkey = message.stream;
} else {
hashkey = message.display_reply_to;
}
if (unread_counts[message.type][hashkey] === undefined) {
unread_counts[message.type][hashkey] = {};
}
if (message.type === 'stream') {
if (unread_subjects[hashkey] === undefined) {
unread_subjects[hashkey] = {};
}
if (unread_subjects[hashkey][message.subject] === undefined) {
unread_subjects[hashkey][message.subject] = {};
}
}
return hashkey;
}
function process_loaded_for_unread(messages) {
$.each(messages, function (idx, message) {
var unread = message_unread(message);
if (!unread) {
return;
}
var hashkey = unread_hashkey(message);
unread_counts[message.type][hashkey][message.id] = true;
if (message.type === 'stream') {
unread_subjects[hashkey][message.subject][message.id] = true;
}
});
unread.process_loaded_messages(messages);
update_unread_counts();
}
@ -406,16 +303,12 @@ function process_loaded_for_unread(messages) {
function process_read_messages(messages) {
var processed = [];
$.each(messages, function (idx, message) {
var hashkey = unread_hashkey(message);
message.flags = message.flags || [];
message.flags.push('read');
processed.push(message.id);
unread.process_read_message(message);
delete unread_counts[message.type][hashkey][message.id];
if (message.type === 'stream') {
delete unread_subjects[message.stream][message.subject][message.id];
}
});
if (processed.length > 0) {
@ -461,14 +354,14 @@ function process_visible_unread_messages() {
var mark_as_read = $.map(visible_messages, function(msg) {
var message = current_msg_list.get(rows.id($(msg)));
if (! message_unread(message)) {
if (! unread.message_unread(message)) {
return undefined;
} else {
return message;
}
});
if (message_unread(selected)) {
if (unread.message_unread(selected)) {
mark_as_read.push(selected);
}
@ -481,7 +374,7 @@ 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) {
if (message_unread(msg)) {
if (unread.message_unread(msg)) {
mark_as_read.push(msg);
}
});
@ -800,23 +693,6 @@ function maybe_add_narrowed_messages(messages, msg_list) {
}});
}
function update_message_unread_subjects(msg, event) {
if (event.subject !== undefined &&
unread_subjects[msg.stream] !== undefined &&
unread_subjects[msg.stream][msg.subject] !== undefined &&
unread_subjects[msg.stream][msg.subject][msg.id]) {
// Move the unread subject count to the new subject
delete unread_subjects[msg.stream][msg.subject][msg.id];
if (unread_subjects[msg.stream][msg.subject].length === 0) {
delete unread_subjects[msg.stream][msg.subject];
}
if (unread_subjects[msg.stream][event.subject] === undefined) {
unread_subjects[msg.stream][event.subject] = {};
}
unread_subjects[msg.stream][event.subject][msg.id] = true;
}
}
function update_messages(events) {
$.each(events, function (idx, event) {
var msg = all_msg_list.get(event.message_id);
@ -830,7 +706,7 @@ function update_messages(events) {
process_message_for_recent_subjects(msg, true);
// Update the unread counts; again, this must be called
// before we update msg.subject
update_message_unread_subjects(msg, event);
unread.update_unread_subjects(msg, event);
msg.subject = event.subject;
// Add the recent subjects entry for the new subject; must