Merge branch 'develop' of bitbucket.org:jamkazam/jam-cloud into develop
This commit is contained in:
commit
ff03770256
|
|
@ -48,7 +48,7 @@ gem 'unf', '0.1.3' #optional fog dependency
|
|||
gem 'country-select'
|
||||
gem 'aasm', '3.0.16'
|
||||
gem 'postgres-copy', '0.6.0'
|
||||
gem 'aws-sdk', '1.29.1'
|
||||
gem 'aws-sdk' #, '1.29.1'
|
||||
gem 'bugsnag'
|
||||
gem 'gon'
|
||||
gem 'cocoon'
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ gem 'amqp', '1.0.2'
|
|||
gem 'will_paginate'
|
||||
gem 'actionmailer', '3.2.13'
|
||||
gem 'sendgrid', '1.2.0'
|
||||
gem 'aws-sdk', '1.29.1'
|
||||
gem 'aws-sdk' #, '1.29.1'
|
||||
gem 'carrierwave', '0.9.0'
|
||||
gem 'aasm', '3.0.16'
|
||||
gem 'devise', '>= 1.1.2'
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ module JamWebEventMachine
|
|||
end
|
||||
|
||||
|
||||
def self.run_em
|
||||
def self.run_em(calling_thread)
|
||||
|
||||
EM.run do
|
||||
# this is global because we need to check elsewhere if we are currently connected to amqp before signalling success with some APIs, such as 'create session'
|
||||
|
|
@ -53,6 +53,8 @@ module JamWebEventMachine
|
|||
MQRouter.user_exchange = exchange
|
||||
end
|
||||
end
|
||||
|
||||
calling_thread.wakeup
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -63,9 +65,12 @@ module JamWebEventMachine
|
|||
end
|
||||
|
||||
def self.run
|
||||
|
||||
current = Thread.current
|
||||
Thread.new do
|
||||
run_em
|
||||
run_em(current)
|
||||
end
|
||||
Thread.stop
|
||||
end
|
||||
|
||||
def self.start
|
||||
|
|
@ -79,8 +84,9 @@ module JamWebEventMachine
|
|||
EM.stop
|
||||
end
|
||||
@@log.debug("starting EventMachine")
|
||||
current = Thread.current
|
||||
Thread.new do
|
||||
run_em
|
||||
run_em(current)
|
||||
end
|
||||
die_gracefully_on_signal
|
||||
end
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ FOO
|
|||
|
||||
def send_test_batch
|
||||
self.perform_event('do_test_run!')
|
||||
if 'test'==Rails.env
|
||||
if 'test' == Rails.env
|
||||
BatchMailer.send_batch_email_test(self.id).deliver!
|
||||
else
|
||||
BatchMailer.send_batch_email_test(self.id).deliver
|
||||
|
|
|
|||
|
|
@ -366,6 +366,8 @@ module JamRuby
|
|||
|
||||
def send_session_ended(session_id)
|
||||
|
||||
return if session_id.nil? # so we don't query every notification in the system with a nil session_id
|
||||
|
||||
notifications = Notification.where(:session_id => session_id)
|
||||
|
||||
# publish to all users who have a notification for this session
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ describe RecordedTrack do
|
|||
describe "aws-based operations", :aws => true do
|
||||
|
||||
def put_file_to_aws(signed_data, contents)
|
||||
|
||||
begin
|
||||
RestClient.put( signed_data[:url],
|
||||
contents,
|
||||
{
|
||||
|
|
@ -76,6 +78,11 @@ describe RecordedTrack do
|
|||
:'Content-MD5' => signed_data[:md5],
|
||||
:Authorization => signed_data[:authorization]
|
||||
})
|
||||
rescue => e
|
||||
puts e.response
|
||||
raise e
|
||||
end
|
||||
|
||||
end
|
||||
# create a test file
|
||||
upload_file='some_file.ogg'
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
|
||||
ENV["RAILS_ENV"] = "test"
|
||||
|
||||
require 'simplecov'
|
||||
require 'support/utilities'
|
||||
require 'active_record'
|
||||
|
|
@ -28,13 +31,14 @@ require 'factories'
|
|||
|
||||
include JamRuby
|
||||
|
||||
|
||||
# manually register observers
|
||||
ActiveRecord::Base.add_observer InvitedUserObserver.instance
|
||||
ActiveRecord::Base.add_observer UserObserver.instance
|
||||
ActiveRecord::Base.add_observer FeedbackObserver.instance
|
||||
ActiveRecord::Base.add_observer RecordedTrackObserver.instance
|
||||
|
||||
RecordedTrack.observers.disable :all # only a few tests want this observer active
|
||||
#RecordedTrack.observers.disable :all # only a few tests want this observer active
|
||||
|
||||
# put ActionMailer into test mode
|
||||
ActionMailer::Base.delivery_method = :test
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ JAMKAZAM_TESTING_BUCKET = 'jamkazam-testing' # cuz i'm not comfortable using aws
|
|||
|
||||
def app_config
|
||||
klass = Class.new do
|
||||
|
||||
def aws_bucket
|
||||
JAMKAZAM_TESTING_BUCKET
|
||||
JAMKAZAM_TESTING_BUCKET
|
||||
end
|
||||
|
||||
def aws_access_key_id
|
||||
|
|
@ -14,6 +15,18 @@ def app_config
|
|||
'h0V0ffr3JOp/UtgaGrRfAk25KHNiO9gm8Pj9m6v3'
|
||||
end
|
||||
|
||||
def aws_region
|
||||
'us-east-1'
|
||||
end
|
||||
|
||||
def aws_bucket_public
|
||||
'jamkazam-testing-public'
|
||||
end
|
||||
|
||||
def aws_cache
|
||||
'315576000'
|
||||
end
|
||||
|
||||
def audiomixer_path
|
||||
# you can specify full path to audiomixer with AUDIOMIXER_PATH env variable...
|
||||
# or we check for audiomixer path in the user's workspace
|
||||
|
|
@ -77,30 +90,6 @@ def app_config
|
|||
"#{external_protocol}#{external_hostname}#{(external_port == 80 || external_port == 443) ? '' : ':' + external_port.to_s}"
|
||||
end
|
||||
|
||||
def aws_access_key_id
|
||||
'AKIAJESQY24TOT542UHQ'
|
||||
end
|
||||
|
||||
def aws_secret_access_key
|
||||
'h0V0ffr3JOp/UtgaGrRfAk25KHNiO9gm8Pj9m6v3'
|
||||
end
|
||||
|
||||
def aws_region
|
||||
'us-east-1'
|
||||
end
|
||||
|
||||
def aws_bucket
|
||||
'jamkazam-testing'
|
||||
end
|
||||
|
||||
def aws_bucket_public
|
||||
'jamkazam-testing-public'
|
||||
end
|
||||
|
||||
def aws_cache
|
||||
'315576000'
|
||||
end
|
||||
|
||||
def max_audio_downloads
|
||||
100
|
||||
end
|
||||
|
|
@ -124,7 +113,7 @@ def app_config
|
|||
|
||||
end
|
||||
|
||||
return klass.new
|
||||
klass.new
|
||||
end
|
||||
|
||||
def run_tests? type
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ gem 'fb_graph', '2.5.9'
|
|||
gem 'sendgrid', '1.2.0'
|
||||
gem 'recaptcha', '0.3.4'
|
||||
gem 'filepicker-rails', '0.1.0'
|
||||
gem 'aws-sdk', '1.29.1'
|
||||
gem 'aws-sdk' #, '1.29.1'
|
||||
gem 'aasm', '3.0.16'
|
||||
gem 'carrierwave', '0.9.0'
|
||||
gem 'carrierwave_direct'
|
||||
|
|
|
|||
|
|
@ -1,15 +1,47 @@
|
|||
// The wrapper around the web-socket connection to the server
|
||||
(function(context, $) {
|
||||
// manages the connection, heartbeats, and reconnect logic.
|
||||
// presents itself as a dialog, or in-situ banner (_jamServer.html.haml)
|
||||
(function (context, $) {
|
||||
|
||||
"use strict";
|
||||
"use strict";
|
||||
|
||||
context.JK = context.JK || {};
|
||||
context.JK = context.JK || {};
|
||||
|
||||
var logger = context.JK.logger;
|
||||
var msg_factory = context.JK.MessageFactory;
|
||||
var logger = context.JK.logger;
|
||||
var msg_factory = context.JK.MessageFactory;
|
||||
// Let socket.io know where WebSocketMain.swf is
|
||||
context.WEB_SOCKET_SWF_LOCATION = "assets/flash/WebSocketMain.swf";
|
||||
|
||||
// Let socket.io know where WebSocketMain.swf is
|
||||
context.WEB_SOCKET_SWF_LOCATION = "assets/flash/WebSocketMain.swf";
|
||||
context.JK.JamServer = function (app) {
|
||||
|
||||
// heartbeat
|
||||
var heartbeatInterval = null;
|
||||
var heartbeatMS = null;
|
||||
var heartbeatMissedMS = 10000; // if 5 seconds go by and we haven't seen a heartbeat ack, get upset
|
||||
var lastHeartbeatAckTime = null;
|
||||
var lastHeartbeatFound = false;
|
||||
var heartbeatAckCheckInterval = null;
|
||||
var notificationLastSeenAt = undefined;
|
||||
var notificationLastSeen = undefined;
|
||||
|
||||
// reconnection logic
|
||||
var connectDeferred = null;
|
||||
var freezeInteraction = false;
|
||||
var countdownInterval = null;
|
||||
var reconnectAttemptLookup = [2, 2, 2, 4, 8, 15, 30];
|
||||
var reconnectAttempt = 0;
|
||||
var reconnectingWaitPeriodStart = null;
|
||||
var reconnectDueTime = null;
|
||||
var connectTimeout = null;
|
||||
|
||||
// elements
|
||||
var $inSituBanner = null;
|
||||
var $inSituBannerHolder = null;
|
||||
var $messageContents = null;
|
||||
var $dialog = null;
|
||||
var $templateServerConnection = null;
|
||||
var $templateDisconnected = null;
|
||||
var $currentDisplay = null;
|
||||
|
||||
var server = {};
|
||||
server.socket = {};
|
||||
|
|
@ -21,141 +53,459 @@
|
|||
server.connected = false;
|
||||
|
||||
|
||||
// if activeElementVotes is null, then we are assuming this is the initial connect sequence
|
||||
function initiateReconnect(activeElementVotes, in_error) {
|
||||
var initialConnect = !!activeElementVotes;
|
||||
|
||||
freezeInteraction = activeElementVotes && ((activeElementVotes.dialog && activeElementVotes.dialog.freezeInteraction === true) || (activeElementVotes.screen && activeElementVotes.screen.freezeInteraction === true));
|
||||
|
||||
if(!initialConnect) {
|
||||
context.JK.CurrentSessionModel.onWebsocketDisconnected(in_error);
|
||||
}
|
||||
|
||||
if(in_error) {
|
||||
reconnectAttempt = 0;
|
||||
$currentDisplay = renderDisconnected();
|
||||
beginReconnectPeriod();
|
||||
}
|
||||
}
|
||||
|
||||
// handles logic if the websocket connection closes, and if it was in error then also prompt for reconnect
|
||||
function closedCleanup(in_error) {
|
||||
if(server.connected) {
|
||||
server.connected = false;
|
||||
|
||||
context.JK.CurrentSessionModel.onWebsocketDisconnected(in_error);
|
||||
// stop future heartbeats
|
||||
if (heartbeatInterval != null) {
|
||||
clearInterval(heartbeatInterval);
|
||||
heartbeatInterval = null;
|
||||
}
|
||||
|
||||
// notify anyone listening that the socket closed
|
||||
var len = server.socketClosedListeners.length;
|
||||
for(var i = 0; i < len; i++) {
|
||||
try {
|
||||
server.socketClosedListeners[i](in_error);
|
||||
} catch (ex) {
|
||||
logger.warn('exception in callback for websocket closed event:' + ex);
|
||||
}
|
||||
}
|
||||
// stop checking for heartbeat acks
|
||||
if (heartbeatAckCheckInterval != null) {
|
||||
clearTimeout(heartbeatAckCheckInterval);
|
||||
heartbeatAckCheckInterval = null;
|
||||
}
|
||||
|
||||
if (server.connected) {
|
||||
server.connected = false;
|
||||
if(app.clientUpdating) {
|
||||
// we don't want to do a 'cover the whole screen' dialog
|
||||
// because the client update is already showing.
|
||||
return;
|
||||
}
|
||||
|
||||
server.reconnecting = true;
|
||||
|
||||
var result = app.activeElementEvent('beforeDisconnect');
|
||||
|
||||
initiateReconnect(result, in_error);
|
||||
|
||||
app.activeElementEvent('afterDisconnect');
|
||||
|
||||
// notify anyone listening that the socket closed
|
||||
var len = server.socketClosedListeners.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
try {
|
||||
server.socketClosedListeners[i](in_error);
|
||||
} catch (ex) {
|
||||
logger.warn('exception in callback for websocket closed event:' + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
server.registerOnSocketClosed = function(callback) {
|
||||
server.socketClosedListeners.push(callback);
|
||||
////////////////////
|
||||
//// HEARTBEAT /////
|
||||
////////////////////
|
||||
function _heartbeatAckCheck() {
|
||||
|
||||
// if we've seen an ack to the latest heartbeat, don't bother with checking again
|
||||
// this makes us resilient to front-end hangs
|
||||
if (lastHeartbeatFound) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if the server is still sending heartbeat acks back down
|
||||
// this logic equates to 'if we have not received a heartbeat within heartbeatMissedMS, then get upset
|
||||
if (new Date().getTime() - lastHeartbeatAckTime.getTime() > heartbeatMissedMS) {
|
||||
logger.error("no heartbeat ack received from server after ", heartbeatMissedMS, " seconds . giving up on socket connection");
|
||||
context.JK.JamServer.close(true);
|
||||
}
|
||||
else {
|
||||
lastHeartbeatFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
server.registerMessageCallback = function(messageType, callback) {
|
||||
if (server.dispatchTable[messageType] === undefined) {
|
||||
server.dispatchTable[messageType] = [];
|
||||
}
|
||||
|
||||
server.dispatchTable[messageType].push(callback);
|
||||
};
|
||||
|
||||
server.unregisterMessageCallback = function(messageType, callback) {
|
||||
if (server.dispatchTable[messageType] !== undefined) {
|
||||
for(var i = server.dispatchTable[messageType].length; i--;) {
|
||||
if (server.dispatchTable[messageType][i] === callback)
|
||||
{
|
||||
server.dispatchTable[messageType].splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (server.dispatchTable[messageType].length === 0) {
|
||||
delete server.dispatchTable[messageType];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
server.connect = function() {
|
||||
logger.log("server.connect");
|
||||
var uri = context.JK.websocket_gateway_uri; // Set in index.html.erb.
|
||||
//var uri = context.gon.websocket_gateway_uri; // Leaving here for now, as we're looking for a better solution.
|
||||
server.socket = new context.WebSocket(uri);
|
||||
server.socket.onopen = server.onOpen;
|
||||
server.socket.onmessage = server.onMessage;
|
||||
server.socket.onclose = server.onClose;
|
||||
};
|
||||
|
||||
server.close = function(in_error) {
|
||||
logger.log("closing websocket");
|
||||
|
||||
server.socket.close();
|
||||
|
||||
closedCleanup(in_error);
|
||||
function _heartbeat() {
|
||||
if (app.heartbeatActive) {
|
||||
var message = context.JK.MessageFactory.heartbeat(notificationLastSeen, notificationLastSeenAt);
|
||||
notificationLastSeenAt = undefined;
|
||||
notificationLastSeen = undefined;
|
||||
context.JK.JamServer.send(message);
|
||||
lastHeartbeatFound = false;
|
||||
}
|
||||
}
|
||||
|
||||
server.rememberLogin = function() {
|
||||
var token, loginMessage;
|
||||
token = $.cookie("remember_token");
|
||||
var clientType = context.jamClient.IsNativeClient() ? 'client' : 'browser';
|
||||
loginMessage = msg_factory.login_with_token(token, null, clientType);
|
||||
server.send(loginMessage);
|
||||
};
|
||||
function loggedIn(header, payload) {
|
||||
|
||||
server.onOpen = function() {
|
||||
logger.log("server.onOpen");
|
||||
server.rememberLogin();
|
||||
};
|
||||
if(!connectTimeout) {
|
||||
clearTimeout(connectTimeout);
|
||||
connectTimeout = null;
|
||||
}
|
||||
|
||||
server.onMessage = function(e) {
|
||||
var message = JSON.parse(e.data),
|
||||
messageType = message.type.toLowerCase(),
|
||||
payload = message[messageType],
|
||||
callbacks = server.dispatchTable[message.type];
|
||||
app.clientId = payload.client_id;
|
||||
|
||||
if(message.type != context.JK.MessageType.HEARTBEAT_ACK && message.type != context.JK.MessageType.PEER_MESSAGE) {
|
||||
logger.log("server.onMessage:" + messageType + " payload:" + JSON.stringify(payload));
|
||||
// tell the backend that we have logged in
|
||||
context.jamClient.OnLoggedIn(payload.user_id, payload.token);
|
||||
|
||||
$.cookie('client_id', payload.client_id);
|
||||
|
||||
heartbeatMS = payload.heartbeat_interval * 1000;
|
||||
logger.debug("jamkazam.js.loggedIn(): clientId now " + app.clientId + "; Setting up heartbeat every " + heartbeatMS + " MS");
|
||||
heartbeatInterval = context.setInterval(_heartbeat, heartbeatMS);
|
||||
heartbeatAckCheckInterval = context.setInterval(_heartbeatAckCheck, 1000);
|
||||
lastHeartbeatAckTime = new Date(new Date().getTime() + heartbeatMS); // add a little forgiveness to server for initial heartbeat
|
||||
|
||||
connectDeferred.resolve();
|
||||
app.activeElementEvent('afterConnect', payload);
|
||||
|
||||
}
|
||||
|
||||
function heartbeatAck(header, payload) {
|
||||
lastHeartbeatAckTime = new Date();
|
||||
|
||||
context.JK.CurrentSessionModel.trackChanges(header, payload);
|
||||
}
|
||||
|
||||
function registerLoginAck() {
|
||||
logger.debug("register for loggedIn to set clientId");
|
||||
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.LOGIN_ACK, loggedIn);
|
||||
}
|
||||
|
||||
function registerHeartbeatAck() {
|
||||
logger.debug("register for heartbeatAck");
|
||||
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.HEARTBEAT_ACK, heartbeatAck);
|
||||
}
|
||||
|
||||
function registerSocketClosed() {
|
||||
logger.debug("register for socket closed");
|
||||
context.JK.JamServer.registerOnSocketClosed(socketClosed);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called whenever the websocket closes; this gives us a chance to cleanup things that should be stopped/cleared
|
||||
* @param in_error did the socket close abnormally?
|
||||
*/
|
||||
function socketClosed(in_error) {
|
||||
|
||||
// tell the backend that we have logged out
|
||||
context.jamClient.OnLoggedOut();
|
||||
|
||||
}
|
||||
|
||||
///////////////////
|
||||
/// RECONNECT /////
|
||||
///////////////////
|
||||
function internetUp() {
|
||||
var start = new Date().getTime();
|
||||
server.connect()
|
||||
.done(function() {
|
||||
guardAgainstRapidTransition(start, performReconnect);
|
||||
})
|
||||
.fail(function() {
|
||||
guardAgainstRapidTransition(start, closedOnReconnectAttempt);
|
||||
});
|
||||
}
|
||||
|
||||
// websocket couldn't connect. let's try again soon
|
||||
function closedOnReconnectAttempt() {
|
||||
failedReconnect();
|
||||
}
|
||||
|
||||
function performReconnect() {
|
||||
|
||||
if($currentDisplay.is('.no-websocket-connection')) {
|
||||
$currentDisplay.hide();
|
||||
|
||||
// TODO: tell certain elements that we've reconnected
|
||||
}
|
||||
else {
|
||||
context.JK.CurrentSessionModel.leaveCurrentSession()
|
||||
.always(function() {
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
server.reconnecting = false;
|
||||
}
|
||||
|
||||
function buildOptions() {
|
||||
return {};
|
||||
}
|
||||
|
||||
function renderDisconnected() {
|
||||
|
||||
var content = null;
|
||||
if(freezeInteraction) {
|
||||
var template = $templateDisconnected.html();
|
||||
var templateHtml = $(context.JK.fillTemplate(template, buildOptions()));
|
||||
templateHtml.find('.reconnect-countdown').html(formatDelaySecs(reconnectDelaySecs()));
|
||||
content = context.JK.Banner.show({
|
||||
html : templateHtml,
|
||||
type: 'reconnect'
|
||||
}) ;
|
||||
}
|
||||
else {
|
||||
var $inSituContent = $(context._.template($templateServerConnection.html(), buildOptions(), { variable: 'data' }));
|
||||
$inSituContent.find('.reconnect-countdown').html(formatDelaySecs(reconnectDelaySecs()));
|
||||
$messageContents.empty();
|
||||
$messageContents.append($inSituContent);
|
||||
$inSituBannerHolder.show();
|
||||
content = $inSituBannerHolder;
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
function formatDelaySecs(secs) {
|
||||
return $('<span class="countdown-seconds"><span class="countdown">' + secs + '</span> ' + (secs == 1 ? ' second.<span style="visibility:hidden">s</span>' : 'seconds.') + '</span>');
|
||||
}
|
||||
|
||||
function setCountdown($parent) {
|
||||
$parent.find('.reconnect-countdown').html(formatDelaySecs(reconnectDelaySecs()));
|
||||
}
|
||||
|
||||
function renderCouldNotReconnect() {
|
||||
return renderDisconnected();
|
||||
}
|
||||
|
||||
function renderReconnecting() {
|
||||
$currentDisplay.find('.reconnect-progress-msg').text('Attempting to reconnect...')
|
||||
|
||||
if($currentDisplay.is('.no-websocket-connection')) {
|
||||
$currentDisplay.find('.disconnected-reconnect').removeClass('reconnect-enabled').addClass('reconnect-disabled');
|
||||
}
|
||||
else {
|
||||
$currentDisplay.find('.disconnected-reconnect').removeClass('button-orange').addClass('button-grey');
|
||||
}
|
||||
}
|
||||
|
||||
function failedReconnect() {
|
||||
reconnectAttempt += 1;
|
||||
$currentDisplay = renderCouldNotReconnect();
|
||||
beginReconnectPeriod();
|
||||
}
|
||||
|
||||
function guardAgainstRapidTransition(start, nextStep) {
|
||||
var now = new Date().getTime();
|
||||
|
||||
if ((now - start) < 1500) {
|
||||
setTimeout(function() {
|
||||
nextStep();
|
||||
}, 1500 - (now - start))
|
||||
}
|
||||
else {
|
||||
nextStep();
|
||||
}
|
||||
}
|
||||
|
||||
function attemptReconnect() {
|
||||
|
||||
var start = new Date().getTime();
|
||||
|
||||
renderReconnecting();
|
||||
|
||||
rest.serverHealthCheck()
|
||||
.done(function() {
|
||||
guardAgainstRapidTransition(start, internetUp);
|
||||
})
|
||||
.fail(function(xhr, textStatus, errorThrown) {
|
||||
|
||||
if(xhr && xhr.status >= 100) {
|
||||
// we could connect to the server, and it's alive
|
||||
guardAgainstRapidTransition(start, internetUp);
|
||||
}
|
||||
else {
|
||||
guardAgainstRapidTransition(start, failedReconnect);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function clearReconnectTimers() {
|
||||
if(countdownInterval) {
|
||||
clearInterval(countdownInterval);
|
||||
countdownInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
function beginReconnectPeriod() {
|
||||
// allow user to force reconnect
|
||||
$currentDisplay.find('a.disconnected-reconnect').unbind('click').click(function() {
|
||||
if($(this).is('.button-orange') || $(this).is('.reconnect-enabled')) {
|
||||
clearReconnectTimers();
|
||||
attemptReconnect();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (callbacks !== undefined) {
|
||||
var len = callbacks.length;
|
||||
for(var i = 0; i < len; i++) {
|
||||
try {
|
||||
callbacks[i](message, payload);
|
||||
} catch (ex) {
|
||||
logger.warn('exception in callback for websocket message:' + ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
reconnectingWaitPeriodStart = new Date().getTime();
|
||||
reconnectDueTime = reconnectingWaitPeriodStart + reconnectDelaySecs() * 1000;
|
||||
|
||||
// update count down timer periodically
|
||||
countdownInterval = setInterval(function() {
|
||||
var now = new Date().getTime();
|
||||
if(now > reconnectDueTime) {
|
||||
clearReconnectTimers();
|
||||
attemptReconnect();
|
||||
}
|
||||
else {
|
||||
logger.log("Unexpected message type %s.", message.type);
|
||||
var secondsUntilReconnect = Math.ceil((reconnectDueTime - now) / 1000);
|
||||
$currentDisplay.find('.reconnect-countdown').html(formatDelaySecs(secondsUntilReconnect));
|
||||
}
|
||||
}, 333);
|
||||
}
|
||||
|
||||
function reconnectDelaySecs() {
|
||||
if (reconnectAttempt > reconnectAttemptLookup.length - 1) {
|
||||
return reconnectAttemptLookup[reconnectAttemptLookup.length - 1];
|
||||
}
|
||||
else {
|
||||
return reconnectAttemptLookup[reconnectAttempt];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
server.registerOnSocketClosed = function (callback) {
|
||||
server.socketClosedListeners.push(callback);
|
||||
}
|
||||
|
||||
server.registerMessageCallback = function (messageType, callback) {
|
||||
if (server.dispatchTable[messageType] === undefined) {
|
||||
server.dispatchTable[messageType] = [];
|
||||
}
|
||||
|
||||
server.dispatchTable[messageType].push(callback);
|
||||
};
|
||||
|
||||
server.onClose = function() {
|
||||
logger.log("Socket to server closed.");
|
||||
server.unregisterMessageCallback = function (messageType, callback) {
|
||||
if (server.dispatchTable[messageType] !== undefined) {
|
||||
for (var i = server.dispatchTable[messageType].length; i--;) {
|
||||
if (server.dispatchTable[messageType][i] === callback) {
|
||||
server.dispatchTable[messageType].splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
closedCleanup(true);
|
||||
if (server.dispatchTable[messageType].length === 0) {
|
||||
delete server.dispatchTable[messageType];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
server.send = function(message) {
|
||||
server.connect = function () {
|
||||
connectDeferred = new $.Deferred();
|
||||
logger.log("server.connect");
|
||||
var uri = context.JK.websocket_gateway_uri; // Set in index.html.erb.
|
||||
//var uri = context.gon.websocket_gateway_uri; // Leaving here for now, as we're looking for a better solution.
|
||||
|
||||
var jsMessage = JSON.stringify(message);
|
||||
server.socket = new context.WebSocket(uri);
|
||||
server.socket.onopen = server.onOpen;
|
||||
server.socket.onmessage = server.onMessage;
|
||||
server.socket.onclose = server.onClose;
|
||||
|
||||
if(message.type != context.JK.MessageType.HEARTBEAT && message.type != context.JK.MessageType.PEER_MESSAGE) {
|
||||
logger.log("server.send(" + jsMessage + ")");
|
||||
}
|
||||
if (server !== undefined && server.socket !== undefined && server.socket.send !== undefined) {
|
||||
server.socket.send(jsMessage);
|
||||
} else {
|
||||
logger.log("Dropped message because server connection is closed.");
|
||||
connectTimeout = setTimeout(function() {
|
||||
connectTimeout = null;
|
||||
if(connectDeferred.state() === 'pending') {
|
||||
connectDeferred.reject();
|
||||
}
|
||||
}, 4000);
|
||||
|
||||
return connectDeferred;
|
||||
};
|
||||
|
||||
server.loginSession = function(sessionId) {
|
||||
var loginMessage;
|
||||
server.close = function (in_error) {
|
||||
logger.log("closing websocket");
|
||||
|
||||
if (!server.signedIn) {
|
||||
logger.log("Not signed in!");
|
||||
// TODO: surface the error
|
||||
return;
|
||||
server.socket.close();
|
||||
|
||||
closedCleanup(in_error);
|
||||
}
|
||||
|
||||
server.rememberLogin = function () {
|
||||
var token, loginMessage;
|
||||
token = $.cookie("remember_token");
|
||||
var clientType = context.jamClient.IsNativeClient() ? 'client' : 'browser';
|
||||
loginMessage = msg_factory.login_with_token(token, null, clientType);
|
||||
server.send(loginMessage);
|
||||
};
|
||||
|
||||
server.onOpen = function () {
|
||||
logger.log("server.onOpen");
|
||||
server.rememberLogin();
|
||||
};
|
||||
|
||||
server.onMessage = function (e) {
|
||||
var message = JSON.parse(e.data),
|
||||
messageType = message.type.toLowerCase(),
|
||||
payload = message[messageType],
|
||||
callbacks = server.dispatchTable[message.type];
|
||||
|
||||
if (message.type != context.JK.MessageType.HEARTBEAT_ACK && message.type != context.JK.MessageType.PEER_MESSAGE) {
|
||||
logger.log("server.onMessage:" + messageType + " payload:" + JSON.stringify(payload));
|
||||
}
|
||||
|
||||
if (callbacks !== undefined) {
|
||||
var len = callbacks.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
try {
|
||||
callbacks[i](message, payload);
|
||||
} catch (ex) {
|
||||
logger.warn('exception in callback for websocket message:' + ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger.log("Unexpected message type %s.", message.type);
|
||||
}
|
||||
};
|
||||
|
||||
loginMessage = msg_factory.login_jam_session(sessionId);
|
||||
server.send(loginMessage);
|
||||
server.onClose = function () {
|
||||
logger.log("Socket to server closed.");
|
||||
|
||||
if(connectDeferred.state() === "pending") {
|
||||
connectDeferred.reject();
|
||||
}
|
||||
|
||||
closedCleanup(true);
|
||||
};
|
||||
|
||||
server.send = function (message) {
|
||||
|
||||
var jsMessage = JSON.stringify(message);
|
||||
|
||||
if (message.type != context.JK.MessageType.HEARTBEAT && message.type != context.JK.MessageType.PEER_MESSAGE) {
|
||||
logger.log("server.send(" + jsMessage + ")");
|
||||
}
|
||||
if (server !== undefined && server.socket !== undefined && server.socket.send !== undefined) {
|
||||
server.socket.send(jsMessage);
|
||||
} else {
|
||||
logger.log("Dropped message because server connection is closed.");
|
||||
}
|
||||
};
|
||||
|
||||
server.loginSession = function (sessionId) {
|
||||
var loginMessage;
|
||||
|
||||
if (!server.signedIn) {
|
||||
logger.log("Not signed in!");
|
||||
// TODO: surface the error
|
||||
return;
|
||||
}
|
||||
|
||||
loginMessage = msg_factory.login_jam_session(sessionId);
|
||||
server.send(loginMessage);
|
||||
};
|
||||
|
||||
/** with the advent of the reliable UDP channel, this is no longer how messages are sent from client-to-clent
|
||||
|
|
@ -163,47 +513,88 @@
|
|||
* @param receiver_id client ID of message to send
|
||||
* @param message the actual message
|
||||
*/
|
||||
server.sendP2PMessage = function(receiver_id, message) {
|
||||
//logger.log("P2P message from [" + server.clientID + "] to [" + receiver_id + "]: " + message);
|
||||
//console.time('sendP2PMessage');
|
||||
var outgoing_msg = msg_factory.client_p2p_message(server.clientID, receiver_id, message);
|
||||
server.send(outgoing_msg);
|
||||
//console.timeEnd('sendP2PMessage');
|
||||
server.sendP2PMessage = function (receiver_id, message) {
|
||||
//logger.log("P2P message from [" + server.clientID + "] to [" + receiver_id + "]: " + message);
|
||||
//console.time('sendP2PMessage');
|
||||
var outgoing_msg = msg_factory.client_p2p_message(server.clientID, receiver_id, message);
|
||||
server.send(outgoing_msg);
|
||||
//console.timeEnd('sendP2PMessage');
|
||||
};
|
||||
|
||||
server.updateNotificationSeen = function(notificationId, notificationCreatedAt) {
|
||||
var time = new Date(notificationCreatedAt);
|
||||
|
||||
if(!notificationCreatedAt) {
|
||||
throw 'invalid value passed to updateNotificationSeen'
|
||||
}
|
||||
|
||||
if(!notificationLastSeenAt) {
|
||||
notificationLastSeenAt = notificationCreatedAt;
|
||||
notificationLastSeen = notificationId;
|
||||
logger.debug("updated notificationLastSeenAt with: " + notificationCreatedAt);
|
||||
}
|
||||
else if(time.getTime() > new Date(notificationLastSeenAt).getTime()) {
|
||||
notificationLastSeenAt = notificationCreatedAt;
|
||||
notificationLastSeen = notificationId;
|
||||
logger.debug("updated notificationLastSeenAt with: " + notificationCreatedAt);
|
||||
}
|
||||
else {
|
||||
logger.debug("ignored notificationLastSeenAt for: " + notificationCreatedAt);
|
||||
}
|
||||
}
|
||||
|
||||
// Message callbacks
|
||||
server.registerMessageCallback(context.JK.MessageType.LOGIN_ACK, function (header, payload) {
|
||||
server.signedIn = true;
|
||||
logger.debug("Handling LOGIN_ACK. Updating client id to " + payload.client_id);
|
||||
server.clientID = payload.client_id;
|
||||
server.publicIP = payload.public_ip;
|
||||
server.connected = true;
|
||||
|
||||
if (context.jamClient !== undefined) {
|
||||
logger.debug("... (handling LOGIN_ACK) Updating backend client, connected to true and clientID to " +
|
||||
payload.client_id);
|
||||
context.jamClient.connected = true;
|
||||
context.jamClient.clientID = server.clientID;
|
||||
}
|
||||
});
|
||||
|
||||
server.registerMessageCallback(context.JK.MessageType.PEER_MESSAGE, function (header, payload) {
|
||||
if (context.jamClient !== undefined) {
|
||||
context.jamClient.P2PMessageReceived(header.from, payload.message);
|
||||
}
|
||||
});
|
||||
|
||||
context.JK.JamServer = server;
|
||||
|
||||
// Message callbacks
|
||||
server.registerMessageCallback(context.JK.MessageType.LOGIN_ACK, function(header, payload) {
|
||||
server.signedIn = true;
|
||||
logger.debug("Handling LOGIN_ACK. Updating client id to " + payload.client_id);
|
||||
server.clientID = payload.client_id;
|
||||
server.publicIP = payload.public_ip;
|
||||
server.connected = true;
|
||||
|
||||
if (context.jamClient !== undefined)
|
||||
{
|
||||
logger.debug("... (handling LOGIN_ACK) Updating backend client, connected to true and clientID to " +
|
||||
payload.client_id);
|
||||
context.jamClient.connected = true;
|
||||
context.jamClient.clientID = server.clientID;
|
||||
}
|
||||
});
|
||||
|
||||
server.registerMessageCallback(context.JK.MessageType.PEER_MESSAGE, function(header, payload) {
|
||||
if (context.jamClient !== undefined)
|
||||
{
|
||||
context.jamClient.P2PMessageReceived(header.from, payload.message);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Callbacks from jamClient
|
||||
if (context.jamClient !== undefined)
|
||||
{
|
||||
context.jamClient.SendP2PMessage.connect(server.sendP2PMessage);
|
||||
if (context.jamClient !== undefined) {
|
||||
context.jamClient.SendP2PMessage.connect(server.sendP2PMessage);
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
registerLoginAck();
|
||||
registerHeartbeatAck();
|
||||
registerSocketClosed();
|
||||
|
||||
$inSituBanner = $('.server-connection');
|
||||
$inSituBannerHolder = $('.no-websocket-connection');
|
||||
$messageContents = $inSituBannerHolder.find('.message-contents');
|
||||
$dialog = $('#banner');
|
||||
$templateServerConnection = $('#template-server-connection');
|
||||
$templateDisconnected = $('#template-disconnected');
|
||||
|
||||
if($inSituBanner.length != 1) { throw "found wrong number of .server-connection: " + $inSituBanner.length; }
|
||||
if($inSituBannerHolder.length != 1) { throw "found wrong number of .no-websocket-connection: " + $inSituBannerHolder.length; }
|
||||
if($messageContents.length != 1) { throw "found wrong number of .message-contents: " + $messageContents.length; }
|
||||
if($dialog.length != 1) { throw "found wrong number of #banner: " + $dialog.length; }
|
||||
if($templateServerConnection.length != 1) { throw "found wrong number of #template-server-connection: " + $templateServerConnection.length; }
|
||||
if($templateDisconnected.length != 1) { throw "found wrong number of #template-disconnected: " + $templateDisconnected.length; }
|
||||
}
|
||||
|
||||
this.initialize = initialize;
|
||||
this.initiateReconnect = initiateReconnect;
|
||||
|
||||
return this;
|
||||
}
|
||||
})(window, jQuery);
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
return newContent;
|
||||
}
|
||||
|
||||
$('#banner').show()
|
||||
$('#banner').attr('data-type', options.type).show()
|
||||
$('#banner_overlay').show()
|
||||
|
||||
// return the core of the banner so that caller can attach event handlers to newly created HTML
|
||||
|
|
|
|||
|
|
@ -123,6 +123,11 @@
|
|||
return false;
|
||||
}
|
||||
|
||||
if(!context.JK.JamServer.connected) {
|
||||
app.notifyAlert("Not Connected", 'To create or join a session, you must be connected to the server.');
|
||||
return false;
|
||||
}
|
||||
|
||||
// If user hasn't completed FTUE - do so now.
|
||||
if (!(context.JK.hasOneConfiguredDevice())) {
|
||||
app.afterFtue = function() { submitForm(evt); };
|
||||
|
|
|
|||
|
|
@ -342,6 +342,12 @@
|
|||
}
|
||||
|
||||
function afterShow(data) {
|
||||
if(!context.JK.JamServer.connected) {
|
||||
app.notifyAlert("Not Connected", 'To create or join a session, you must be connected to the server.');
|
||||
window.location = '/client#/home'
|
||||
return;
|
||||
}
|
||||
|
||||
clearResults();
|
||||
buildQuery();
|
||||
refreshDisplay();
|
||||
|
|
|
|||
|
|
@ -20,16 +20,9 @@
|
|||
var app;
|
||||
var logger = context.JK.logger;
|
||||
var rest = context.JK.Rest();
|
||||
var heartbeatInterval = null;
|
||||
var heartbeatMS = null;
|
||||
var heartbeatMissedMS = 10000; // if 5 seconds go by and we haven't seen a heartbeat ack, get upset
|
||||
var inBadState = false;
|
||||
var lastHeartbeatAckTime = null;
|
||||
var lastHeartbeatFound = false;
|
||||
var heartbeatAckCheckInterval = null;
|
||||
var userDeferred = null;
|
||||
var notificationLastSeenAt = undefined;
|
||||
var notificationLastSeen = undefined;
|
||||
|
||||
|
||||
var opts = {
|
||||
inClient: true, // specify false if you want the app object but none of the client-oriented features
|
||||
|
|
@ -72,58 +65,6 @@
|
|||
$(routes.handler);
|
||||
}
|
||||
|
||||
function _heartbeatAckCheck() {
|
||||
|
||||
// if we've seen an ack to the latest heartbeat, don't bother with checking again
|
||||
// this makes us resilient to front-end hangs
|
||||
if (lastHeartbeatFound) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if the server is still sending heartbeat acks back down
|
||||
// this logic equates to 'if we have not received a heartbeat within heartbeatMissedMS, then get upset
|
||||
if (new Date().getTime() - lastHeartbeatAckTime.getTime() > heartbeatMissedMS) {
|
||||
logger.error("no heartbeat ack received from server after ", heartbeatMissedMS, " seconds . giving up on socket connection");
|
||||
context.JK.JamServer.close(true);
|
||||
}
|
||||
else {
|
||||
lastHeartbeatFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
function _heartbeat() {
|
||||
if (app.heartbeatActive) {
|
||||
var message = context.JK.MessageFactory.heartbeat(notificationLastSeen, notificationLastSeenAt);
|
||||
notificationLastSeenAt = undefined;
|
||||
notificationLastSeen = undefined;
|
||||
context.JK.JamServer.send(message);
|
||||
lastHeartbeatFound = false;
|
||||
}
|
||||
}
|
||||
|
||||
function loggedIn(header, payload) {
|
||||
app.clientId = payload.client_id;
|
||||
|
||||
// tell the backend that we have logged in
|
||||
context.jamClient.OnLoggedIn(payload.user_id, payload.token);
|
||||
|
||||
$.cookie('client_id', payload.client_id);
|
||||
app.initAfterConnect();
|
||||
|
||||
heartbeatMS = payload.heartbeat_interval * 1000;
|
||||
logger.debug("jamkazam.js.loggedIn(): clientId now " + app.clientId + "; Setting up heartbeat every " + heartbeatMS + " MS");
|
||||
heartbeatInterval = context.setInterval(_heartbeat, heartbeatMS);
|
||||
heartbeatAckCheckInterval = context.setInterval(_heartbeatAckCheck, 1000);
|
||||
lastHeartbeatAckTime = new Date(new Date().getTime() + heartbeatMS); // add a little forgiveness to server for initial heartbeat
|
||||
}
|
||||
|
||||
function heartbeatAck(header, payload) {
|
||||
lastHeartbeatAckTime = new Date();
|
||||
|
||||
context.JK.CurrentSessionModel.trackChanges(header, payload);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This occurs when the websocket gateway loses a connection to the backend messaging system,
|
||||
* resulting in severe loss of functionality
|
||||
|
|
@ -153,37 +94,6 @@
|
|||
context.jamClient.OnDownloadAvailable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever the websocket closes; this gives us a chance to cleanup things that should be stopped/cleared
|
||||
* @param in_error did the socket close abnormally?
|
||||
*/
|
||||
function socketClosed(in_error) {
|
||||
|
||||
// tell the backend that we have logged out
|
||||
context.jamClient.OnLoggedOut();
|
||||
|
||||
// stop future heartbeats
|
||||
if (heartbeatInterval != null) {
|
||||
clearInterval(heartbeatInterval);
|
||||
heartbeatInterval = null;
|
||||
}
|
||||
|
||||
// stop checking for heartbeat acks
|
||||
if (heartbeatAckCheckInterval != null) {
|
||||
clearTimeout(heartbeatAckCheckInterval);
|
||||
heartbeatAckCheckInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
function registerLoginAck() {
|
||||
logger.debug("register for loggedIn to set clientId");
|
||||
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.LOGIN_ACK, loggedIn);
|
||||
}
|
||||
|
||||
function registerHeartbeatAck() {
|
||||
logger.debug("register for heartbeatAck");
|
||||
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.HEARTBEAT_ACK, heartbeatAck);
|
||||
}
|
||||
|
||||
function registerBadStateError() {
|
||||
logger.debug("register for server_bad_state_error");
|
||||
|
|
@ -200,12 +110,6 @@
|
|||
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.DOWNLOAD_AVAILABLE, downloadAvailable);
|
||||
}
|
||||
|
||||
|
||||
function registerSocketClosed() {
|
||||
logger.debug("register for socket closed");
|
||||
context.JK.JamServer.registerOnSocketClosed(socketClosed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic error handler for Ajax calls.
|
||||
*/
|
||||
|
|
@ -259,7 +163,7 @@
|
|||
* being shown or hidden.
|
||||
* @screen is a string corresponding to the screen's layout-id attribute
|
||||
* @handler is an object with up to four optional keys:
|
||||
* beforeHide, afterHide, beforeShow, afterShow, which should all have
|
||||
* beforeHide, afterHide, beforeShow, afterShow, beforeDisconnect, which should all have
|
||||
* functions as values. If there is data provided by the screen's route
|
||||
* it will be provided to these functions.
|
||||
*/
|
||||
|
|
@ -387,26 +291,12 @@
|
|||
return userDeferred;
|
||||
}
|
||||
|
||||
this.activeElementEvent = function(evtName, data) {
|
||||
return this.layout.activeElementEvent(evtName, data);
|
||||
}
|
||||
|
||||
this.updateNotificationSeen = function(notificationId, notificationCreatedAt) {
|
||||
var time = new Date(notificationCreatedAt);
|
||||
|
||||
if(!notificationCreatedAt) {
|
||||
throw 'invalid value passed to updateNotificationSeen'
|
||||
}
|
||||
|
||||
if(!notificationLastSeenAt) {
|
||||
notificationLastSeenAt = notificationCreatedAt;
|
||||
notificationLastSeen = notificationId;
|
||||
logger.debug("updated notificationLastSeenAt with: " + notificationCreatedAt);
|
||||
}
|
||||
else if(time.getTime() > new Date(notificationLastSeenAt).getTime()) {
|
||||
notificationLastSeenAt = notificationCreatedAt;
|
||||
notificationLastSeen = notificationId;
|
||||
logger.debug("updated notificationLastSeenAt with: " + notificationCreatedAt);
|
||||
}
|
||||
else {
|
||||
logger.debug("ignored notificationLastSeenAt for: " + notificationCreatedAt);
|
||||
}
|
||||
context.JK.JamServer.updateNotificationSeen(notificationId, notificationCreatedAt);
|
||||
}
|
||||
|
||||
this.unloadFunction = function () {
|
||||
|
|
@ -437,11 +327,8 @@
|
|||
userDeferred = rest.getUserDetail();
|
||||
|
||||
if (opts.inClient) {
|
||||
registerLoginAck();
|
||||
registerHeartbeatAck();
|
||||
registerBadStateRecovered();
|
||||
registerBadStateError();
|
||||
registerSocketClosed();
|
||||
registerDownloadAvailable();
|
||||
context.JK.FaderHelpers.initialize();
|
||||
context.window.onunload = this.unloadFunction;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
// privates
|
||||
var logger = context.JK.logger;
|
||||
|
||||
var NOT_HANDLED = "not handled";
|
||||
|
||||
var me = null; // Reference to this instance for context sanity.
|
||||
|
||||
var opts = {
|
||||
|
|
@ -36,6 +38,7 @@
|
|||
notifyGutter: 10,
|
||||
collapsedSidebar: 30,
|
||||
panelHeaderHeight: 36,
|
||||
alwaysOpenPanelHeaderHeight:78, // for the search bar
|
||||
gutter: 60, // Margin around the whole UI
|
||||
screenMargin: 0, // Margin around screens (not headers/sidebar)
|
||||
gridOuterMargin: 6, // Outer margin on Grids (added to screenMargin if screen)
|
||||
|
|
@ -74,7 +77,7 @@
|
|||
}
|
||||
|
||||
function setInitialExpandedSidebarPanel() {
|
||||
expandedPanel = $('[layout="panel"]').first().attr("layout-id");
|
||||
expandedPanel = 'panelFriends';
|
||||
}
|
||||
|
||||
function layout() {
|
||||
|
|
@ -253,10 +256,9 @@
|
|||
}
|
||||
var $expandedPanel = $('[layout-id="' + expandedPanel + '"]');
|
||||
var $expandedPanelContents = $expandedPanel.find('[layout-panel="contents"]');
|
||||
var combinedHeaderHeight = $('[layout-panel="contents"]').length * opts.panelHeaderHeight;
|
||||
var searchHeight = $('.sidebar .search').first().height();
|
||||
var combinedHeaderHeight = ($('[layout-panel="contents"]').length - 1) * opts.panelHeaderHeight + opts.alwaysOpenPanelHeaderHeight;
|
||||
var expanderHeight = $('[layout-sidebar-expander]').height();
|
||||
var expandedPanelHeight = sidebarHeight - (combinedHeaderHeight + expanderHeight + searchHeight);
|
||||
var expandedPanelHeight = sidebarHeight - (combinedHeaderHeight + expanderHeight);
|
||||
$('[layout-panel="contents"]').hide();
|
||||
$('[layout-panel="contents"]').css({"height": "1px"});
|
||||
$expandedPanelContents.show();
|
||||
|
|
@ -315,11 +317,16 @@
|
|||
// This allows dialogs to use the notification.
|
||||
$('[layout="notify"]').css({"z-index": "1001", "padding": "20px"});
|
||||
$('[layout="panel"]').css({position: 'relative'});
|
||||
$('[layout-panel="expanded"] [layout-panel="header"]').css({
|
||||
margin: "0px",
|
||||
padding: "0px",
|
||||
height: opts.panelHeaderHeight + "px"
|
||||
});
|
||||
var $headers = $('[layout-panel="expanded"] [layout-panel="header"]');
|
||||
context._.each($headers, function($header) {
|
||||
$header = $($header);
|
||||
var isAlwaysOpenHeader = $header.is('.always-open');
|
||||
$header.css({
|
||||
margin: "0px",
|
||||
padding: "0px",
|
||||
height: (isAlwaysOpenHeader ? opts.alwaysOpenPanelHeaderHeight : opts.panelHeaderHeight) + "px"
|
||||
});
|
||||
})
|
||||
$('[layout-grid]').css({
|
||||
position: "relative"
|
||||
});
|
||||
|
|
@ -436,18 +443,29 @@
|
|||
return screenBindings[screen][evtName].call(me, data);
|
||||
}
|
||||
}
|
||||
return NOT_HANDLED;
|
||||
}
|
||||
|
||||
function dialogEvent(dialog, evtName, data) {
|
||||
if (dialog && dialog in dialogBindings) {
|
||||
if (evtName in dialogBindings[dialog]) {
|
||||
var result = dialogBindings[dialog][evtName].call(me, data);
|
||||
if (result === false) {
|
||||
return false;
|
||||
}
|
||||
return dialogBindings[dialog][evtName].call(me, data);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return NOT_HANDLED;
|
||||
}
|
||||
|
||||
function activeElementEvent(evtName, data) {
|
||||
var result = {};
|
||||
var currDialog = currentDialog();
|
||||
if(currDialog) {
|
||||
result.dialog = dialogEvent(currDialog.attr('layout-id'), evtName, data);
|
||||
}
|
||||
|
||||
if(currentScreen) {
|
||||
result.screen = screenEvent(currentScreen, evtName, data);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function onHashChange(e, postFunction) {
|
||||
|
|
@ -614,7 +632,7 @@
|
|||
}
|
||||
|
||||
function showDialog(dialog, options) {
|
||||
if (!dialogEvent(dialog, 'beforeShow', options)) {
|
||||
if (dialogEvent(dialog, 'beforeShow', options) === false) {
|
||||
return;
|
||||
}
|
||||
var $overlay = $('.dialog-overlay')
|
||||
|
|
@ -896,14 +914,30 @@
|
|||
return isNoisyNotification(payload);
|
||||
}
|
||||
|
||||
this.shouldFreezeAppOnDisconnect = function() {
|
||||
return shouldFreezeAppOnDisconnect();
|
||||
}
|
||||
|
||||
this.isDialogShowing = function() {
|
||||
return isDialogShowing();
|
||||
}
|
||||
|
||||
this.activeElementEvent = function(evtName, data) {
|
||||
return activeElementEvent(evtName, data);
|
||||
}
|
||||
|
||||
this.close = function (evt) {
|
||||
close(evt);
|
||||
};
|
||||
|
||||
this.beforeDisconnect = function() {
|
||||
fireEvents();
|
||||
}
|
||||
|
||||
this.afterReconnect = function() {
|
||||
fireEvents();
|
||||
}
|
||||
|
||||
this.closeDialog = closeDialog;
|
||||
|
||||
this.handleDialogState = handleDialogState;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,18 @@
|
|||
setCount(count + 1);
|
||||
}
|
||||
|
||||
function decrementNotificationCount() {
|
||||
var count = parseInt($count.text());
|
||||
if(count > 0) {
|
||||
count = count - 1;
|
||||
setCount(count);
|
||||
if(count == 0) {
|
||||
lowlightCount();
|
||||
missedNotificationsWhileAway = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set the element to white, and pulse it down to the un-highlighted value 2x, then set
|
||||
function pulseToDark() {
|
||||
logger.debug("pulsing notification badge")
|
||||
|
|
@ -348,6 +360,7 @@
|
|||
|
||||
|
||||
function deleteNotification(notificationId) {
|
||||
console.trace();
|
||||
var url = "/api/users/" + context.JK.currentUserId + "/notifications/" + notificationId;
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
|
|
@ -356,8 +369,11 @@
|
|||
url: url,
|
||||
processData: false,
|
||||
success: function(response) {
|
||||
$('li[notification-id=' + notificationId + ']').hide();
|
||||
//decrementNotificationCount();
|
||||
var notification = $('li[notification-id=' + notificationId + ']');
|
||||
if(notification.length > 0) {
|
||||
decrementNotificationCount();
|
||||
}
|
||||
notification.remove();
|
||||
},
|
||||
error: app.ajaxError
|
||||
});
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@
|
|||
}
|
||||
|
||||
context.JK.SearchResultScreen.onSearchSuccess = function(response) {
|
||||
$('#sidebar-search-results').show();
|
||||
searchResults(response, true);
|
||||
searchResults(response, false);
|
||||
context.JK.bindHoverEvents();
|
||||
|
|
@ -183,11 +184,6 @@
|
|||
if (isSidebar) {
|
||||
// show header
|
||||
$('#sidebar-search-header').show();
|
||||
// hide panels
|
||||
$('[layout-panel="contents"]').hide();
|
||||
$('[layout-panel="contents"]').css({"height": "1px"});
|
||||
// resize search results area
|
||||
$('#sidebar-search-results').height(context.JK.Sidebar.getHeight() + 'px');
|
||||
}
|
||||
else {
|
||||
$('#result-count').html(resultCount);
|
||||
|
|
|
|||
|
|
@ -139,8 +139,11 @@
|
|||
shareDialog.initialize(context.JK.FacebookHelperInstance);
|
||||
}
|
||||
|
||||
function beforeDisconnect() {
|
||||
return { freezeInteraction: true };
|
||||
}
|
||||
|
||||
function alertCallback(type, text) {
|
||||
function alertCallback(type, text) {
|
||||
|
||||
function timeCallback() {
|
||||
var start = new Date();
|
||||
|
|
@ -275,6 +278,13 @@
|
|||
|
||||
function afterShow(data) {
|
||||
|
||||
if(!context.JK.JamServer.connected) {
|
||||
promptLeave = false;
|
||||
app.notifyAlert("Not Connected", 'To create or join a session, you must be connected to the server.');
|
||||
window.location = '/client#/home'
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context.JK.hasOneConfiguredDevice() || context.JK.TrackHelpers.getUserTracks(context.jamClient).length == 0) {
|
||||
app.afterFtue = function() { initializeSession(); };
|
||||
app.cancelFtue = function() { promptLeave = false; window.location = '/client#/home' };
|
||||
|
|
@ -1516,7 +1526,8 @@
|
|||
'beforeShow': beforeShow,
|
||||
'afterShow': afterShow,
|
||||
'beforeHide': beforeHide,
|
||||
'beforeLeave' : beforeLeave
|
||||
'beforeLeave' : beforeLeave,
|
||||
'beforeDisconnect' : beforeDisconnect,
|
||||
};
|
||||
app.bindScreen('session', screenBindings);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -151,6 +151,11 @@
|
|||
var $parentRow = $('tr[id=' + session.id + ']', tbGroup);
|
||||
|
||||
$('.join-link', $parentRow).click(function(evt) {
|
||||
if(!context.JK.JamServer.connected) {
|
||||
app.notifyAlert("Not Connected", 'To create or join a session, you must be connected to the server.');
|
||||
return false;
|
||||
}
|
||||
|
||||
// If no FTUE, show that first.
|
||||
if (!context.JK.hasOneConfiguredDevice() || context.JK.TrackHelpers.getUserTracks(context.jamClient).length == 0) {
|
||||
app.afterFtue = function() { joinClick(session.id); };
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
// we track all the clientIDs of all the participants ever seen by this session, so that we can reliably convert a clientId from the backend into a username/avatar
|
||||
var participantsEverSeen = {};
|
||||
|
||||
function id() {
|
||||
function id() {
|
||||
return currentSession ? currentSession.id : null;
|
||||
}
|
||||
|
||||
|
|
@ -379,67 +379,11 @@
|
|||
});
|
||||
}
|
||||
|
||||
function reconnect() {
|
||||
context.JK.CurrentSessionModel.leaveCurrentSession()
|
||||
.always(function() {
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
function registerReconnect(content) {
|
||||
$('a.disconnected-reconnect', content).click(function() {
|
||||
|
||||
var template = $('#template-reconnecting').html();
|
||||
var templateHtml = context.JK.fillTemplate(template, null);
|
||||
var content = context.JK.Banner.show({
|
||||
html : template
|
||||
});
|
||||
|
||||
rest.serverHealthCheck()
|
||||
.done(function() {
|
||||
reconnect();
|
||||
})
|
||||
.fail(function(xhr, textStatus, errorThrown) {
|
||||
|
||||
if(xhr && xhr.status >= 100) {
|
||||
// we could connect to the server, and it's alive
|
||||
reconnect();
|
||||
}
|
||||
else {
|
||||
var template = $('#template-could-not-reconnect').html();
|
||||
var templateHtml = context.JK.fillTemplate(template, null);
|
||||
var content = context.JK.Banner.show({
|
||||
html : template
|
||||
});
|
||||
|
||||
registerReconnect(content);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function onWebsocketDisconnected(in_error) {
|
||||
|
||||
if(app.clientUpdating) {
|
||||
// we don't want to do a 'cover the whole screen' dialog
|
||||
// because the client update is already showing.
|
||||
return;
|
||||
}
|
||||
|
||||
// kill the streaming of the session immediately
|
||||
logger.debug("calling jamClient.LeaveSession for clientId=" + clientId);
|
||||
client.LeaveSession({ sessionID: currentSessionId });
|
||||
|
||||
if(in_error) {
|
||||
var template = $('#template-disconnected').html();
|
||||
var templateHtml = context.JK.fillTemplate(template, null);
|
||||
var content = context.JK.Banner.show({
|
||||
html : template
|
||||
}) ;
|
||||
registerReconnect(content);
|
||||
}
|
||||
}
|
||||
|
||||
// returns a deferred object
|
||||
|
|
@ -456,7 +400,6 @@
|
|||
if(foundParticipant) {
|
||||
return $.Deferred().resolve(foundParticipant.user).promise();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: find it via some REST API if not found?
|
||||
|
|
|
|||
|
|
@ -104,40 +104,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
context.JK.Sidebar.getHeight = function() {
|
||||
// TODO: refactor this - copied from layout.js
|
||||
var sidebarHeight = $(context).height() - 75 - 2 * 60 + $('[layout-sidebar-expander]').height();
|
||||
var combinedHeaderHeight = $('[layout-panel="contents"]').length * 36;
|
||||
var searchHeight = $('.sidebar .search').first().height();
|
||||
var expanderHeight = $('[layout-sidebar-expander]').height();
|
||||
var expandedPanelHeight = sidebarHeight - (combinedHeaderHeight + expanderHeight + searchHeight);
|
||||
return expandedPanelHeight;
|
||||
}
|
||||
|
||||
function showFriendsPanel() {
|
||||
|
||||
var $expandedPanelContents = $('[layout-id="panelFriends"] [layout-panel="contents"]');
|
||||
var expandedPanelHeight = context.JK.Sidebar.getHeight();
|
||||
|
||||
// hide all other contents
|
||||
$('[layout-panel="contents"]').hide();
|
||||
$('[layout-panel="contents"]').css({"height": "1px"});
|
||||
|
||||
// show the appropriate contens
|
||||
$expandedPanelContents.show();
|
||||
$expandedPanelContents.animate({"height": expandedPanelHeight + "px"}, 400);
|
||||
}
|
||||
|
||||
function hideSearchResults() {
|
||||
emptySearchResults();
|
||||
$('#search-input').val('');
|
||||
$('#sidebar-search-header').hide();
|
||||
showFriendsPanel();
|
||||
}
|
||||
|
||||
function emptySearchResults() {
|
||||
$('#sidebar-search-results').empty();
|
||||
$('#sidebar-search-results').height('0px');
|
||||
}
|
||||
|
||||
var delay = (function(){
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
var $previousMessagesScroller = null;
|
||||
var $sendTextMessage = null;
|
||||
var $form = null;
|
||||
var $interactionBlocker = null;
|
||||
var $disconnectedMsg = null;
|
||||
var $textBox = null;
|
||||
var userLookup = null;
|
||||
var otherId = null;
|
||||
|
|
@ -48,10 +50,14 @@
|
|||
}
|
||||
|
||||
function sendMessage() {
|
||||
if(!context.JK.JamServer.connected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var msg = $textBox.val();
|
||||
if(!msg || msg == '') {
|
||||
// don't bother the server with empty messages
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!sendingMessage) {
|
||||
|
|
@ -136,35 +142,30 @@
|
|||
$textBox.focus();
|
||||
}
|
||||
|
||||
function beforeShow(args) {
|
||||
|
||||
app.layout.closeDialog('text-message') // ensure no others are showing. this is a singleton dialog
|
||||
|
||||
function renderDialog() {
|
||||
app.user()
|
||||
.done(function(userDetail) {
|
||||
.done(function (userDetail) {
|
||||
user = userDetail;
|
||||
|
||||
var other = args.d1;
|
||||
|
||||
if(!other) throw "other must be specified in TextMessageDialog"
|
||||
otherId = other;
|
||||
|
||||
showing = true;
|
||||
|
||||
userLookup[user.id] = user;
|
||||
|
||||
rest.getUserDetail({id: otherId})
|
||||
.done(function(otherUser) {
|
||||
.done(function (otherUser) {
|
||||
|
||||
userLookup[otherUser.id] = otherUser;
|
||||
$dialog.find('.receiver-name').text(otherUser.name);
|
||||
$dialog.find('textarea').attr('placeholder', 'enter a message to ' + otherUser.name + '...');
|
||||
$dialog.find('.offline-tip').text('An email will be sent if ' + otherUser.name + ' is offline');
|
||||
$dialog.find('.offline-tip').text('An email will be sent if ' + otherUser.name + ' is offline');
|
||||
if (!context.JK.JamServer.connected) {
|
||||
renderNotConnected();
|
||||
}
|
||||
$sendTextMessage.click(sendMessage);
|
||||
|
||||
rest.getNotifications(buildParams())
|
||||
.done(function(response) {
|
||||
context._.each(response, function(textMessage) {
|
||||
.done(function (response) {
|
||||
context._.each(response, function (textMessage) {
|
||||
renderMessage(textMessage.message, textMessage.source_user_id, userLookup[textMessage.source_user_id].name, textMessage.created_at);
|
||||
})
|
||||
|
||||
|
|
@ -172,21 +173,53 @@
|
|||
fullyInitialized = true;
|
||||
drainQueue();
|
||||
})
|
||||
.fail(function(jqXHR) {
|
||||
.fail(function (jqXHR) {
|
||||
app.notifyServerError(jqXHR, 'Unable to Load Conversation')
|
||||
})
|
||||
})
|
||||
.fail(function(jqXHR) {
|
||||
.fail(function (jqXHR) {
|
||||
app.notifyServerError(jqXHR, 'Unable to Load Other User')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function beforeShow(args) {
|
||||
|
||||
app.layout.closeDialog('text-message') // ensure no others are showing. this is a singleton dialog
|
||||
|
||||
var other = args.d1;
|
||||
|
||||
if (!other) throw "other must be specified in TextMessageDialog"
|
||||
otherId = other;
|
||||
|
||||
renderDialog();
|
||||
}
|
||||
|
||||
function afterHide() {
|
||||
showing = false;
|
||||
reset();
|
||||
}
|
||||
|
||||
function renderNotConnected() {
|
||||
$interactionBlocker.addClass('active');
|
||||
$disconnectedMsg.addClass('active');
|
||||
}
|
||||
|
||||
function renderConnected() {
|
||||
$interactionBlocker.removeClass('active');
|
||||
$disconnectedMsg.removeClass('active');
|
||||
}
|
||||
|
||||
function beforeDisconnect() {
|
||||
renderNotConnected();
|
||||
}
|
||||
|
||||
function afterConnect() {
|
||||
renderConnected();
|
||||
reset();
|
||||
renderDialog();
|
||||
}
|
||||
|
||||
function pasteIntoInput(el, text) {
|
||||
el.focus();
|
||||
if (typeof el.selectionStart == "number"
|
||||
|
|
@ -255,24 +288,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
function showDialog(_other) {
|
||||
|
||||
app.layout.closeDialog('text-message') // this dialog is implemented as a singleton, so must enforce this
|
||||
|
||||
reset();
|
||||
|
||||
if(!_other) throw "other must be specified in TextMessageDialog"
|
||||
otherId = _other;
|
||||
|
||||
app.layout.showDialog('text-message')
|
||||
}*/
|
||||
|
||||
function initialize() {
|
||||
var dialogBindings = {
|
||||
'beforeShow' : beforeShow,
|
||||
'afterShow' : afterShow,
|
||||
'afterHide': afterHide
|
||||
'afterHide': afterHide,
|
||||
'beforeDisconnect': beforeDisconnect,
|
||||
'afterConnect': afterConnect
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -284,6 +306,8 @@
|
|||
$sendTextMessage = $dialog.find('.btn-send-text-message');
|
||||
$form = $dialog.find('form');
|
||||
$textBox = $form.find('textarea');
|
||||
$interactionBlocker = $dialog.find('.interaction-blocker');
|
||||
$disconnectedMsg = $dialog.find('.disconnected-msg');
|
||||
|
||||
events();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,19 @@
|
|||
#banner {
|
||||
display:none;
|
||||
|
||||
&[data-type="reconnect"] {
|
||||
height:240px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight:bold;
|
||||
font-size:x-large;
|
||||
}
|
||||
|
||||
.countdown {
|
||||
font-weight:bold;
|
||||
min-width:9px;
|
||||
display:inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
#banner h2 {
|
||||
font-weight:bold;
|
||||
font-size:x-large;
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@
|
|||
*= require ./account
|
||||
*= require ./search
|
||||
*= require ./ftue
|
||||
*= require ./jamServer
|
||||
*= require ./gearWizard
|
||||
*= require ./whatsNextDialog
|
||||
*= require ./invitationDialog
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
|
||||
&.musician-bubble {
|
||||
width:410px;
|
||||
width:425px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
.no-websocket-connection {
|
||||
display:none;
|
||||
text-align:center;
|
||||
width:100%;
|
||||
position:absolute;
|
||||
}
|
||||
|
||||
.server-connection {
|
||||
margin:auto;
|
||||
display:inline-block;
|
||||
zoom:1;
|
||||
text-align:center;
|
||||
padding:10px 20px;
|
||||
background-color:#404040;
|
||||
border-color:#ccc;
|
||||
border-style:solid;
|
||||
border-width:0 2px 2px;
|
||||
|
||||
|
||||
-webkit-box-shadow: 0px 0px 15px rgba(50, 50, 50, 1);
|
||||
-moz-box-shadow: 0px 0px 15px rgba(50, 50, 50, 1);
|
||||
box-shadow: 0px 0px 15px rgba(50, 50, 50, 1);
|
||||
|
||||
h2 {
|
||||
font-size:20px;
|
||||
vertical-align:baseline;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
img.alert-icon {
|
||||
top:14px;
|
||||
height:16px;
|
||||
width:16px;
|
||||
|
||||
&.left-side {
|
||||
padding-right:20px;
|
||||
}
|
||||
|
||||
&.right-side {
|
||||
padding-left:20px;
|
||||
}
|
||||
}
|
||||
|
||||
.reconnect-progress-msg {
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
.reconnect-countdown {
|
||||
}
|
||||
|
||||
#reconnect-now {
|
||||
margin-top:10px;
|
||||
}
|
||||
|
||||
.countdown {
|
||||
font-weight:bold;
|
||||
min-width:9px;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
.countdown-seconds {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -58,4 +58,39 @@
|
|||
text-align:center;
|
||||
width:50px;
|
||||
}
|
||||
|
||||
.interaction-blocker {
|
||||
display:none;
|
||||
position:absolute;
|
||||
background-color:#333;
|
||||
text-align:center;
|
||||
|
||||
&.active {
|
||||
display:block;
|
||||
top:0;
|
||||
bottom:40px;
|
||||
left:0;
|
||||
right:0;
|
||||
|
||||
opacity:0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.disconnected-msg {
|
||||
color:white;
|
||||
font-size:25px;
|
||||
left:0;
|
||||
margin:auto;
|
||||
position:absolute;
|
||||
text-align:center;
|
||||
top:30%;
|
||||
width:100%;
|
||||
display:none;
|
||||
|
||||
&.active {
|
||||
display:inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -9,3 +9,8 @@ body {
|
|||
height:100%;
|
||||
margin:0 !important;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
width:1280px;
|
||||
margin:0 auto;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
|
||||
class VideosController < ApplicationController
|
||||
|
||||
def show_dialog
|
||||
@video_id = @params[:video_id]
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
<!-- generic banner for use by an code -->
|
||||
<div class="overlay" id="banner_overlay"></div>
|
||||
<div id="banner" class="dialog-overlay-sm">
|
||||
<div id="banner" class="dialog-overlay-sm" data-type="">
|
||||
|
||||
<!-- dialog header -->
|
||||
<div class="content-head">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
.no-websocket-connection
|
||||
.server-connection
|
||||
%h2
|
||||
= image_tag( "content/icon_alert.png" , :class => "alert-icon left-side" )
|
||||
%span Disconnected from Server
|
||||
= image_tag( "content/icon_alert.png" , :class => "alert-icon right-side" )
|
||||
.message-contents
|
||||
%script{type: 'text/template', id: 'template-server-connection'}
|
||||
%div.reconnect-progress-msg
|
||||
%span.reconnect-before= 'Reconnecting automatically in '
|
||||
%span.reconnect-countdown= '{{data.countdown}}'
|
||||
%a.disconnected-reconnect.reconnect-enabled{href:'#'} RECONNECT NOW
|
||||
|
|
@ -11,28 +11,36 @@
|
|||
</div>
|
||||
|
||||
<!-- Search Box -->
|
||||
<div class="search">
|
||||
<div layout-panel="header" class="panel-header">
|
||||
<h2>search</h2>
|
||||
<div class="search" layout="panel" layout-id="panelSearch">
|
||||
<div layout-panel="collapsed">
|
||||
</div>
|
||||
<div class="searchbox">
|
||||
<form id="searchForm">
|
||||
<input id="search-input" autocomplete="off" type="text" name="search" placeholder="enter search terms" />
|
||||
</form>
|
||||
<div id="sidebar-search-header" style="margin: 4px 4px 8px 8px">
|
||||
<div class="left">
|
||||
<strong><a id="sidebar-search-expand" style="color:#fff; text-decoration:underline">« EXPAND</a></strong>
|
||||
</div>
|
||||
<!-- search filter dropdown -->
|
||||
<div class="right">
|
||||
Show: <%= select_tag(Search::SEARCH_TEXT_TYPE_ID, options_for_select(Search::SEARCH_TEXT_TYPES.collect { |ii| [ii.to_s.titleize, ii] })) %>
|
||||
<div layout-panel="expanded" class="panel expanded">
|
||||
<div layout-panel="header" class="panel-header always-open">
|
||||
<h2>search</h2>
|
||||
<div class="searchbox">
|
||||
<form id="searchForm">
|
||||
<input id="search-input" autocomplete="off" type="text" name="search" placeholder="enter search terms" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="clear:both;"></div><br />
|
||||
<!-- border between header and beginning of search results -->
|
||||
<div class="sidebar-search-result"></div>
|
||||
<div id="sidebar-search-results" class="results-wrapper">
|
||||
|
||||
<!-- border between header and beginning of search results -->
|
||||
<!--<div class="sidebar-search-result"></div>-->
|
||||
<div class="panelcontents" layout-panel="contents">
|
||||
<div id="sidebar-search-header" style="margin: 4px 4px 8px 8px">
|
||||
<div class="left">
|
||||
<strong><a id="sidebar-search-expand" style="color:#fff; text-decoration:underline">« EXPAND</a></strong>
|
||||
</div>
|
||||
<!-- search filter dropdown -->
|
||||
<div class="right">
|
||||
Show: <%= select_tag(Search::SEARCH_TEXT_TYPE_ID, options_for_select(Search::SEARCH_TEXT_TYPES.collect { |ii| [ii.to_s.titleize, ii] })) %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="clear:both;"></div><br />
|
||||
<div id="sidebar-search-results" class="results-wrapper"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@
|
|||
.right
|
||||
%a.button-grey.btn-close-dialog{href:'#', 'layout-action' => 'close'} CLOSE
|
||||
%a.button-orange.btn-send-text-message{href:'#'} SEND
|
||||
.interaction-blocker
|
||||
%span.disconnected-msg DISCONNECTED FROM SERVER
|
||||
|
||||
|
||||
%script{type: 'text/template', id: 'template-previous-message'}
|
||||
.previous-message
|
||||
|
|
|
|||
|
|
@ -4,39 +4,12 @@
|
|||
<br />
|
||||
<br />
|
||||
<div align="center">
|
||||
You have been disconnected from JamKazam.
|
||||
You have been disconnected from JamKazam. <br/><br/>
|
||||
<span class="reconnect-progress-msg">Reconnecting automatically in <span class="reconnect-countdown">{countdown}</span></span>
|
||||
</div>
|
||||
<br clear="all" /><br />
|
||||
|
||||
<div class="right">
|
||||
<a href="#" class="button-orange disconnected-reconnect">RECONNECT</a>
|
||||
<a href="#" class="button-orange disconnected-reconnect">RECONNECT NOW</a>
|
||||
</div>
|
||||
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="template-reconnecting">
|
||||
<h2 align="center">Reconnecting to Server</h2>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<div align="center">
|
||||
Attempting to reestablish connection to the server...
|
||||
</div>
|
||||
<br clear="all" /><br />
|
||||
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="template-could-not-reconnect">
|
||||
<h2 align="center">Unable to Reconnect</h2>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<div align="center">
|
||||
We were not able to reconnect to JamKazam. <br/></br/>
|
||||
Please check your internet connection, then try again later.
|
||||
</div>
|
||||
<br clear="all" /><br />
|
||||
<div class="right">
|
||||
<a href="#" class="button-orange disconnected-reconnect">TRY AGAIN TO RECONNECT</a>
|
||||
</div>
|
||||
</script>
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
<%= render "faders" %>
|
||||
<%= render "vu_meters" %>
|
||||
<%= render "ftue" %>
|
||||
<%= render "jamServer" %>
|
||||
<%= render "clients/gear/gear_wizard" %>
|
||||
<%= render "terms" %>
|
||||
<%= render "leaveSessionWarning" %>
|
||||
|
|
@ -118,8 +119,9 @@
|
|||
window.location.href = '/?redirect-to=' + encodeURIComponent(JK.locationPath());
|
||||
<% end %>
|
||||
|
||||
|
||||
// Some things can't be initialized until we're connected. Put them here.
|
||||
function _initAfterConnect() {
|
||||
function _initAfterConnect(connected) {
|
||||
if (this.didInitAfterConnect) return;
|
||||
this.didInitAfterConnect = true
|
||||
|
||||
|
|
@ -242,12 +244,17 @@
|
|||
var testBridgeScreen = new JK.TestBridgeScreen(JK.app);
|
||||
testBridgeScreen.initialize();
|
||||
|
||||
if(!connected) {
|
||||
jamServer.initiateReconnect(null, true);
|
||||
}
|
||||
|
||||
JK.app.initialRouting();
|
||||
JK.hideCurtain(300);
|
||||
}
|
||||
|
||||
JK.app = JK.JamKazam();
|
||||
JK.app.initAfterConnect = _initAfterConnect;
|
||||
var jamServer = new JK.JamServer(JK.app);
|
||||
jamServer.initialize();
|
||||
|
||||
// If no jamClient (when not running in native client)
|
||||
// create a fake one.
|
||||
|
|
@ -302,25 +309,16 @@
|
|||
|
||||
JK.TickDuration('.feed-entry.music-session-history-entry .inprogress .tick-duration');
|
||||
|
||||
JK.JamServer.connect(); // singleton here defined in JamServer.js
|
||||
JK.JamServer.connect() // singleton here defined in JamServer.js
|
||||
.done(function() {
|
||||
_initAfterConnect(true);
|
||||
})
|
||||
.fail(function() {
|
||||
_initAfterConnect(false);
|
||||
});
|
||||
|
||||
// this ensures that there is always a CurrentSessionModel, even if it's for a non-active session
|
||||
JK.CurrentSessionModel = new JK.SessionModel(JK.app, JK.JamServer, window.jamClient);
|
||||
|
||||
// Run a check to see if we're logged in yet. Only after that should
|
||||
// we initialize the other screens.
|
||||
function testConnected() {
|
||||
this.numCalls = (this.numCalls || 0) + 1;
|
||||
if (JK.clientId) {
|
||||
JK.app.initAfterConnect();
|
||||
} else {
|
||||
if (50 <= this.numCalls) { // 5 second max
|
||||
JK.notifyAlert('Server Error', 'Could not connect to server. Please try again later.')
|
||||
} else {
|
||||
window.setTimeout(testConnected, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
testConnected();
|
||||
}
|
||||
|
||||
JK.bindHoverEvents();
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@
|
|||
<body class="jam">
|
||||
<div id="minimal-container">
|
||||
<%= javascript_include_tag "minimal/minimal" %>
|
||||
<%= yield %>
|
||||
<div class="wrapper">
|
||||
<%= yield %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
|
@ -56,5 +58,4 @@
|
|||
<%= render "shared/ga" %>
|
||||
<!-- version info: <%= version %> -->
|
||||
</body>
|
||||
</html>
|
||||
c
|
||||
</html>
|
||||
|
|
@ -2,8 +2,9 @@ unless $rails_rake_task
|
|||
|
||||
JamWebEventMachine.start
|
||||
|
||||
if APP_CONFIG.websocket_gateway_enable && !$rails_rake_task
|
||||
if APP_CONFIG.websocket_gateway_enable && !$rails_rake_task && ENV['NO_WEBSOCKET_GATEWAY'] != '1'
|
||||
|
||||
current = Thread.current
|
||||
Thread.new do
|
||||
JamWebsockets::Server.new.run(
|
||||
:port => APP_CONFIG.websocket_gateway_port,
|
||||
|
|
@ -11,8 +12,10 @@ unless $rails_rake_task
|
|||
:connect_time_stale => APP_CONFIG.websocket_gateway_connect_time_stale,
|
||||
:connect_time_expire => APP_CONFIG.websocket_gateway_connect_time_expire,
|
||||
:rabbitmq_host => APP_CONFIG.rabbitmq_host,
|
||||
:rabbitmq_port => APP_CONFIG.rabbitmq_port)
|
||||
:rabbitmq_port => APP_CONFIG.rabbitmq_port,
|
||||
:calling_thread => current)
|
||||
end
|
||||
|
||||
Thread.stop
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -63,8 +63,6 @@ SampleApp::Application.routes.draw do
|
|||
|
||||
match '/events/:slug', to: 'events#show', :via => :get, :as => 'event'
|
||||
|
||||
match '/video/dialog/:id', to: 'videos#show', :via => :get
|
||||
|
||||
match '/endorse/:id/:service', to: 'users#endorse', :as => 'endorse'
|
||||
|
||||
# temporarily allow for debugging--only allows admini n
|
||||
|
|
|
|||
|
|
@ -4,12 +4,6 @@ describe "Account", :js => true, :type => :feature, :capybara_feature => true do
|
|||
|
||||
subject { page }
|
||||
|
||||
before(:all) do
|
||||
Capybara.javascript_driver = :poltergeist
|
||||
Capybara.current_driver = Capybara.javascript_driver
|
||||
Capybara.default_wait_time = 10
|
||||
end
|
||||
|
||||
let(:user) { FactoryGirl.create(:user) }
|
||||
|
||||
before(:each) do
|
||||
|
|
@ -92,7 +86,7 @@ describe "Account", :js => true, :type => :feature, :capybara_feature => true do
|
|||
before(:each) do
|
||||
fill_in "first_name", with: "Bobby"
|
||||
fill_in "last_name", with: "Toes"
|
||||
find('input[name=subscribe_email]').set(false)
|
||||
uncheck('subscribe_email')
|
||||
find("#account-edit-profile-submit").trigger(:click)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,6 @@ describe "Notification Highlighter", :js => true, :type => :feature, :capybara_f
|
|||
|
||||
subject { page }
|
||||
|
||||
before(:all) do
|
||||
Capybara.javascript_driver = :poltergeist
|
||||
Capybara.current_driver = Capybara.javascript_driver
|
||||
Capybara.default_wait_time = 10
|
||||
end
|
||||
|
||||
let(:user) { FactoryGirl.create(:user) }
|
||||
let(:user2) { FactoryGirl.create(:user) }
|
||||
|
|
@ -138,30 +133,34 @@ describe "Notification Highlighter", :js => true, :type => :feature, :capybara_f
|
|||
end
|
||||
end
|
||||
|
||||
describe "delete notification" do
|
||||
|
||||
describe "user no unseen notifications" do
|
||||
describe "notification occurs in realtime" do
|
||||
before(:each) do
|
||||
User.delete_all
|
||||
end
|
||||
|
||||
describe "sidebar is open" do
|
||||
describe "user can see notifications" do
|
||||
it "stays deactivated" do
|
||||
it "while notification panel closed" do
|
||||
# we should see the count go to 1, but once the notification is accepted, which causes it to delete,
|
||||
# we should see the count go back down to 0.
|
||||
|
||||
end
|
||||
end
|
||||
in_client(user) do
|
||||
sign_in_poltergeist(user)
|
||||
end
|
||||
|
||||
describe "user can not see notifications" do
|
||||
describe "with dialog open" do
|
||||
it "temporarily activates" do
|
||||
in_client(user2) do
|
||||
sign_in_poltergeist(user2)
|
||||
find_musician(user)
|
||||
find(".result-list-button-wrapper[data-musician-id='#{user.id}'] .search-m-friend").trigger(:click)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
in_client(user) do
|
||||
badge = find("#{NOTIFICATION_PANEL} .badge", text: '1')
|
||||
badge['class'].include?('highlighted').should == true
|
||||
|
||||
describe "with document blurred" do
|
||||
it "temporarily activates" do
|
||||
find('#notification #ok-button', text: 'ACCEPT').trigger(:click)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
badge = find("#{NOTIFICATION_PANEL} .badge", text: '0')
|
||||
badge['class'].include?('highlighted').should == false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
require 'spec_helper'
|
||||
|
||||
# tests what happens when the websocket connection goes away
|
||||
describe "Reconnect", :js => true, :type => :feature, :capybara_feature => true do
|
||||
|
||||
subject { page }
|
||||
|
||||
let(:user1) { FactoryGirl.create(:user) }
|
||||
let(:user2) { FactoryGirl.create(:user) }
|
||||
|
||||
before(:each) do
|
||||
emulate_client
|
||||
end
|
||||
|
||||
it "websocket connection is down on initial connection" do
|
||||
|
||||
FactoryGirl.create(:friendship, :user => user1, :friend => user2)
|
||||
FactoryGirl.create(:friendship, :user => user2, :friend => user1)
|
||||
|
||||
Rails.application.config.stub(:websocket_gateway_uri).and_return('ws://localhost:99/websocket') # bogus port
|
||||
|
||||
sign_in_poltergeist(user1, validate: false)
|
||||
|
||||
page.should have_selector('.no-websocket-connection')
|
||||
|
||||
find('.homecard.createsession').trigger(:click)
|
||||
find('h1', text:'create session')
|
||||
find('#btn-create-session').trigger(:click)
|
||||
find('#notification h2', text: 'Not Connected') # get notified you can't go to create session
|
||||
page.evaluate_script('window.history.back()')
|
||||
|
||||
find('.homecard.findsession').trigger(:click)
|
||||
find('#notification h2', text: 'Not Connected') # get notified you can't go to find session
|
||||
find('h2', text: 'create session') # and be back on home screen
|
||||
|
||||
find('.homecard.feed').trigger(:click)
|
||||
find('h1', text:'feed')
|
||||
page.evaluate_script('window.history.back()')
|
||||
|
||||
find('.homecard.musicians').trigger(:click)
|
||||
find('h1', text:'musicians')
|
||||
page.evaluate_script('window.history.back()')
|
||||
|
||||
find('.homecard.profile').trigger(:click)
|
||||
find('h1', text:'profile')
|
||||
page.evaluate_script('window.history.back()')
|
||||
|
||||
find('.homecard.account').trigger(:click)
|
||||
find('h1', text:'account')
|
||||
page.evaluate_script('window.history.back()')
|
||||
|
||||
initiate_text_dialog user2
|
||||
|
||||
find('span.disconnected-msg', text: 'DISCONNECTED FROM SERVER')
|
||||
end
|
||||
|
||||
it "websocket goes down on home page" do
|
||||
|
||||
sign_in_poltergeist(user1)
|
||||
|
||||
5.times do
|
||||
close_websocket
|
||||
|
||||
# we should see indication that the websocket is down
|
||||
page.should have_selector('.no-websocket-connection')
|
||||
|
||||
# but.. after a few seconds, it should reconnect on it's own
|
||||
page.should_not have_selector('.no-websocket-connection')
|
||||
end
|
||||
|
||||
# then verify we can create a session
|
||||
|
||||
create_join_session(user1, [user2])
|
||||
|
||||
formal_leave_by user1
|
||||
|
||||
# websocket goes down while chatting
|
||||
in_client(user1) do
|
||||
initiate_text_dialog user2
|
||||
|
||||
# normal, happy dialog
|
||||
page.should_not have_selector('span.disconnected-msg', text: 'DISCONNECTED FROM SERVER')
|
||||
|
||||
close_websocket
|
||||
|
||||
# dialog-specific disconnect should show
|
||||
page.should have_selector('span.disconnected-msg', text: 'DISCONNECTED FROM SERVER')
|
||||
# and generic disconnect
|
||||
page.should have_selector('.no-websocket-connection')
|
||||
|
||||
# after a few seconds, the page should reconnect on it's own
|
||||
page.should_not have_selector('span.disconnected-msg', text: 'DISCONNECTED FROM SERVER')
|
||||
page.should_not have_selector('.no-websocket-connection')
|
||||
end
|
||||
end
|
||||
|
||||
it "websocket goes down on session page" do
|
||||
|
||||
create_session(creator: user1)
|
||||
|
||||
5.times do
|
||||
close_websocket
|
||||
|
||||
# we should see indication that the websocket is down
|
||||
page.should have_selector('h2', text: 'Disconnected from Server')
|
||||
|
||||
# but.. after a few seconds, it should reconnect on it's own
|
||||
page.should_not have_selector('h2', text: 'Disconnected from Server')
|
||||
|
||||
find('h1', text:'session')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -4,13 +4,8 @@ describe "Profile Menu", :js => true, :type => :feature, :capybara_feature => tr
|
|||
|
||||
subject { page }
|
||||
|
||||
before(:all) do
|
||||
Capybara.javascript_driver = :poltergeist
|
||||
Capybara.current_driver = Capybara.javascript_driver
|
||||
Capybara.default_wait_time = 10
|
||||
end
|
||||
|
||||
let(:user) { FactoryGirl.create(:user) }
|
||||
let(:user2) { FactoryGirl.create(:user) }
|
||||
|
||||
before(:each) do
|
||||
UserMailer.deliveries.clear
|
||||
|
|
@ -36,5 +31,19 @@ describe "Profile Menu", :js => true, :type => :feature, :capybara_feature => tr
|
|||
end
|
||||
end
|
||||
|
||||
describe "panel behavior" do
|
||||
it "search, then click notifications" do
|
||||
|
||||
notification = Notification.send_text_message("text message", user2, user)
|
||||
notification.errors.any?.should be_false
|
||||
|
||||
site_search(user2.name, validate: user2)
|
||||
|
||||
open_notifications
|
||||
|
||||
find("#sidebar-notification-list li[notification-id='#{notification.id}']")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ Thread.new {
|
|||
end
|
||||
}
|
||||
|
||||
current = Thread.current
|
||||
Thread.new do
|
||||
ActiveRecord::Base.connection.disconnect!
|
||||
ActiveRecord::Base.establish_connection(YAML::load(File.open('config/database.yml'))["test"])
|
||||
|
|
@ -52,12 +53,13 @@ Thread.new do
|
|||
:connect_time_stale => 2,
|
||||
:connect_time_expire => 5,
|
||||
:rabbitmq_host => 'localhost',
|
||||
:rabbitmq_port => 5672)
|
||||
:rabbitmq_port => 5672,
|
||||
:calling_thread => current)
|
||||
rescue Exception => e
|
||||
puts "websocket-gateway failed: #{e}"
|
||||
end
|
||||
|
||||
end
|
||||
Thread.stop
|
||||
|
||||
|
||||
ActiveRecord::Base.connection.disconnect!
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ def site_search(text, options = {})
|
|||
fill_in "search-input", with: text
|
||||
end
|
||||
|
||||
find('#sidebar-search-expand').trigger(:click) if options[:expand]
|
||||
if options[:expand]
|
||||
page.driver.execute_script("jQuery('#searchForm').submit()")
|
||||
find('h1', text:'search results')
|
||||
end
|
||||
end
|
||||
|
||||
# goes to the musician tile, and tries to find a musician
|
||||
|
|
@ -37,6 +40,16 @@ def find_musician(user)
|
|||
raise "unable to find musician #{user}"
|
||||
end
|
||||
|
||||
def initiate_text_dialog(user)
|
||||
|
||||
# verify that the chat window is grayed out
|
||||
site_search(user.first_name, expand: true)
|
||||
|
||||
find("#search-results a[user-id=\"#{user.id}\"][hoveraction=\"musician\"]", text: user.name).hover_intent
|
||||
find('#musician-hover #btnMessage').trigger(:click)
|
||||
find('h1', text: 'conversation with ' + user.name)
|
||||
end
|
||||
|
||||
# sends a text message in the chat interface.
|
||||
def send_text_message(msg, options={})
|
||||
find('#text-message-dialog') # assert that the dialog is showing already
|
||||
|
|
@ -62,6 +75,7 @@ def open_notifications
|
|||
find("#{NOTIFICATION_PANEL} .panel-header").trigger(:click)
|
||||
end
|
||||
|
||||
|
||||
def hover_intent(element)
|
||||
element.hover
|
||||
element.hover
|
||||
|
|
@ -95,4 +109,8 @@ end
|
|||
# simulates focus event on window
|
||||
def window_focus
|
||||
page.evaluate_script(%{window.jQuery(window).trigger('focus');})
|
||||
end
|
||||
|
||||
def close_websocket
|
||||
page.evaluate_script("window.JK.JamServer.close(true)")
|
||||
end
|
||||
|
|
@ -86,13 +86,19 @@ def set_cookie(k, v)
|
|||
end
|
||||
end
|
||||
|
||||
def sign_in_poltergeist(user)
|
||||
def sign_in_poltergeist(user, options = {})
|
||||
validate = options[:validate]
|
||||
validate = true if validate.nil?
|
||||
|
||||
visit signin_path
|
||||
fill_in "Email Address:", with: user.email
|
||||
fill_in "Password:", with: user.password
|
||||
click_button "SIGN IN"
|
||||
|
||||
wait_until_curtain_gone
|
||||
|
||||
# presence of this means websocket gateway is not working
|
||||
page.should have_no_selector('.no-websocket-connection') if validate
|
||||
end
|
||||
|
||||
|
||||
|
|
@ -411,8 +417,8 @@ def wait_for_easydropdown(select)
|
|||
end
|
||||
|
||||
# defaults to enter key (13)
|
||||
def send_key(keycode = 13)
|
||||
keypress_script = "var e = $.Event('keydown', { keyCode: #{keycode} }); $('#search-input').trigger(e);"
|
||||
def send_key(selector, keycode = 13)
|
||||
keypress_script = "var e = $.Event('keyup', { keyCode: #{keycode} }); jQuery('#{selector}').trigger(e);"
|
||||
page.driver.execute_script(keypress_script)
|
||||
|
||||
end
|
||||
|
|
@ -35,7 +35,7 @@ gem 'aasm', '3.0.16'
|
|||
gem 'carrierwave'
|
||||
gem 'devise'
|
||||
gem 'postgres-copy'
|
||||
gem 'aws-sdk', '1.29.1'
|
||||
gem 'aws-sdk' #, '1.29.1'
|
||||
gem 'bugsnag'
|
||||
gem 'postgres_ext'
|
||||
gem 'resque'
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@ module JamWebsockets
|
|||
end
|
||||
|
||||
def run(options={})
|
||||
|
||||
host = "0.0.0.0"
|
||||
port = options[:port]
|
||||
connect_time_stale = options[:connect_time_stale].to_i
|
||||
connect_time_expire = options[:connect_time_expire].to_i
|
||||
rabbitmq_host = options[:rabbitmq_host]
|
||||
rabbitmq_port = options[:rabbitmq_port].to_i
|
||||
calling_thread = options[:calling_thread]
|
||||
|
||||
@log.info "starting server #{host}:#{port} staleness_time=#{connect_time_stale}; reconnect time = #{connect_time_expire}, rabbitmq=#{rabbitmq_host}:#{rabbitmq_port}"
|
||||
|
||||
|
|
@ -36,6 +36,7 @@ module JamWebsockets
|
|||
start_connection_expiration(expire_time)
|
||||
start_connection_flagger(connect_time_stale)
|
||||
start_websocket_listener(host, port, options[:emwebsocket_debug])
|
||||
calling_thread.wakeup if calling_thread
|
||||
end
|
||||
|
||||
# if you don't do this, the app won't exit unless you kill -9
|
||||
|
|
@ -55,6 +56,7 @@ module JamWebsockets
|
|||
@log.info "new client #{ws}"
|
||||
@router.new_client(ws)
|
||||
end
|
||||
@log.debug("started websocket")
|
||||
end
|
||||
|
||||
def start_connection_expiration(stale_max_time)
|
||||
|
|
|
|||
Loading…
Reference in New Issue