Files
zulip/static/js/activity.js
Steve Howell 6d511486cb Add activity.process_loaded_messages() and get_huddles().
Activity.js now has the capability to track huddles that
come through in loaded messages and return them in reverse
chronological order by their most recent message.  Right
now this only connected to a unit test, not any production
code.

(imported from commit 59957086fa2e454e5711472df091f178217aed2b)
2013-11-05 15:34:28 -05:00

248 lines
7.0 KiB
JavaScript

var activity = (function () {
var exports = {};
/*
Helpers for detecting user activity and managing user idle states
*/
/* Broadcast "idle" to server after 5 minutes of local inactivity */
var DEFAULT_IDLE_TIMEOUT_MS = 5 * 60 * 1000;
/* Time between keep-alive pings */
var ACTIVE_PING_INTERVAL_MS = 50 * 1000;
/* Mark users as offline after 140 seconds since their last checkin */
var OFFLINE_THRESHOLD_SECS = 140;
var presence_descriptions = {
active: 'is active',
idle: 'is not active'
};
/* Keep in sync with views.py:json_update_active_status() */
exports.ACTIVE = "active";
exports.IDLE = "idle";
// When you start Zulip, has_focus should be true, but it might not be the
// case after a server-initiated reload.
exports.has_focus = document.hasFocus && document.hasFocus();
// We initialize this to true, to count new page loads, but set it to
// false in the onload function in reload.js if this was a
// server-initiated-reload to avoid counting a server-initiated reload
// as user activity.
exports.new_user_input = true;
$("html").on("mousemove", function () {
exports.new_user_input = true;
});
var presence_info = {};
var huddle_timestamps = new Dict();
exports.process_loaded_messages = function (messages) {
_.each(messages, function (message) {
if (message.type === 'private') {
if (message.reply_to.indexOf(',') > 0) {
var old_timestamp = huddle_timestamps.get(message.reply_to);
if (!old_timestamp || (old_timestamp < message.timestamp)) {
huddle_timestamps.set(message.reply_to, message.timestamp);
}
}
}
});
};
exports.get_huddles = function () {
var huddles = huddle_timestamps.keys();
huddles = _.sortBy(huddles, function (huddle) {
return huddle_timestamps.get(huddle);
});
return huddles.reverse();
};
function sort_users(users, presence_info) {
// TODO sort by unread count first, once we support that
users.sort(function (a, b) {
if (presence_info[a] === 'active' && presence_info[b] !== 'active') {
return -1;
} else if (presence_info[b] === 'active' && presence_info[a] !== 'active') {
return 1;
}
if (presence_info[a] === 'idle' && presence_info[b] !== 'idle') {
return -1;
} else if (presence_info[b] === 'idle' && presence_info[a] !== 'idle') {
return 1;
}
// Sort equivalent PM names alphabetically
var full_name_a = a;
var full_name_b = b;
if (people_dict.has(a)) {
full_name_a = people_dict.get(a).full_name;
}
if (people_dict.has(b)) {
full_name_b = people_dict.get(b).full_name;
}
return util.strcmp(full_name_a, full_name_b);
});
return users;
}
// for testing:
exports._sort_users = sort_users;
function focus_lost() {
if (!exports.has_focus) {
return false;
}
exports.has_focus = false;
}
function update_users() {
if (page_params.domain === 'mit.edu') {
return; // MIT realm doesn't have a presence list
}
var users = sort_users(Object.keys(presence_info), presence_info);
var my_info = {
name: page_params.fullname,
email: page_params.email,
type: (activity.has_focus) ? activity.ACTIVE : activity.IDLE,
type_desc: presence_descriptions.active,
my_fullname: true
};
function info_for(email) {
var presence = presence_info[email];
return {
name: people_dict.get(email).full_name,
email: email,
type: presence,
type_desc: presence_descriptions[presence]
};
}
var user_emails = _.filter(users, function (email) {
return people_dict.has(email);
});
var user_info = [my_info].concat(_.map(user_emails, info_for));
$('#user_presences').html(templates.render('user_presence_rows', {users: user_info}));
// Update the counts in the presence list.
if (!suppress_unread_counts) {
// We do this after rendering the template, to avoid dealing with
// the suppress_unread_counts conditional in the template.
var set_count = function (email) {
stream_list.set_presence_list_count(email, unread.num_unread_for_person(email));
};
_.each(user_emails, set_count);
set_count(page_params.email);
}
// Update user fading, if necessary.
compose_fade.update_faded_users();
}
function status_from_timestamp(baseline_time, presence) {
if (presence.website === undefined) {
return 'offline';
}
var age = baseline_time - presence.website.timestamp;
var status = 'offline';
if (age < OFFLINE_THRESHOLD_SECS) {
status = presence.website.status;
}
return status;
}
function focus_ping() {
$.post('/json/update_active_status',
{status: (exports.has_focus) ? exports.ACTIVE : exports.IDLE,
new_user_input: exports.new_user_input}, function (data) {
if (data === undefined || data.presences === undefined) {
// We sometimes receive no data even on successful
// requests; we should figure out why but this will
// prevent us from throwing errors until then
return;
}
presence_info = {};
// Update Zephyr mirror activity warning
if (data.zephyr_mirror_active === false) {
$('#zephyr-mirror-error').show();
} else {
$('#zephyr-mirror-error').hide();
}
exports.new_user_input = false;
// Ping returns the active peer list
_.each(data.presences, function (presence, this_email) {
if (page_params.email !== this_email) {
presence_info[this_email] = status_from_timestamp(data.server_timestamp, presence);
}
});
update_users();
});
}
function focus_gained() {
if (!exports.has_focus) {
exports.has_focus = true;
focus_ping();
}
}
exports.initialize = function () {
$(window).focus(focus_gained);
$(window).idle({idle: DEFAULT_IDLE_TIMEOUT_MS,
onIdle: focus_lost,
onActive: focus_gained,
keepTracking: true});
setInterval(focus_ping, ACTIVE_PING_INTERVAL_MS);
focus_ping();
};
// Set user statuses. `users` should be an object with user emails as keys
// and presence information (see `status_from_timestamp`) as values.
//
// The object does not need to include every user, only the ones
// whose presence you wish to update.
//
// This rerenders the user sidebar at the end, which can be slow if done too
// often, so try to avoid calling this repeatedly.
exports.set_user_statuses = function (users, server_time) {
_.each(users, function (presence, email) {
if (email === page_params.email) {
return;
}
presence_info[email] = status_from_timestamp(server_time, presence);
});
update_users();
};
return exports;
}());
if (typeof module !== 'undefined') {
module.exports = activity;
}