* VRFS-594; add dialog to initiate reconnect if you lose webocket connection

This commit is contained in:
Seth Call 2013-08-31 13:54:11 +00:00
parent 742c3276b8
commit dad7ac6691
14 changed files with 327 additions and 57 deletions

View File

@ -95,7 +95,8 @@
context.jamClient.connected = false;
}
// TODO: reconnect
context.JK.CurrentSessionModel.onWebsocketDisconnected();
};
server.send = function(message) {

View File

@ -0,0 +1,56 @@
(function(context,$) {
"use strict";
context.JK = context.JK || {};
context.JK.Banner = (function() {
var self = this;
var logger = context.JK.logger;
// responsible for updating the contents of the update dialog
// as well as registering for any event handlers
function show(options) {
var text = options.text;
var html = options.html;
var newContent = null;
if (html) {
newContent = $('#banner .dialog-inner').html(html);
}
else if(text) {
newContent = $('#banner .dialog-inner').html(text);
}
else {
console.error("unable to show banner for empty message")
return newContent;
}
$('#banner').show()
$('#banner_overlay').show()
// return the core of the banner so that caller can attach event handlers to newly created HTML
return newContent;
}
function hide() {
$('#banner').hide();
$('#banner_overlay .dialog-inner').html("");
$('#banner_overlay').hide();
}
function initialize() {
return self;
}
// Expose publics
var me = {
initialize: initialize,
show : show,
hide : hide
}
return me;
})();
})(window,jQuery);

View File

@ -270,7 +270,29 @@
ftueSave(false);
}
function videoLinkClicked(evt) {
var myOS = jamClient.GetOSAsString();
var link;
if (myOS === 'MacOSX') {
link = $(evt.currentTarget).attr('external-link-mac');
} else if (myOS === 'Win32') {
link = $(evt.currentTarget).attr('external-link-win');
}
if (link) {
context.jamClient.OpenSystemBrowser(link);
}
}
function events() {
$('.ftue-video-link').hover(
function(evt) { // handlerIn
$(this).addClass('hover');
},
function(evt) { // handlerOut
$(this).removeClass('hover');
}
);
$('.ftue-video-link').on('click', videoLinkClicked);
$('[layout-wizard-step="2"] .settings-driver select').on('change', audioDriverChanged);
$('[layout-wizard-step="2"] .settings-controls select').on('change', audioDeviceChanged);
$('#btn-asio-control-panel').on('click', openASIOControlPanel);

View File

@ -144,6 +144,15 @@
});
}
/** check if the server is alive */
function serverHealthCheck(options) {
return $.ajax({
url: window.host,
cache: false,
dataType: "html"
});
}
function getId(options) {
var id = options && options["id"]
@ -201,6 +210,7 @@
this.getClientDownloads = getClientDownloads
this.createInvitation = createInvitation;
this.postFeedback = postFeedback;
this.serverHealthCheck = serverHealthCheck;
return this;
};

View File

