mirror of https://github.com/zulip/zulip.git
Extract presence.js to track presence info.
Most of this code was simply moved from activity.js with some minor renaming of functions like set_presence_info -> set_info. Some functions were slightly nontrivial extractions: is_not_offline: came from activity.huddle_fraction_present get_status/get_mobile: simple getters set_user_status: partial extraction from activity.set_user_status last_active_date: pulled out of admin.js code We also fixed activity.filter_and_sort to take user_ids.
This commit is contained in:
parent
0605a9fb0f
commit
2718bd0b5d
|
@ -98,6 +98,7 @@
|
||||||
"floating_recipient_bar": false,
|
"floating_recipient_bar": false,
|
||||||
"tab_bar": false,
|
"tab_bar": false,
|
||||||
"emoji": false,
|
"emoji": false,
|
||||||
|
"presence": false,
|
||||||
"activity": false,
|
"activity": false,
|
||||||
"invite": false,
|
"invite": false,
|
||||||
"colorspace": false,
|
"colorspace": false,
|
||||||
|
|
|
@ -22,9 +22,14 @@ add_dependencies({
|
||||||
hash_util: 'js/hash_util.js',
|
hash_util: 'js/hash_util.js',
|
||||||
hashchange: 'js/hashchange.js',
|
hashchange: 'js/hashchange.js',
|
||||||
narrow: 'js/narrow.js',
|
narrow: 'js/narrow.js',
|
||||||
|
presence: 'js/presence.js',
|
||||||
activity: 'js/activity.js',
|
activity: 'js/activity.js',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var presence = global.presence;
|
||||||
|
|
||||||
|
var OFFLINE_THRESHOLD_SECS = 140;
|
||||||
|
|
||||||
set_global('resize', {
|
set_global('resize', {
|
||||||
resize_page_components: function () {},
|
resize_page_components: function () {},
|
||||||
});
|
});
|
||||||
|
@ -88,7 +93,7 @@ global.compile_template('user_presence_rows');
|
||||||
presence_info[fred.user_id] = { status: 'active' };
|
presence_info[fred.user_id] = { status: 'active' };
|
||||||
presence_info[jill.user_id] = { status: 'active' };
|
presence_info[jill.user_id] = { status: 'active' };
|
||||||
|
|
||||||
activity.presence_info = presence_info;
|
presence.presence_info = presence_info;
|
||||||
activity._sort_users(user_ids);
|
activity._sort_users(user_ids);
|
||||||
|
|
||||||
assert.deepEqual(user_ids, [
|
assert.deepEqual(user_ids, [
|
||||||
|
@ -192,7 +197,7 @@ global.compile_template('user_presence_rows');
|
||||||
presence_info[fred.user_id] = { status: 'idle' }; // counts as present
|
presence_info[fred.user_id] = { status: 'idle' }; // counts as present
|
||||||
// jill not in list
|
// jill not in list
|
||||||
presence_info[mark.user_id] = { status: 'offline' }; // does not count
|
presence_info[mark.user_id] = { status: 'offline' }; // does not count
|
||||||
activity.presence_info = presence_info;
|
presence.presence_info = presence_info;
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
activity.huddle_fraction_present(huddle),
|
activity.huddle_fraction_present(huddle),
|
||||||
|
@ -201,54 +206,57 @@ global.compile_template('user_presence_rows');
|
||||||
|
|
||||||
|
|
||||||
(function test_on_mobile_property() {
|
(function test_on_mobile_property() {
|
||||||
|
// TODO: move this test to a new test module directly testing presence.js
|
||||||
|
var status_from_timestamp = presence._status_from_timestamp;
|
||||||
|
|
||||||
var base_time = 500;
|
var base_time = 500;
|
||||||
var presence = {
|
var info = {
|
||||||
website: {
|
website: {
|
||||||
status: "active",
|
status: "active",
|
||||||
timestamp: base_time,
|
timestamp: base_time,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
var status = activity._status_from_timestamp(
|
var status = status_from_timestamp(
|
||||||
base_time + activity._OFFLINE_THRESHOLD_SECS - 1, presence);
|
base_time + OFFLINE_THRESHOLD_SECS - 1, info);
|
||||||
assert.equal(status.mobile, false);
|
assert.equal(status.mobile, false);
|
||||||
|
|
||||||
presence.Android = {
|
info.Android = {
|
||||||
status: "active",
|
status: "active",
|
||||||
timestamp: base_time + activity._OFFLINE_THRESHOLD_SECS / 2,
|
timestamp: base_time + OFFLINE_THRESHOLD_SECS / 2,
|
||||||
pushable: false,
|
pushable: false,
|
||||||
};
|
};
|
||||||
status = activity._status_from_timestamp(
|
status = status_from_timestamp(
|
||||||
base_time + activity._OFFLINE_THRESHOLD_SECS, presence);
|
base_time + OFFLINE_THRESHOLD_SECS, info);
|
||||||
assert.equal(status.mobile, true);
|
assert.equal(status.mobile, true);
|
||||||
assert.equal(status.status, "active");
|
assert.equal(status.status, "active");
|
||||||
|
|
||||||
status = activity._status_from_timestamp(
|
status = status_from_timestamp(
|
||||||
base_time + activity._OFFLINE_THRESHOLD_SECS - 1, presence);
|
base_time + OFFLINE_THRESHOLD_SECS - 1, info);
|
||||||
assert.equal(status.mobile, false);
|
assert.equal(status.mobile, false);
|
||||||
assert.equal(status.status, "active");
|
assert.equal(status.status, "active");
|
||||||
|
|
||||||
status = activity._status_from_timestamp(
|
status = status_from_timestamp(
|
||||||
base_time + activity._OFFLINE_THRESHOLD_SECS * 2, presence);
|
base_time + OFFLINE_THRESHOLD_SECS * 2, info);
|
||||||
assert.equal(status.mobile, false);
|
assert.equal(status.mobile, false);
|
||||||
assert.equal(status.status, "offline");
|
assert.equal(status.status, "offline");
|
||||||
|
|
||||||
presence.Android = {
|
info.Android = {
|
||||||
status: "idle",
|
status: "idle",
|
||||||
timestamp: base_time + activity._OFFLINE_THRESHOLD_SECS / 2,
|
timestamp: base_time + OFFLINE_THRESHOLD_SECS / 2,
|
||||||
pushable: true,
|
pushable: true,
|
||||||
};
|
};
|
||||||
status = activity._status_from_timestamp(
|
status = status_from_timestamp(
|
||||||
base_time + activity._OFFLINE_THRESHOLD_SECS, presence);
|
base_time + OFFLINE_THRESHOLD_SECS, info);
|
||||||
assert.equal(status.mobile, true);
|
assert.equal(status.mobile, true);
|
||||||
assert.equal(status.status, "idle");
|
assert.equal(status.status, "idle");
|
||||||
|
|
||||||
status = activity._status_from_timestamp(
|
status = status_from_timestamp(
|
||||||
base_time + activity._OFFLINE_THRESHOLD_SECS - 1, presence);
|
base_time + OFFLINE_THRESHOLD_SECS - 1, info);
|
||||||
assert.equal(status.mobile, false);
|
assert.equal(status.mobile, false);
|
||||||
assert.equal(status.status, "active");
|
assert.equal(status.status, "active");
|
||||||
|
|
||||||
status = activity._status_from_timestamp(
|
status = status_from_timestamp(
|
||||||
base_time + activity._OFFLINE_THRESHOLD_SECS * 2, presence);
|
base_time + OFFLINE_THRESHOLD_SECS * 2, info);
|
||||||
assert.equal(status.mobile, true);
|
assert.equal(status.mobile, true);
|
||||||
assert.equal(status.status, "offline");
|
assert.equal(status.status, "offline");
|
||||||
|
|
||||||
|
@ -272,23 +280,23 @@ global.compile_template('user_presence_rows');
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
activity.set_presence_info(presences, base_time);
|
presence.set_info(presences, base_time);
|
||||||
|
|
||||||
assert.deepEqual(activity.presence_info[alice.user_id],
|
assert.deepEqual(presence.presence_info[alice.user_id],
|
||||||
{ status: 'active', mobile: false, last_active: 500}
|
{ status: 'active', mobile: false, last_active: 500}
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.deepEqual(activity.presence_info[fred.user_id],
|
assert.deepEqual(presence.presence_info[fred.user_id],
|
||||||
{ status: 'idle', mobile: false, last_active: 500}
|
{ status: 'idle', mobile: false, last_active: 500}
|
||||||
);
|
);
|
||||||
}());
|
}());
|
||||||
|
|
||||||
activity.presence_info = {};
|
presence.presence_info = {};
|
||||||
activity.presence_info[alice.user_id] = { status: activity.IDLE };
|
presence.presence_info[alice.user_id] = { status: activity.IDLE };
|
||||||
activity.presence_info[fred.user_id] = { status: activity.ACTIVE };
|
presence.presence_info[fred.user_id] = { status: activity.ACTIVE };
|
||||||
activity.presence_info[jill.user_id] = { status: activity.ACTIVE };
|
presence.presence_info[jill.user_id] = { status: activity.ACTIVE };
|
||||||
activity.presence_info[mark.user_id] = { status: activity.IDLE };
|
presence.presence_info[mark.user_id] = { status: activity.IDLE };
|
||||||
activity.presence_info[norbert.user_id] = { status: activity.ACTIVE };
|
presence.presence_info[norbert.user_id] = { status: activity.ACTIVE };
|
||||||
|
|
||||||
(function test_presence_list_full_update() {
|
(function test_presence_list_full_update() {
|
||||||
global.$ = function () {
|
global.$ = function () {
|
||||||
|
|
|
@ -10,20 +10,6 @@ var DEFAULT_IDLE_TIMEOUT_MS = 5 * 60 * 1000;
|
||||||
/* Time between keep-alive pings */
|
/* Time between keep-alive pings */
|
||||||
var ACTIVE_PING_INTERVAL_MS = 50 * 1000;
|
var ACTIVE_PING_INTERVAL_MS = 50 * 1000;
|
||||||
|
|
||||||
/* Mark users as offline after 140 seconds since their last checkin,
|
|
||||||
* Keep in sync with zerver/tornado/event_queue.py:receiver_is_idle
|
|
||||||
*/
|
|
||||||
var OFFLINE_THRESHOLD_SECS = 140;
|
|
||||||
|
|
||||||
// Testing
|
|
||||||
exports._OFFLINE_THRESHOLD_SECS = OFFLINE_THRESHOLD_SECS;
|
|
||||||
|
|
||||||
var MOBILE_DEVICES = ["Android", "ZulipiOS", "ios"];
|
|
||||||
|
|
||||||
function is_mobile(device) {
|
|
||||||
return MOBILE_DEVICES.indexOf(device) !== -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var presence_descriptions = {
|
var presence_descriptions = {
|
||||||
active: 'is active',
|
active: 'is active',
|
||||||
idle: 'is not active',
|
idle: 'is not active',
|
||||||
|
@ -47,8 +33,6 @@ $("html").on("mousemove", function () {
|
||||||
exports.new_user_input = true;
|
exports.new_user_input = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.presence_info = {};
|
|
||||||
|
|
||||||
var huddle_timestamps = new Dict();
|
var huddle_timestamps = new Dict();
|
||||||
|
|
||||||
function update_count_in_dom(count_span, value_span, count) {
|
function update_count_in_dom(count_span, value_span, count) {
|
||||||
|
@ -165,16 +149,12 @@ exports.short_huddle_name = function (huddle) {
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.huddle_fraction_present = function (huddle) {
|
exports.huddle_fraction_present = function (huddle) {
|
||||||
var presence_info = exports.presence_info;
|
|
||||||
var user_ids = huddle.split(',');
|
var user_ids = huddle.split(',');
|
||||||
|
|
||||||
var num_present = 0;
|
var num_present = 0;
|
||||||
_.each(user_ids, function (user_id) {
|
_.each(user_ids, function (user_id) {
|
||||||
if (presence_info[user_id]) {
|
if (presence.is_not_offline(user_id)) {
|
||||||
var status = presence_info[user_id].status;
|
num_present += 1;
|
||||||
if (status && (status !== 'offline')) {
|
|
||||||
num_present += 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -184,7 +164,6 @@ exports.huddle_fraction_present = function (huddle) {
|
||||||
};
|
};
|
||||||
|
|
||||||
function compare_function(a, b) {
|
function compare_function(a, b) {
|
||||||
var presence_info = exports.presence_info;
|
|
||||||
|
|
||||||
function level(status) {
|
function level(status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
|
@ -197,8 +176,8 @@ function compare_function(a, b) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var level_a = level(presence_info[a].status);
|
var level_a = level(presence.get_status(a));
|
||||||
var level_b = level(presence_info[b].status);
|
var level_b = level(presence.get_status(b));
|
||||||
var diff = level_a - level_b;
|
var diff = level_a - level_b;
|
||||||
if (diff !== 0) {
|
if (diff !== 0) {
|
||||||
return diff;
|
return diff;
|
||||||
|
@ -264,8 +243,7 @@ function matches_filter(user_id) {
|
||||||
return (filter_user_ids([user_id]).length === 1);
|
return (filter_user_ids([user_id]).length === 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function filter_and_sort(users) {
|
function filter_and_sort(user_ids) {
|
||||||
var user_ids = Object.keys(users);
|
|
||||||
user_ids = filter_user_ids(user_ids);
|
user_ids = filter_user_ids(user_ids);
|
||||||
user_ids = sort_users(user_ids);
|
user_ids = sort_users(user_ids);
|
||||||
return user_ids;
|
return user_ids;
|
||||||
|
@ -281,16 +259,16 @@ function get_num_unread(user_id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function info_for(user_id) {
|
function info_for(user_id) {
|
||||||
var presence = exports.presence_info[user_id].status;
|
var status = presence.get_status(user_id);
|
||||||
var person = people.get_person_from_user_id(user_id);
|
var person = people.get_person_from_user_id(user_id);
|
||||||
return {
|
return {
|
||||||
href: narrow.pm_with_uri(person.email),
|
href: narrow.pm_with_uri(person.email),
|
||||||
name: person.full_name,
|
name: person.full_name,
|
||||||
user_id: user_id,
|
user_id: user_id,
|
||||||
num_unread: get_num_unread(user_id),
|
num_unread: get_num_unread(user_id),
|
||||||
type: presence,
|
type: status,
|
||||||
type_desc: presence_descriptions[presence],
|
type_desc: presence_descriptions[status],
|
||||||
mobile: exports.presence_info[user_id].mobile,
|
mobile: presence.get_mobile(user_id),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,10 +313,9 @@ exports.build_user_sidebar = function () {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var users = exports.presence_info;
|
var user_ids = filter_and_sort(presence.get_user_ids());
|
||||||
users = filter_and_sort(users);
|
|
||||||
|
|
||||||
var user_info = _.map(users, info_for);
|
var user_info = _.map(user_ids, info_for);
|
||||||
var html = templates.render('user_presence_rows', {users: user_info});
|
var html = templates.render('user_presence_rows', {users: user_info});
|
||||||
$('#user_presences').html(html);
|
$('#user_presences').html(html);
|
||||||
|
|
||||||
|
@ -398,51 +375,6 @@ exports.update_huddles = function () {
|
||||||
show_huddles();
|
show_huddles();
|
||||||
};
|
};
|
||||||
|
|
||||||
function status_from_timestamp(baseline_time, presence) {
|
|
||||||
var status = 'offline';
|
|
||||||
var last_active = 0;
|
|
||||||
var mobileAvailable = false;
|
|
||||||
var nonmobileAvailable = false;
|
|
||||||
_.each(presence, function (device_presence, device) {
|
|
||||||
var age = baseline_time - device_presence.timestamp;
|
|
||||||
if (last_active < device_presence.timestamp) {
|
|
||||||
last_active = device_presence.timestamp;
|
|
||||||
}
|
|
||||||
if (is_mobile(device)) {
|
|
||||||
mobileAvailable = device_presence.pushable || mobileAvailable;
|
|
||||||
}
|
|
||||||
if (age < OFFLINE_THRESHOLD_SECS) {
|
|
||||||
switch (device_presence.status) {
|
|
||||||
case 'active':
|
|
||||||
if (is_mobile(device)) {
|
|
||||||
mobileAvailable = true;
|
|
||||||
} else {
|
|
||||||
nonmobileAvailable = true;
|
|
||||||
}
|
|
||||||
status = device_presence.status;
|
|
||||||
break;
|
|
||||||
case 'idle':
|
|
||||||
if (status !== 'active') {
|
|
||||||
status = device_presence.status;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'offline':
|
|
||||||
if (status !== 'active' && status !== 'idle') {
|
|
||||||
status = device_presence.status;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
blueslip.error('Unexpected status', {presence_object: device_presence, device: device}, undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return {status: status,
|
|
||||||
mobile: !nonmobileAvailable && mobileAvailable,
|
|
||||||
last_active: last_active };
|
|
||||||
}
|
|
||||||
|
|
||||||
// For testing
|
|
||||||
exports._status_from_timestamp = status_from_timestamp;
|
|
||||||
|
|
||||||
function focus_ping(want_redraw) {
|
function focus_ping(want_redraw) {
|
||||||
channel.post({
|
channel.post({
|
||||||
|
@ -465,7 +397,7 @@ function focus_ping(want_redraw) {
|
||||||
// not send us any presences data. But avoiding the redraw
|
// not send us any presences data. But avoiding the redraw
|
||||||
// helps.
|
// helps.
|
||||||
if (want_redraw) {
|
if (want_redraw) {
|
||||||
exports.set_presence_info(data.presences, data.server_timestamp);
|
presence.set_info(data.presences, data.server_timestamp);
|
||||||
exports.build_user_sidebar();
|
exports.build_user_sidebar();
|
||||||
exports.update_huddles();
|
exports.update_huddles();
|
||||||
}
|
}
|
||||||
|
@ -487,7 +419,7 @@ exports.initialize = function () {
|
||||||
onActive: focus_gained,
|
onActive: focus_gained,
|
||||||
keepTracking: true});
|
keepTracking: true});
|
||||||
|
|
||||||
activity.set_presence_info(page_params.initial_presences,
|
presence.set_info(page_params.initial_presences,
|
||||||
page_params.initial_servertime);
|
page_params.initial_servertime);
|
||||||
exports.build_user_sidebar();
|
exports.build_user_sidebar();
|
||||||
exports.update_huddles();
|
exports.update_huddles();
|
||||||
|
@ -503,36 +435,22 @@ exports.initialize = function () {
|
||||||
setInterval(get_full_presence_list_update, ACTIVE_PING_INTERVAL_MS);
|
setInterval(get_full_presence_list_update, ACTIVE_PING_INTERVAL_MS);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.set_user_status = function (email, presence, server_time) {
|
exports.set_user_status = function (email, info, server_time) {
|
||||||
if (people.is_current_user(email)) {
|
if (people.is_current_user(email)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var user_id = people.get_user_id(email);
|
var user_id = people.get_user_id(email);
|
||||||
if (user_id) {
|
if (!user_id) {
|
||||||
var status = status_from_timestamp(server_time, presence);
|
|
||||||
exports.presence_info[user_id] = status;
|
|
||||||
exports.insert_user_into_list(user_id);
|
|
||||||
} else {
|
|
||||||
blueslip.warn('unknown email: ' + email);
|
blueslip.warn('unknown email: ' + email);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
presence.set_user_status(user_id, info, server_time);
|
||||||
|
exports.insert_user_into_list(user_id);
|
||||||
exports.update_huddles();
|
exports.update_huddles();
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.set_presence_info = function (presences, server_timestamp) {
|
|
||||||
exports.presence_info = {};
|
|
||||||
_.each(presences, function (presence, this_email) {
|
|
||||||
if (!people.is_current_user(this_email)) {
|
|
||||||
var user_id = people.get_user_id(this_email);
|
|
||||||
if (user_id) {
|
|
||||||
var status = status_from_timestamp(server_timestamp,
|
|
||||||
presence);
|
|
||||||
exports.presence_info[user_id] = status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.redraw = function () {
|
exports.redraw = function () {
|
||||||
exports.build_user_sidebar();
|
exports.build_user_sidebar();
|
||||||
exports.update_huddles();
|
exports.update_huddles();
|
||||||
|
|
|
@ -139,12 +139,13 @@ function populate_users(realm_people_data) {
|
||||||
var row = $(templates.render("admin_user_list", {user: user}));
|
var row = $(templates.render("admin_user_list", {user: user}));
|
||||||
if (people.is_current_user(user.email)) {
|
if (people.is_current_user(user.email)) {
|
||||||
activity_rendered = timerender.render_date(new XDate());
|
activity_rendered = timerender.render_date(new XDate());
|
||||||
} else if (activity.presence_info[user.user_id]) {
|
|
||||||
// XDate takes number of milliseconds since UTC epoch.
|
|
||||||
var last_active = activity.presence_info[user.user_id].last_active * 1000;
|
|
||||||
activity_rendered = timerender.render_date(new XDate(last_active));
|
|
||||||
} else {
|
} else {
|
||||||
activity_rendered = $("<span></span>").text(i18n.t("Never"));
|
var last_active_date = presence.last_active_date(user.user_id);
|
||||||
|
if (last_active_date) {
|
||||||
|
activity_rendered = timerender.render_date(last_active_date);
|
||||||
|
} else {
|
||||||
|
activity_rendered = $("<span></span>").text(i18n.t("Never"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
row.find(".last_active").append(activity_rendered);
|
row.find(".last_active").append(activity_rendered);
|
||||||
users_table.append(row);
|
users_table.append(row);
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
var presence = (function () {
|
||||||
|
var exports = {};
|
||||||
|
|
||||||
|
// This module just manages data. See activity.js for
|
||||||
|
// the UI of our buddy list.
|
||||||
|
|
||||||
|
exports.presence_info = {};
|
||||||
|
|
||||||
|
|
||||||
|
/* Mark users as offline after 140 seconds since their last checkin,
|
||||||
|
* Keep in sync with zerver/tornado/event_queue.py:receiver_is_idle
|
||||||
|
*/
|
||||||
|
var OFFLINE_THRESHOLD_SECS = 140;
|
||||||
|
|
||||||
|
var MOBILE_DEVICES = ["Android", "ZulipiOS", "ios"];
|
||||||
|
|
||||||
|
function is_mobile(device) {
|
||||||
|
return MOBILE_DEVICES.indexOf(device) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.is_not_offline = function (user_id) {
|
||||||
|
var presence_info = exports.presence_info;
|
||||||
|
|
||||||
|
if (presence_info[user_id]) {
|
||||||
|
var status = presence_info[user_id].status;
|
||||||
|
if (status && (status !== 'offline')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.get_status = function (user_id) {
|
||||||
|
return exports.presence_info[user_id].status;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.get_mobile = function (user_id) {
|
||||||
|
return exports.presence_info[user_id].mobile;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.get_user_ids = function () {
|
||||||
|
var user_ids = Object.keys(exports.presence_info);
|
||||||
|
return user_ids;
|
||||||
|
};
|
||||||
|
|
||||||
|
function status_from_timestamp(baseline_time, info) {
|
||||||
|
var status = 'offline';
|
||||||
|
var last_active = 0;
|
||||||
|
var mobileAvailable = false;
|
||||||
|
var nonmobileAvailable = false;
|
||||||
|
_.each(info, function (device_presence, device) {
|
||||||
|
var age = baseline_time - device_presence.timestamp;
|
||||||
|
if (last_active < device_presence.timestamp) {
|
||||||
|
last_active = device_presence.timestamp;
|
||||||
|
}
|
||||||
|
if (is_mobile(device)) {
|
||||||
|
mobileAvailable = device_presence.pushable || mobileAvailable;
|
||||||
|
}
|
||||||
|
if (age < OFFLINE_THRESHOLD_SECS) {
|
||||||
|
switch (device_presence.status) {
|
||||||
|
case 'active':
|
||||||
|
if (is_mobile(device)) {
|
||||||
|
mobileAvailable = true;
|
||||||
|
} else {
|
||||||
|
nonmobileAvailable = true;
|
||||||
|
}
|
||||||
|
status = device_presence.status;
|
||||||
|
break;
|
||||||
|
case 'idle':
|
||||||
|
if (status !== 'active') {
|
||||||
|
status = device_presence.status;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'offline':
|
||||||
|
if (status !== 'active' && status !== 'idle') {
|
||||||
|
status = device_presence.status;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
blueslip.error('Unexpected status', {presence_object: device_presence, device: device}, undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {status: status,
|
||||||
|
mobile: !nonmobileAvailable && mobileAvailable,
|
||||||
|
last_active: last_active };
|
||||||
|
}
|
||||||
|
|
||||||
|
// For testing
|
||||||
|
exports._status_from_timestamp = status_from_timestamp;
|
||||||
|
|
||||||
|
exports.set_user_status = function (user_id, info, server_time) {
|
||||||
|
var status = status_from_timestamp(server_time, info);
|
||||||
|
exports.presence_info[user_id] = status;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.set_info = function (presences, server_timestamp) {
|
||||||
|
exports.presence_info = {};
|
||||||
|
_.each(presences, function (info, this_email) {
|
||||||
|
if (!people.is_current_user(this_email)) {
|
||||||
|
var user_id = people.get_user_id(this_email);
|
||||||
|
if (user_id) {
|
||||||
|
var status = status_from_timestamp(server_timestamp,
|
||||||
|
info);
|
||||||
|
exports.presence_info[user_id] = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.last_active_date = function (user_id) {
|
||||||
|
var info = exports.presence_info[user_id];
|
||||||
|
|
||||||
|
if (!info || !info.last_active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var date = new XDate(info.last_active * 1000);
|
||||||
|
return date;
|
||||||
|
};
|
||||||
|
|
||||||
|
return exports;
|
||||||
|
|
||||||
|
}());
|
||||||
|
if (typeof module !== 'undefined') {
|
||||||
|
module.exports = presence;
|
||||||
|
}
|
|
@ -677,7 +677,7 @@ def receiver_is_idle(user_profile_id, realm_presences):
|
||||||
idle = True
|
idle = True
|
||||||
else:
|
else:
|
||||||
active_datetime = timestamp_to_datetime(latest_active_timestamp)
|
active_datetime = timestamp_to_datetime(latest_active_timestamp)
|
||||||
# 140 seconds is consistent with activity.js:OFFLINE_THRESHOLD_SECS
|
# 140 seconds is consistent with presence.js:OFFLINE_THRESHOLD_SECS
|
||||||
idle = timezone.now() - active_datetime > datetime.timedelta(seconds=140)
|
idle = timezone.now() - active_datetime > datetime.timedelta(seconds=140)
|
||||||
|
|
||||||
return off_zulip or idle
|
return off_zulip or idle
|
||||||
|
|
|
@ -893,6 +893,7 @@ JS_SPECS = {
|
||||||
'js/message_fetch.js',
|
'js/message_fetch.js',
|
||||||
'js/server_events.js',
|
'js/server_events.js',
|
||||||
'js/zulip.js',
|
'js/zulip.js',
|
||||||
|
'js/presence.js',
|
||||||
'js/activity.js',
|
'js/activity.js',
|
||||||
'js/user_events.js',
|
'js/user_events.js',
|
||||||
'js/colorspace.js',
|
'js/colorspace.js',
|
||||||
|
|
Loading…
Reference in New Issue