* VRFS-1473 - notifications should be highlighted, wip
This commit is contained in:
parent
d365054237
commit
526f6fe577
|
|
@ -137,4 +137,4 @@ cascading_delete_constraints_for_release.sql
|
||||||
events_social_description.sql
|
events_social_description.sql
|
||||||
fix_broken_cities.sql
|
fix_broken_cities.sql
|
||||||
notifications_with_text.sql
|
notifications_with_text.sql
|
||||||
|
notification_seen_at.sql
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE users ADD COLUMN notification_seen_at TIMESTAMP;
|
||||||
|
|
@ -472,6 +472,7 @@ message TestClientMessage {
|
||||||
// sent from client to server periodically to let server track if the client is truly alive and avoid TCP timeout scenarios
|
// sent from client to server periodically to let server track if the client is truly alive and avoid TCP timeout scenarios
|
||||||
// the server will send a HeartbeatAck in response to this
|
// the server will send a HeartbeatAck in response to this
|
||||||
message Heartbeat {
|
message Heartbeat {
|
||||||
|
optional string notification_seen_at = 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -291,7 +291,12 @@ module JamRuby
|
||||||
recordings.concat(msh)
|
recordings.concat(msh)
|
||||||
recordings.sort! {|a,b| b.created_at <=> a.created_at}.first(5)
|
recordings.sort! {|a,b| b.created_at <=> a.created_at}.first(5)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# returns the # of new notifications
|
||||||
|
def new_notifications
|
||||||
|
Notification.select('id').where(target_user_id: id).where('created_at > ?', notification_seen_at).count
|
||||||
|
end
|
||||||
|
|
||||||
def confirm_email!
|
def confirm_email!
|
||||||
self.email_confirmed = true
|
self.email_confirmed = true
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,9 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
// Heartbeat message
|
// Heartbeat message
|
||||||
factory.heartbeat = function() {
|
factory.heartbeat = function(notification_last_seen_at) {
|
||||||
var data = {};
|
var data = {};
|
||||||
|
data.notification_last_seen_at = notification_last_seen_at;
|
||||||
return client_container(msg.HEARTBEAT, route_to.SERVER, data);
|
return client_container(msg.HEARTBEAT, route_to.SERVER, data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
//= require jquery.infinitescroll
|
//= require jquery.infinitescroll
|
||||||
//= require jquery.hoverIntent
|
//= require jquery.hoverIntent
|
||||||
//= require jquery.dotdotdot
|
//= require jquery.dotdotdot
|
||||||
|
//= require jquery.pulse
|
||||||
//= require AAA_Log
|
//= require AAA_Log
|
||||||
//= require globals
|
//= require globals
|
||||||
//= require AAB_message_factory
|
//= require AAB_message_factory
|
||||||
|
|
|
||||||
|
|
@ -934,6 +934,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNotifications(options) {
|
function getNotifications(options) {
|
||||||
|
if(!options) options = {};
|
||||||
var id = getId(options);
|
var id = getId(options);
|
||||||
return $.ajax({
|
return $.ajax({
|
||||||
type: "GET",
|
type: "GET",
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
var lastHeartbeatFound = false;
|
var lastHeartbeatFound = false;
|
||||||
var heartbeatAckCheckInterval = null;
|
var heartbeatAckCheckInterval = null;
|
||||||
var userDeferred = null;
|
var userDeferred = null;
|
||||||
|
var notificationLastSeenAt = undefined;
|
||||||
|
|
||||||
var opts = {
|
var opts = {
|
||||||
inClient: true, // specify false if you want the app object but none of the client-oriented features
|
inClient: true, // specify false if you want the app object but none of the client-oriented features
|
||||||
|
|
@ -92,7 +93,8 @@
|
||||||
function _heartbeat() {
|
function _heartbeat() {
|
||||||
if (app.heartbeatActive) {
|
if (app.heartbeatActive) {
|
||||||
|
|
||||||
var message = context.JK.MessageFactory.heartbeat();
|
var message = context.JK.MessageFactory.heartbeat(notificationLastSeenAt);
|
||||||
|
notificationLastSeenAt = undefined;
|
||||||
context.JK.JamServer.send(message);
|
context.JK.JamServer.send(message);
|
||||||
lastHeartbeatFound = false;
|
lastHeartbeatFound = false;
|
||||||
}
|
}
|
||||||
|
|
@ -384,6 +386,26 @@
|
||||||
return userDeferred;
|
return userDeferred;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.updateNotificationSeen = function(notificationCreatedAt) {
|
||||||
|
var time = new Date(notificationCreatedAt);
|
||||||
|
|
||||||
|
if(!notificationCreatedAt) {
|
||||||
|
throw 'invalid value passed to updateNotificationSeen'
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!notificationLastSeenAt) {
|
||||||
|
notificationLastSeenAt = notificationCreatedAt;
|
||||||
|
logger.debug("updated notificationLastSeenAt with: " + notificationCreatedAt);
|
||||||
|
}
|
||||||
|
else if(time.getTime() > new Date(notificationLastSeenAt).getTime()) {
|
||||||
|
notificationLastSeenAt = notificationCreatedAt;
|
||||||
|
logger.debug("updated notificationLastSeenAt with: " + notificationCreatedAt);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.debug("ignored notificationLastSeenAt for: " + notificationCreatedAt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.unloadFunction = function () {
|
this.unloadFunction = function () {
|
||||||
logger.debug("window.unload function called.");
|
logger.debug("window.unload function called.");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -425,6 +425,7 @@
|
||||||
unstackDialogs($overlay);
|
unstackDialogs($overlay);
|
||||||
$dialog.hide();
|
$dialog.hide();
|
||||||
dialogEvent(dialog, 'afterHide');
|
dialogEvent(dialog, 'afterHide');
|
||||||
|
$(me).triggerHandler('dialog_closed', {dialogCount: openDialogs.length})
|
||||||
}
|
}
|
||||||
|
|
||||||
function screenEvent(screen, evtName, data) {
|
function screenEvent(screen, evtName, data) {
|
||||||
|
|
@ -526,6 +527,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isDialogShowing() {
|
||||||
|
return openDialogs.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responsible for keeping N dialogs in correct stacked order,
|
* Responsible for keeping N dialogs in correct stacked order,
|
||||||
* also moves the .dialog-overlay such that it hides/obscures all dialogs except the highest one
|
* also moves the .dialog-overlay such that it hides/obscures all dialogs except the highest one
|
||||||
|
|
@ -849,6 +854,10 @@
|
||||||
showDialog(dialog, options);
|
showDialog(dialog, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.isDialogShowing = function() {
|
||||||
|
return isDialogShowing();
|
||||||
|
}
|
||||||
|
|
||||||
this.close = function (evt) {
|
this.close = function (evt) {
|
||||||
close(evt);
|
close(evt);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
(function(context,$) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
context.JK = context.JK || {};
|
||||||
|
context.JK.NotificationPanel = function(app) {
|
||||||
|
var logger = context.JK.logger;
|
||||||
|
var friends = [];
|
||||||
|
var rest = context.JK.Rest();
|
||||||
|
var missedNotificationsWhileAway = false;
|
||||||
|
var $panel = null;
|
||||||
|
var $expanded = null;
|
||||||
|
var $count = null;
|
||||||
|
var darkenedColor = '#0D7B89';
|
||||||
|
var highlightedColor = 'white'
|
||||||
|
|
||||||
|
|
||||||
|
// one important limitation; if the user is focused on an iframe, this will be false
|
||||||
|
// however, if they are doing something with Facebook or the photo picker, this may actually still be desirable
|
||||||
|
function userCanSeeNotifications() {
|
||||||
|
return document.hasFocus() || app.layout.isDialogShowing();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNotificationsPanelVisible() {
|
||||||
|
return $expanded.is(':visible')
|
||||||
|
}
|
||||||
|
|
||||||
|
function incrementNotificationCount() {
|
||||||
|
var count = parseInt($count.text());
|
||||||
|
$count.text(count + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the element to white, and pulse it down to the un-highlighted value 2x, then set
|
||||||
|
function pulseToDark() {
|
||||||
|
lowlightCount();
|
||||||
|
$count.pulse({'background-color' : highlightedColor}, {pulses: 2}, function() {
|
||||||
|
$count.text('0');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function lowlightCount() {
|
||||||
|
$count.removeClass('highlighted');
|
||||||
|
}
|
||||||
|
|
||||||
|
function highlightCount() {
|
||||||
|
$count.addClass('highlighted');
|
||||||
|
}
|
||||||
|
|
||||||
|
function onNotificationOccurred(payload) {
|
||||||
|
if(userCanSeeNotifications()) {
|
||||||
|
app.updateNotificationSeen(payload.created_at);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
highlightCount();
|
||||||
|
incrementNotificationCount();
|
||||||
|
missedNotificationsWhileAway = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function userCameBack() {
|
||||||
|
if(isNotificationsPanelVisible()) {
|
||||||
|
if(missedNotificationsWhileAway) {
|
||||||
|
// catch user's eye, then put count to 0
|
||||||
|
pulseToDark();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
missedNotificationsWhileAway = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function windowBlurred() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function events() {
|
||||||
|
$(app.layout).on('dialog_closed', function(e, data) {if(data.dialogCount == 0) userCameBack(); });
|
||||||
|
$(window).focus(userCameBack);
|
||||||
|
$(window).blur(windowBlurred);
|
||||||
|
}
|
||||||
|
|
||||||
|
function populate() {
|
||||||
|
// retrieve pending notifications for this user
|
||||||
|
rest.getNotifications()
|
||||||
|
.done(function(response) {
|
||||||
|
updateNotificationList(response);
|
||||||
|
})
|
||||||
|
.fail(app.ajaxError)
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize(sidebar) {
|
||||||
|
$panel = $('[layout-id="panelNotifications"]');
|
||||||
|
$expanded = $panel.find('.panel.expanded');
|
||||||
|
$count = $panel.find('#sidebar-notification-count');
|
||||||
|
if($panel.length == 0) throw "notifications panel not found"
|
||||||
|
if($expanded.length == 0) throw "notifications expanded content not found"
|
||||||
|
if($count.length == 0) throw "notifications count element not found";
|
||||||
|
|
||||||
|
events();
|
||||||
|
|
||||||
|
populate();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.initiliaze = initialize;
|
||||||
|
this.onNotificationOccurred = onNotificationOccurred;
|
||||||
|
};
|
||||||
|
})(window, jQuery);
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
var rest = context.JK.Rest();
|
var rest = context.JK.Rest();
|
||||||
var invitationDialog = null;
|
var invitationDialog = null;
|
||||||
var textMessageDialog = null;
|
var textMessageDialog = null;
|
||||||
|
var notificationPanel = null;
|
||||||
|
|
||||||
function initializeSearchPanel() {
|
function initializeSearchPanel() {
|
||||||
$('#search_text_type').change(function() {
|
$('#search_text_type').change(function() {
|
||||||
|
|
@ -116,23 +117,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function initializeNotificationsPanel() {
|
function initializeNotificationsPanel() {
|
||||||
// retrieve pending notifications for this user
|
notificationPanel = new context.JK.NotificationPanel(app);
|
||||||
var url = "/api/users/" + context.JK.currentUserId + "/notifications"
|
notificationPanel.initialize();
|
||||||
$.ajax({
|
|
||||||
type: "GET",
|
|
||||||
dataType: "json",
|
|
||||||
contentType: 'application/json',
|
|
||||||
url: url,
|
|
||||||
processData: false,
|
|
||||||
success: function(response) {
|
|
||||||
|
|
||||||
updateNotificationList(response);
|
|
||||||
|
|
||||||
// set notification count
|
|
||||||
$('#sidebar-notification-count').html(response.length);
|
|
||||||
},
|
|
||||||
error: app.ajaxError
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateNotificationList(response) {
|
function updateNotificationList(response) {
|
||||||
|
|
@ -362,12 +348,8 @@
|
||||||
$('#sidebar-search-results').height('0px');
|
$('#sidebar-search-results').height('0px');
|
||||||
}
|
}
|
||||||
|
|
||||||
function incrementNotificationCount() {
|
|
||||||
var count = parseInt($('#sidebar-notification-count').html());
|
|
||||||
$('#sidebar-notification-count').html(count + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function decrementNotificationCount() {
|
function decrementNotificationCount() {
|
||||||
|
/**
|
||||||
var count = parseInt($('#sidebar-notification-count').html());
|
var count = parseInt($('#sidebar-notification-count').html());
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
$('#sidebar-notification-count').html(0);
|
$('#sidebar-notification-count').html(0);
|
||||||
|
|
@ -375,6 +357,13 @@
|
||||||
else {
|
else {
|
||||||
$('#sidebar-notification-count').html(count - 1);
|
$('#sidebar-notification-count').html(count - 1);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// one important limitation; if the user is focused on an iframe, this will be false
|
||||||
|
// however, if they are doing something with Facebook or the photo picker, this may actually still be desirable
|
||||||
|
function userCanSeeNotifications() {
|
||||||
|
return document.hasFocus() || app.layout.isDialogShowing();
|
||||||
}
|
}
|
||||||
|
|
||||||
// default handler for incoming notification
|
// default handler for incoming notification
|
||||||
|
|
@ -402,6 +391,13 @@
|
||||||
|
|
||||||
$('#sidebar-notification-list').prepend(notificationHtml);
|
$('#sidebar-notification-list').prepend(notificationHtml);
|
||||||
|
|
||||||
|
if(userCanSeeNotifications()) {
|
||||||
|
app.updateNotificationSeen(payload.created_at);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
initializeActions(payload, type);
|
initializeActions(payload, type);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -153,9 +153,9 @@
|
||||||
$element.bt(text, options);
|
$element.bt(text, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.JK.bindHoverEvents = function ($parent) {
|
context.JK.bindHoverEvents = function ($parent) {
|
||||||
|
|
||||||
if(!$parent) {
|
if (!$parent) {
|
||||||
$parent = $('body');
|
$parent = $('body');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -328,9 +328,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// creates an array with entries like [{ id: "drums", description: "Drums"}, ]
|
// creates an array with entries like [{ id: "drums", description: "Drums"}, ]
|
||||||
context.JK.listInstruments = function() {
|
context.JK.listInstruments = function () {
|
||||||
var instrumentArray = [];
|
var instrumentArray = [];
|
||||||
$.each(context.JK.server_to_client_instrument_map, function(key, val) {
|
$.each(context.JK.server_to_client_instrument_map, function (key, val) {
|
||||||
instrumentArray.push({"id": context.JK.server_to_client_instrument_map[key].client_id, "description": key});
|
instrumentArray.push({"id": context.JK.server_to_client_instrument_map[key].client_id, "description": key});
|
||||||
});
|
});
|
||||||
return instrumentArray;
|
return instrumentArray;
|
||||||
|
|
@ -652,22 +652,22 @@
|
||||||
return hasFlash;
|
return hasFlash;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.JK.hasOneConfiguredDevice = function() {
|
context.JK.hasOneConfiguredDevice = function () {
|
||||||
var result = context.jamClient.FTUEGetGoodConfigurationList();
|
var result = context.jamClient.FTUEGetGoodConfigurationList();
|
||||||
logger.debug("hasOneConfiguredDevice: ", result);
|
logger.debug("hasOneConfiguredDevice: ", result);
|
||||||
return result.length > 0;
|
return result.length > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
context.JK.getGoodAudioConfigs = function() {
|
context.JK.getGoodAudioConfigs = function () {
|
||||||
var result = context.jamClient.FTUEGetGoodAudioConfigurations();
|
var result = context.jamClient.FTUEGetGoodAudioConfigurations();
|
||||||
logger.debug("goodAudioConfigs=%o", result);
|
logger.debug("goodAudioConfigs=%o", result);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
context.JK.getGoodConfigMap = function() {
|
context.JK.getGoodConfigMap = function () {
|
||||||
var goodConfigMap = [];
|
var goodConfigMap = [];
|
||||||
var goodConfigs = context.JK.getGoodAudioConfigs();
|
var goodConfigs = context.JK.getGoodAudioConfigs();
|
||||||
$.each(goodConfigs, function(index, profileKey) {
|
$.each(goodConfigs, function (index, profileKey) {
|
||||||
var friendlyName = context.jamClient.FTUEGetConfigurationDevice(profileKey);
|
var friendlyName = context.jamClient.FTUEGetConfigurationDevice(profileKey);
|
||||||
goodConfigMap.push({key: profileKey, name: friendlyName});
|
goodConfigMap.push({key: profileKey, name: friendlyName});
|
||||||
});
|
});
|
||||||
|
|
@ -675,12 +675,12 @@
|
||||||
return goodConfigMap;
|
return goodConfigMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.JK.getBadAudioConfigs = function() {
|
context.JK.getBadAudioConfigs = function () {
|
||||||
var badAudioConfigs = [];
|
var badAudioConfigs = [];
|
||||||
var allAudioConfigs = context.jamClient.FTUEGetAllAudioConfigurations();
|
var allAudioConfigs = context.jamClient.FTUEGetAllAudioConfigurations();
|
||||||
var goodAudioConfigs = context.JK.getGoodAudioConfigs();
|
var goodAudioConfigs = context.JK.getGoodAudioConfigs();
|
||||||
|
|
||||||
for (var i=0; i < allAudioConfigs.length; i++) {
|
for (var i = 0; i < allAudioConfigs.length; i++) {
|
||||||
if ($.inArray(allAudioConfigs[i], goodAudioConfigs) === -1) {
|
if ($.inArray(allAudioConfigs[i], goodAudioConfigs) === -1) {
|
||||||
badAudioConfigs.push(allAudioConfigs[i]);
|
badAudioConfigs.push(allAudioConfigs[i]);
|
||||||
}
|
}
|
||||||
|
|
@ -689,10 +689,10 @@
|
||||||
return badAudioConfigs;
|
return badAudioConfigs;
|
||||||
};
|
};
|
||||||
|
|
||||||
context.JK.getBadConfigMap = function() {
|
context.JK.getBadConfigMap = function () {
|
||||||
var badConfigMap = [];
|
var badConfigMap = [];
|
||||||
var badConfigs = context.JK.getBadAudioConfigs();
|
var badConfigs = context.JK.getBadAudioConfigs();
|
||||||
$.each(badConfigs, function(index, profileKey) {
|
$.each(badConfigs, function (index, profileKey) {
|
||||||
var friendlyName = context.jamClient.FTUEGetConfigurationDevice(profileKey);
|
var friendlyName = context.jamClient.FTUEGetConfigurationDevice(profileKey);
|
||||||
badConfigMap.push({key: profileKey, name: friendlyName});
|
badConfigMap.push({key: profileKey, name: friendlyName});
|
||||||
});
|
});
|
||||||
|
|
@ -700,7 +700,7 @@
|
||||||
return badConfigMap;
|
return badConfigMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.JK.getFirstGoodDevice = function(preferredDeviceId) {
|
context.JK.getFirstGoodDevice = function (preferredDeviceId) {
|
||||||
var badConfigs = context.JK.getBadAudioConfigs();
|
var badConfigs = context.JK.getBadAudioConfigs();
|
||||||
|
|
||||||
function getGoodDevice() {
|
function getGoodDevice() {
|
||||||
|
|
@ -713,7 +713,7 @@
|
||||||
}
|
}
|
||||||
return deviceId;
|
return deviceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
var deviceId = null;
|
var deviceId = null;
|
||||||
|
|
||||||
if (preferredDeviceId) {
|
if (preferredDeviceId) {
|
||||||
|
|
@ -724,7 +724,7 @@
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
deviceId = getGoodDevice();
|
deviceId = getGoodDevice();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
deviceId = getGoodDevice();
|
deviceId = getGoodDevice();
|
||||||
|
|
@ -733,14 +733,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns /client#/home for http://www.jamkazam.com/client#/home
|
// returns /client#/home for http://www.jamkazam.com/client#/home
|
||||||
context.JK.locationPath = function() {
|
context.JK.locationPath = function () {
|
||||||
var bits = context.location.href.split('/');
|
var bits = context.location.href.split('/');
|
||||||
return '/' + bits.slice(3).join('/');
|
return '/' + bits.slice(3).join('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
context.JK.nowUTC = function() {
|
context.JK.nowUTC = function () {
|
||||||
var d = new Date();
|
var d = new Date();
|
||||||
return new Date( d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds() );
|
return new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds());
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
|
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
|
||||||
|
|
|
||||||
|
|
@ -78,8 +78,8 @@
|
||||||
var clicked = $(this);
|
var clicked = $(this);
|
||||||
var href = clicked.attr('href');
|
var href = clicked.attr('href');
|
||||||
if(href != "#") {
|
if(href != "#") {
|
||||||
|
context.JK.GA.trackDownload(clicked.attr('data-platform'));
|
||||||
rest.userDownloadedClient().always(function() {
|
rest.userDownloadedClient().always(function() {
|
||||||
context.JK.GA.trackDownload(clicked.attr('data-platform'));
|
|
||||||
$('body').append('<iframe class="downloading" src="' + clicked.attr('href') + '" style="display:none"/>')
|
$('body').append('<iframe class="downloading" src="' + clicked.attr('href') + '" style="display:none"/>')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,10 @@
|
||||||
-webkit-border-radius:50%;
|
-webkit-border-radius:50%;
|
||||||
-moz-border-radius:50%;
|
-moz-border-radius:50%;
|
||||||
border-radius:50%;
|
border-radius:50%;
|
||||||
|
|
||||||
|
&.highlighted {
|
||||||
|
background-color:white;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.expander {
|
.expander {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ end
|
||||||
|
|
||||||
# give back more info if the user being fetched is yourself
|
# give back more info if the user being fetched is yourself
|
||||||
if @user == current_user
|
if @user == current_user
|
||||||
attributes :email, :original_fpfile, :cropped_fpfile, :crop_selection, :session_settings, :show_whats_next, :subscribe_email, :auth_twitter
|
attributes :email, :original_fpfile, :cropped_fpfile, :crop_selection, :session_settings, :show_whats_next, :subscribe_email, :auth_twitter, :new_notifications
|
||||||
|
|
||||||
|
|
||||||
elsif current_user
|
elsif current_user
|
||||||
node :is_friend do |uu|
|
node :is_friend do |uu|
|
||||||
|
|
|
||||||
|
|
@ -311,6 +311,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
JK.bindHoverEvents();
|
JK.bindHoverEvents();
|
||||||
|
|
||||||
|
setInterval(function() {
|
||||||
|
console.log("IN FOCUS: " + document.hasFocus());
|
||||||
|
}, 1000)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*global jQuery*/
|
||||||
|
/*jshint curly:false*/
|
||||||
|
|
||||||
|
;(function ( $, window) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var defaults = {
|
||||||
|
pulses : 1,
|
||||||
|
interval : 0,
|
||||||
|
returnDelay : 0,
|
||||||
|
duration : 500
|
||||||
|
};
|
||||||
|
|
||||||
|
$.fn.pulse = function(properties, options, callback) {
|
||||||
|
// $(...).pulse('destroy');
|
||||||
|
var stop = properties === 'destroy';
|
||||||
|
|
||||||
|
if (typeof options === 'function') {
|
||||||
|
callback = options;
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
options = $.extend({}, defaults, options);
|
||||||
|
|
||||||
|
if (!(options.interval >= 0)) options.interval = 0;
|
||||||
|
if (!(options.returnDelay >= 0)) options.returnDelay = 0;
|
||||||
|
if (!(options.duration >= 0)) options.duration = 500;
|
||||||
|
if (!(options.pulses >= -1)) options.pulses = 1;
|
||||||
|
if (typeof callback !== 'function') callback = function(){};
|
||||||
|
|
||||||
|
return this.each(function () {
|
||||||
|
var el = $(this),
|
||||||
|
property,
|
||||||
|
original = {};
|
||||||
|
|
||||||
|
var data = el.data('pulse') || {};
|
||||||
|
data.stop = stop;
|
||||||
|
el.data('pulse', data);
|
||||||
|
|
||||||
|
for (property in properties) {
|
||||||
|
if (properties.hasOwnProperty(property)) original[property] = el.css(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
var timesPulsed = 0;
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
if (typeof el.data('pulse') === 'undefined') return;
|
||||||
|
if (el.data('pulse').stop) return;
|
||||||
|
if (options.pulses > -1 && ++timesPulsed > options.pulses) return callback.apply(el);
|
||||||
|
el.animate(
|
||||||
|
properties,
|
||||||
|
{
|
||||||
|
duration : options.duration / 2,
|
||||||
|
complete : function(){
|
||||||
|
window.setTimeout(function(){
|
||||||
|
el.animate(original, {
|
||||||
|
duration : options.duration / 2,
|
||||||
|
complete : function() {
|
||||||
|
window.setTimeout(animate, options.interval);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},options.returnDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
animate();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
})( jQuery, window, document );
|
||||||
|
|
@ -565,9 +565,30 @@ module JamWebsockets
|
||||||
raise SessionError, 'connection state is gone. please reconnect.'
|
raise SessionError, 'connection state is gone. please reconnect.'
|
||||||
else
|
else
|
||||||
Connection.transaction do
|
Connection.transaction do
|
||||||
music_session = MusicSession.select(:track_changes_counter).find_by_id(connection.music_session_id) if connection.music_session_id
|
# send back track_changes_counter if in a session
|
||||||
track_changes_counter = music_session.track_changes_counter if music_session
|
if connection.music_session_id
|
||||||
|
music_session = MusicSession.select(:track_changes_counter).find_by_id(connection.music_session_id)
|
||||||
|
track_changes_counter = music_session.track_changes_counter if music_session
|
||||||
|
end
|
||||||
|
|
||||||
|
# update connection updated_at
|
||||||
connection.touch
|
connection.touch
|
||||||
|
|
||||||
|
# update user's notification_seen_at field if the heartbeat indicates it saw one
|
||||||
|
notification_seen_at_parsed = nil
|
||||||
|
notification_seen_at = heartbeat.notification_seen_at if heartbeat.value_for_tag(1)
|
||||||
|
begin
|
||||||
|
notification_seen_at_parsed = Time.parse(notification_seen_at)
|
||||||
|
rescue Exception => e
|
||||||
|
@log.error "unable to parse notification_seen_at in heartbeat from #{context}. notification_seen_at: #{notification_seen_at}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if notification_seen_at_parsed
|
||||||
|
connection.user.notification_seen_at = notification_seen_at
|
||||||
|
unless connection.user.save(validate: false)
|
||||||
|
@log.error "unable to update notification_seen_at for client #{context}. errors: #{connection.user.errors.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
ConnectionManager.active_record_transaction do |connection_manager|
|
ConnectionManager.active_record_transaction do |connection_manager|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue