Show a mobile icon in the presence list when relevant

We show a user as "on mobile" if:
 * They are only active on mobile
 * They are inactive on all devices and can receive push notifications

(imported from commit 0510b9371727cd19c72f6990df7112921c36ad48)
This commit is contained in:
Luke Faraone 2014-02-26 19:46:17 -05:00 committed by Steve Howell
parent 4397c25976
commit bd20a756f9
5 changed files with 124 additions and 11 deletions

View File

@ -13,6 +13,14 @@ var ACTIVE_PING_INTERVAL_MS = 50 * 1000;
/* Mark users as offline after 140 seconds since their last checkin */ /* Mark users as offline after 140 seconds since their last checkin */
var OFFLINE_THRESHOLD_SECS = 140; var OFFLINE_THRESHOLD_SECS = 140;
// Testing
exports._OFFLINE_THRESHOLD_SECS = OFFLINE_THRESHOLD_SECS;
var MOBILE_DEVICES = ["Android", "IOS"];
function is_mobile(device) {
return MOBILE_DEVICES.indexOf(device) !== -1;
}
var presence_descriptions = { var presence_descriptions = {
active: 'is active', active: 'is active',
@ -41,6 +49,7 @@ var presence_info = {};
var huddle_timestamps = new Dict(); var huddle_timestamps = new Dict();
exports.process_loaded_messages = function (messages) { exports.process_loaded_messages = function (messages) {
_.each(messages, function (message) { _.each(messages, function (message) {
if (message.type === 'private') { if (message.type === 'private') {
@ -222,7 +231,8 @@ function actually_update_users() {
email: email, email: email,
num_unread: get_num_unread(email), num_unread: get_num_unread(email),
type: presence, type: presence,
type_desc: presence_descriptions[presence] type_desc: presence_descriptions[presence],
mobile: presence_info[email].mobile
}; };
} }
@ -285,19 +295,45 @@ exports.update_huddles = function () {
}; };
function status_from_timestamp(baseline_time, presence) { function status_from_timestamp(baseline_time, presence) {
if (presence.website === undefined) {
return {status: 'offline'};
}
var age = baseline_time - presence.website.timestamp;
var status = 'offline'; var status = 'offline';
if (age < OFFLINE_THRESHOLD_SECS) { var mobileAvailable = false;
status = presence.website.status; var nonmobileAvailable = false;
} _.each(presence, function (device_presence, device) {
return {status: status}; var age = baseline_time - 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 };
} }
// For testing
exports._status_from_timestamp = status_from_timestamp;
function focus_ping() { function focus_ping() {
channel.post({ channel.post({
url: '/json/update_active_status', url: '/json/update_active_status',

View File

@ -825,6 +825,9 @@ a .icon-vector-flip-vertical:before {
.icon-vector-lemon:before { .icon-vector-lemon:before {
content: "\f094"; content: "\f094";
} }
.icon-vector-mobile:before {
content: "\f10b";
}
.icon-vector-phone:before { .icon-vector-phone:before {
content: "\f095"; content: "\f095";
} }

View File

@ -444,6 +444,18 @@ a:hover code {
border-color: gray; border-color: gray;
} }
.user-device-indicator {
display: block;
width: 8px;
height: 8px;
border-radius: 50%;
float: left;
position: relative;
top: 5px;
margin-right: 0.5em;
content: "";
}
#user_presences a, #group-pms a { #user_presences a, #group-pms a {
color: #333; color: #333;
} }

View File

@ -3,6 +3,7 @@
<li data-email="{{email}}" class="user_sidebar_entry {{#if num_unread}}user-with-count {{/if}}narrow-filter user_{{type}}"> <li data-email="{{email}}" class="user_sidebar_entry {{#if num_unread}}user-with-count {{/if}}narrow-filter user_{{type}}">
<span class="selectable_sidebar_block"> <span class="selectable_sidebar_block">
<span class="user-status-indicator"></span> <span class="user-status-indicator"></span>
<span class="user-device-indicator{{#if mobile}} icon-vector-mobile{{/if}}"></span>
<a href="#" data-email="{{email}}" data-name="{{name}}" title="{{name}} {{type_desc}}" <a href="#" data-email="{{email}}" data-name="{{name}}" title="{{name}} {{type_desc}}"
class="{{#if my_fullname}} my_fullname{{/if}}">{{name}}</a> class="{{#if my_fullname}} my_fullname{{/if}}">{{name}}</a>
</span> </span>

View File

@ -153,3 +153,64 @@ var activity = require('js/activity.js');
); );
}()); }());
(function test_on_mobile_property() {
var base_time = 500;
var presence = {
website: {
status: "active",
timestamp: base_time
}
};
var status = activity._status_from_timestamp(
base_time + activity._OFFLINE_THRESHOLD_SECS - 1, presence
);
assert.equal(status.mobile, false);
presence.Android = {
status: "active",
timestamp: base_time + activity._OFFLINE_THRESHOLD_SECS / 2,
pushable: false
};
status = activity._status_from_timestamp(
base_time + activity._OFFLINE_THRESHOLD_SECS, presence
);
assert.equal(status.mobile, true);
assert.equal(status.status, "active");
status = activity._status_from_timestamp(
base_time + activity._OFFLINE_THRESHOLD_SECS - 1, presence
);
assert.equal(status.mobile, false);
assert.equal(status.status, "active");
status = activity._status_from_timestamp(
base_time + activity._OFFLINE_THRESHOLD_SECS * 2, presence
);
assert.equal(status.mobile, false);
assert.equal(status.status, "offline");
presence.Android = {
status: "idle",
timestamp: base_time + activity._OFFLINE_THRESHOLD_SECS / 2,
pushable: true
};
status = activity._status_from_timestamp(
base_time + activity._OFFLINE_THRESHOLD_SECS, presence
);
assert.equal(status.mobile, true);
assert.equal(status.status, "idle");
status = activity._status_from_timestamp(
base_time + activity._OFFLINE_THRESHOLD_SECS - 1, presence
);
assert.equal(status.mobile, false);
assert.equal(status.status, "active");
status = activity._status_from_timestamp(
base_time + activity._OFFLINE_THRESHOLD_SECS * 2, presence
);
assert.equal(status.mobile, true);
assert.equal(status.status, "offline");
}());