@ -121,22 +121,37 @@
}
function afterCurrentUserLoaded() {
sessionModel = new context.JK.SessionModel(
// It seems the SessionModel should be a singleton.
// a client can only be in one session at a time,
// and other parts of the code want to know at any certain times
// about the current session, if any (for example, reconnect logic)
context.JK.CurrentSessionModel = sessionModel = new context.JK.SessionModel(
context.JK.JamServer,
context.jamClient,
context.JK.userMe
context.jamClient
);
sessionModel.subscribe('sessionScreen', sessionChanged);
sessionModel.joinSession(sessionId);
sessionModel.joinSession(sessionId)
.fail(function(xhr, textStatus, errorMessage) {
if(xhr.status == 404) {
// we tried to join the session, but it's already gone. kick user back to join session screen
window.location = "#/findSession"
app.notify(
{ title: "Unable to Join Session",
text: "The session you attempted to join is over."
},
{ no_cancel: true });
}else {
app.ajaxError(xhr, textStatus, errorMessage);
}
})
}
function beforeHide(data) {
// track that the screen is inactive, to disable body-level handlers
screenActive = false;
sessionModel.leaveCurrentSession(sessionId);
// 'unregister' for callbacks
context.jamClient.SessionRegisterCallback("");
context.jamClient.SessionSetAlertCallback("");
sessionModel.leaveCurrentSession()
.fail(app.ajaxError)
}
function sessionChanged() {

View File

@ -7,12 +7,13 @@
context.JK = context.JK || {};
var logger = context.JK.logger;
context.JK.SessionModel = function(server, client, currentUser) {
context.JK.SessionModel = function(server, client) {
var clientId = client.clientID;
var currentSessionId = null; // Set on join, prior to setting currentSession.
var currentSession = null;
var subscribers = {};
var users = {}; // User info for session participants
var rest = context.JK.Rest();
function id() {
return currentSession.id;
@ -32,24 +33,54 @@
function joinSession(sessionId) {
currentSessionId = sessionId;
logger.debug("SessionModel.joinSession(" + sessionId + ")");
joinSessionRest(sessionId, function() {
refreshCurrentSession();
});
server.registerMessageCallback(context.JK.MessageType.MUSICIAN_SESSION_JOIN, refreshCurrentSession);
server.registerMessageCallback(context.JK.MessageType.MUSICIAN_SESSION_DEPART, refreshCurrentSession);
var deferred = joinSessionRest(sessionId);
deferred
.done(function(){
logger.debug("calling jamClient.JoinSession");
client.JoinSession({ sessionID: sessionId });
refreshCurrentSession();
server.registerMessageCallback(context.JK.MessageType.MUSICIAN_SESSION_JOIN, refreshCurrentSession);
server.registerMessageCallback(context.JK.MessageType.MUSICIAN_SESSION_DEPART, refreshCurrentSession);
});
return deferred;
}
/**
* Leave the current session
* Leave the current session, if there is one.
* callback: called in all conditions; either after an attempt is made to tell the server that we are leaving,
* or immediately if there is no session
*/
function leaveCurrentSession() {
logger.debug("SessionModel.leaveCurrentSession()");
// TODO - sessionChanged will be called with currentSession = null
server.unregisterMessageCallback(context.JK.MessageType.MUSICIAN_SESSION_JOIN, refreshCurrentSession);
server.unregisterMessageCallback(context.JK.MessageType.MUSICIAN_SESSION_DEPART, refreshCurrentSession);
leaveSessionRest(currentSessionId, sessionChanged);
currentSession = null;
currentSessionId = null;
var deferred;
if(currentSessionId) {
logger.debug("SessionModel.leaveCurrentSession()");
// TODO - sessionChanged will be called with currentSession = null
server.unregisterMessageCallback(context.JK.MessageType.MUSICIAN_SESSION_JOIN, refreshCurrentSession);
server.unregisterMessageCallback(context.JK.MessageType.MUSICIAN_SESSION_DEPART, refreshCurrentSession);
// leave the session right away without waiting on REST. Why? If you can't contact the server, or if it takes a long
// time, for that entire duration you'll still be sending voice data to the other users.
// this may be bad if someone decides to badmouth others in the left-session during this time
logger.debug("calling jamClient.LeaveSession for clientId=" + clientId);
client.LeaveSession({ sessionID: currentSessionId });
deferred = leaveSessionRest(currentSessionId);
deferred.done(function() {
sessionChanged();
});
// 'unregister' for callbacks
context.jamClient.SessionRegisterCallback("");
context.jamClient.SessionSetAlertCallback("");
currentSession = null;
currentSessionId = null;
}
else {
deferred = rest.serverHealthCheck();
}
return deferred;
}
/**
@ -268,7 +299,7 @@
* Make the server calls to join the current user to
* the session provided.
*/
function joinSessionRest(sessionId, callback) {
function joinSessionRest(sessionId) {
var tracks = context.JK.TrackHelpers.getUserTracks(context.jamClient);
var data = {
client_id: clientId,
@ -277,38 +308,77 @@
tracks: tracks
};
var url = "/api/sessions/" + sessionId + "/participants";
$.ajax({
return $.ajax({
type: "POST",
dataType: "json",
contentType: 'application/json',
url: url,
async: false,
data: JSON.stringify(data),
processData:false,
success: function(response) {
logger.debug("calling jamClient.JoinSession");
client.JoinSession({ sessionID: sessionId });
callback();
},
error: ajaxError
processData:false
});
}
function leaveSessionRest(sessionId, callback) {
function leaveSessionRest(sessionId) {
var url = "/api/participants/" + clientId;
$.ajax({
return $.ajax({
type: "DELETE",
url: url,
async: false,
success: function (response) {
logger.debug("calling jamClient.LeaveSession for clientId=" + clientId);
client.LeaveSession({ sessionID: sessionId });
callback();
},
error: ajaxError
async: false
});
}
function reconnect() {
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
});
context.JK.CurrentSessionModel.leaveCurrentSession()
.done(function() {
reconnect();
})
.fail(function(xhr, textStatus, errorThrown) {
console.log("leaveCurrentSession failed: ", arguments);
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);
}
});
});
}
function onWebsocketDisconnected() {
var template = $('#template-disconnected').html();
var templateHtml = context.JK.fillTemplate(template, null);
var content = context.JK.Banner.show({
html : template
}) ;
// kill the streaming of the session immediately
logger.debug("calling jamClient.LeaveSession for clientId=" + clientId);
client.LeaveSession({ sessionID: currentSessionId });
registerReconnect(content);
}
function ajaxError(jqXHR, textStatus, errorMessage) {
logger.error("Unexpected ajax error: " + textStatus);
}
@ -324,6 +394,7 @@
this.addTrack = addTrack;
this.updateTrack = updateTrack;
this.deleteTrack = deleteTrack;
this.onWebsocketDisconnected = onWebsocketDisconnected;
this.getCurrentSession = function() {
return currentSession;
};

View File

@ -0,0 +1,8 @@
#banner {
display:none;
}
#banner h2 {
font-weight:bold;
font-size:x-large;
}

View File

@ -30,6 +30,7 @@
*= require ./genreSelector
*= require ./sessionList
*= require ./searchResults
*= require ./banner
*= require ./clientUpdate
*= require jquery.Jcrop
*/

View File

@ -18,12 +18,16 @@ div.dialog.ftue .ftue-inner div[layout-wizard-step="1"] {
li {
text-align:center;
width: 33%;
width: 31%;
height: 170px;
margin:0px;
padding:0px;
/*padding:0px;*/
list-style: none;
float:left;
}
li.first {
width: 34%;
}
}
div.dialog.ftue .ftue-inner div[layout-wizard-step="3"] {
h5 {
@ -421,3 +425,14 @@ table.audiogeartable {
font-size:15px;
color:#aaa;
}
.ftue-video-link {
padding:4px;
cursor:pointer;
background-color:#333;
border: 1px solid #333;
}
.ftue-video-link.hover {
background-color:#444;
border: 1px solid #555;
}

View File

@ -0,0 +1,18 @@
<!-- generic banner for use by an code -->
<div class="overlay" id="banner_overlay"></div>
<div id="banner" class="dialog-overlay-sm">
<!-- dialog header -->
<div class="content-head">
<%= image_tag("content/icon_alert.png", :height => '24', :width => '24', :class => "content-icon") %><h1>alert</h1>
</div>
<div class="dialog-inner">
<!-- contents are replaced by caller to f-->
</div>
<!-- end right column -->
<br clear="all">
</div>

View File

@ -10,35 +10,37 @@
<!-- First screen of the FTUE wizard -->
<div layout-wizard-step="1" dialog-title="welcome!" style="display:block;">
<p class="intro">
Please identify which of the three types of audio gear below you are going to use with the JamKazam
service, and click either the Windows or Mac link under the appropriate gear to watch a video on how
to navigate this initial setup and testing process. After watching the video, click the 'NEXT'
button to get started.
Please identify which of the three types of audio gear below you
are going to use with the JamKazam service, and click one to
watch a video on how to navigate this initial setup and testing
process. After watching the video, click the 'NEXT' button to
get started. If you don't have your audio gear handy now, click
Cancel.
</p>
<ul class="device_type">
<li>
<li class="ftue-video-link first"
external-link-win="http://www.youtube.com/watch?v=hCoQAoEM5P8"
external-link-mac="http://www.youtube.com/watch?v=TRzb7OTlO-Q">
AUDIO DEVICE WITH PORTS FOR INSTRUMENT OR MIC INPUT JACKS<br/>
<p><%= image_tag "content/audio_capture_ftue.png", {:width => 243, :height => 70} %></p>
<p><a href="http://www.youtube.com/watch?v=jM5_MWtlxxE" rel="external">Windows Video</a><br/>
<a href="http://www.youtube.com/watch?v=TRzb7OTlO-Q" rel="external">Mac Video</a></p>
</li>
<li>
<li class="ftue-video-link"
external-link-win="http://www.youtube.com/watch?v=IDrLa8TOXwQ"
external-link-mac="http://www.youtube.com/watch?v=vIs7ArrjMpE">
USB MICROPHONE<br/>
<p><%= image_tag "content/microphone_ftue.png", {:width => 70, :height => 113} %></p>
<p><a href="http://www.youtube.com/watch?v=IDrLa8TOXwQ" rel="external">Windows Video</a><br/>
<a href="http://www.youtube.com/watch?v=vIs7ArrjMpE" rel="external">Mac Video</a></p>
</li>
<li>
<li class="ftue-video-link"
external-link-win="http://www.youtube.com/watch?v=PCri4Xed4CA"
external-link-mac="http://www.youtube.com/watch?v=Gatmd_ja47U">
COMPUTER'S BUILT-IN MIC &amp; SPEAKERS/HEADPHONES<br/>
<p><%= image_tag "content/computer_ftue.png", {:width => 118, :height => 105} %></p>
<p><a href="http://www.youtube.com/watch?v=PCri4Xed4CA" rel="external">Windows Video</a><br/>
<a href="http://www.youtube.com/watch?v=Gatmd_ja47U" rel="external">Mac Video</a></p>
</li>
</ul>
<div class="right mr30 buttonbar">
<!-- <a class="button-grey" layout-action="close">REGISTER AS A FAN</a>&nbsp; -->
<a class="button-grey" layout-action="close">CANCEL</a>&nbsp;
<a class="button-orange" layout-wizard-link="2">NEXT</a>
</div>

View File

@ -0,0 +1,42 @@
<script type="text/template" id="template-disconnected">
<h2 align="center">Disconnected from Server</h2>
<br />
<br />
<br />
<div align="center">
You have been disconnected from JamKazam.
</div>
<br clear="all" /><br />
<div class="right">
<a href="#" class="button-orange disconnected-reconnect">RECONNECT</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>

View File

@ -29,6 +29,8 @@
<%= render "account_audio_profile" %>
<%= render "notify" %>
<%= render "client_update" %>
<%= render "banner" %>
<%= render "clients/banners/disconnected" %>
<%= render "overlay_small" %>
<script type="text/javascript">
@ -82,6 +84,8 @@
var clientUpdate = new JK.ClientUpdate()
clientUpdate.initialize().check()
// Some things can't be initialized until we're connected. Put them here.
function _initAfterConnect() {
@ -118,6 +122,7 @@
// This is a helper class with a singleton. No need to instantiate.
JK.GenreSelectorHelper.initialize();
JK.Banner.initialize();
var createSessionScreen = new JK.CreateSessionScreen(JK.app);
createSessionScreen.initialize();
@ -155,6 +160,8 @@
JK.app = JK.JamKazam();
JK.app.initialize();
JK.JamServer.connect(); // singleton here defined in JamServer.js
// this ensures that there is always a CurrentSessionModel, even if it's for a non-active session
JK.CurrentSessionModel = new JK.SessionModel(JK.JamServer, window.jamClient);
// Run a check to see if we're logged in yet. Only after that should
// we initialize the other screens.

View File

@ -32,5 +32,7 @@
<div id="footer-container">
<%= render "clients/footer" %>
</div>
<!-- version info: <%= version %> -->
</body>
</html>