presence: Simplify payload for webapp.

This changes the payload that is used
to populate `page_params` for the webapp,
as well as responses to the once-every-50-seconds
presence pings.

Now our dictionary of users only has these
two fields in the value:

    - activity_timestamp
    - idle_timestamp

Example data:

    {
        6: Object { idle_timestamp: 1585746028 },
        7: Object { active_timestamp: 1585745774 },
        8: Object { active_timestamp: 1585745578,
                    idle_timestamp: 1585745400}
    }

We only send the slimmer type of payload
to clients that have set `slim_presence`
to True.

Note that this commit does not change the format
of the event data, which still looks like this:

    {
        website: {
            client: 'website',
            pushable: false,
            status: 'active',
            timestamp: 1585745225
        }
    }
This commit is contained in:
Steve Howell
2020-02-07 13:50:30 +00:00
committed by Tim Abbott
parent da6508a7af
commit 1ae07b93d8
4 changed files with 350 additions and 161 deletions

View File

@@ -41,53 +41,108 @@ exports.get_user_ids = function () {
return Array.from(exports.presence_info.keys());
};
function status_from_timestamp(baseline_time, info) {
let status = 'offline';
let last_active = 0;
exports.status_from_raw = function (raw) {
/*
Example of `raw`:
for (const [device, device_presence] of Object.entries(info)) {
const age = baseline_time - device_presence.timestamp;
if (last_active < device_presence.timestamp) {
last_active = device_presence.timestamp;
{
active_timestamp: 1585745133
idle_timestamp: 1585745091
server_timestamp: 1585745140
}
if (age < OFFLINE_THRESHOLD_SECS) {
switch (device_presence.status) {
case 'active':
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);
*/
function age(timestamp) {
return raw.server_timestamp - (timestamp || 0);
}
const active_timestamp = raw.active_timestamp;
const idle_timestamp = raw.idle_timestamp;
/*
If the server sends us `active_timestamp`, this
means at least one client was active at this time
(and hasn't changed since).
As long as the timestamp is current enough, we will
show the user as active (even if there's a newer
timestamp for idle).
*/
if (age(active_timestamp) < OFFLINE_THRESHOLD_SECS) {
return {
status: 'active',
last_active: active_timestamp,
};
}
if (age(idle_timestamp) < OFFLINE_THRESHOLD_SECS) {
return {
status: 'idle',
last_active: active_timestamp,
};
}
return {
status: 'offline',
last_active: active_timestamp,
};
};
exports.update_info_from_event = function (user_id, info, server_timestamp) {
/*
Example of `info`:
{
website: {
client: 'website',
pushable: false,
status: 'active',
timestamp: 1585745225
}
}
Example of `raw`:
{
active_timestamp: 1585745133
idle_timestamp: 1585745091
server_timestamp: 1585745140
}
*/
const raw = raw_info.get(user_id) || {};
raw.server_timestamp = server_timestamp;
for (const rec of Object.values(info)) {
if (rec.status === 'active') {
if (rec.timestamp > (raw.active_timestamp || 0)) {
raw.active_timestamp = rec.timestamp;
}
}
if (rec.status === 'idle') {
if (rec.timestamp > (raw.idle_timestamp || 0)) {
raw.idle_timestamp = rec.timestamp;
}
}
}
return {status: status,
last_active: last_active };
}
// For testing
exports._status_from_timestamp = status_from_timestamp;
raw_info.set(user_id, raw);
exports.update_info_from_event = function (user_id, info, server_time) {
raw_info.set(user_id, {
info: info,
server_time: server_time,
});
const status = status_from_timestamp(server_time, info);
const status = exports.status_from_raw(raw);
exports.presence_info.set(user_id, status);
};
exports.set_info = function (presences, server_timestamp) {
/*
Example `presences` data:
{
6: Object { idle_timestamp: 1585746028 },
7: Object { active_timestamp: 1585745774 },
8: Object { active_timestamp: 1585745578 }
}
*/
raw_info.clear();
exports.presence_info.clear();
for (const [user_id_str, info] of Object.entries(presences)) {
@@ -125,14 +180,15 @@ exports.set_info = function (presences, server_timestamp) {
continue;
}
raw_info.set(user_id, {
info: info,
server_time: server_timestamp,
});
const raw = {
server_timestamp: server_timestamp,
active_timestamp: info.active_timestamp || undefined,
idle_timestamp: info.idle_timestamp || undefined,
};
const status = status_from_timestamp(server_timestamp,
info);
raw_info.set(user_id, raw);
const status = exports.status_from_raw(raw);
exports.presence_info.set(user_id, status);
}
exports.update_info_for_small_realm();