jam-cloud/app/assets/javascripts/sessionModel.js

340 lines
12 KiB
JavaScript

// The session model contains information about the music
// sessions that the current client has joined.
(function(context,$) {
"use strict";
context.JK = context.JK || {};
var logger = context.JK.logger;
context.JK.SessionModel = function(server, client, currentUser) {
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
function id() {
return currentSession.id;
}
function participants() {
if (currentSession) {
return currentSession.participants;
} else {
return [];
}
}
/**
* Join the session specified by the provided id.
*/
function joinSession(sessionId) {
currentSessionId = sessionId;
logger.debug("SessionModel.joinSession(" + sessionId + ")");
joinSessionRest(sessionId, function() {
refreshCurrentSession();
});
server.registerMessageCallback(
context.JK.MessageType.USER_JOINED_MUSIC_SESSION,
refreshCurrentSession);
server.registerMessageCallback(
context.JK.MessageType.USER_LEFT_MUSIC_SESSION,
refreshCurrentSession);
}
/**
* Leave the current session
*/
function leaveCurrentSession() {
logger.debug("SessionModel.leaveCurrentSession()");
// TODO - sessionChanged will be called with currentSession = null
server.unregisterMessageCallback(
context.JK.MessageType.USER_JOINED_MUSIC_SESSION,
refreshCurrentSession);
server.unregisterMessageCallback(
context.JK.MessageType.USER_LEFT_MUSIC_SESSION,
refreshCurrentSession);
leaveSessionRest(currentSessionId, sessionChanged);
currentSession = null;
currentSessionId = null;
}
/**
* Refresh the current session, and participants.
*/
function refreshCurrentSession() {
logger.debug("SessionModel.refreshCurrentSession()");
refreshCurrentSessionRest(function() {
refreshCurrentSessionParticipantsRest(sessionChanged);
});
}
/**
* Subscribe for sessionChanged events. Provide a subscriberId
* and a callback to be invoked on session changes.
*/
function subscribe(subscriberId, sessionChangedCallback) {
logger.debug("SessionModel.subscribe(" + subscriberId + ", [callback])");
subscribers[subscriberId] = sessionChangedCallback;
}
/**
* Notify subscribers that the current session has changed.
*/
function sessionChanged() {
logger.debug("SessionModel.sessionChanged()");
for (var subscriberId in subscribers) {
subscribers[subscriberId]();
}
}
/**
* Reload the session data from the REST server, calling
* the provided callback when complete.
*/
function refreshCurrentSessionRest(callback) {
var url = "/api/sessions/" + currentSessionId;
$.ajax({
type: "GET",
url: url,
success: function(response) {
sendClientParticipantChanges(currentSession, response);
currentSession = response;
callback();
},
error: ajaxError
});
}
/**
* Seems silly. We should just have the bridge take sessionId, clientId
*/
function _toJamClientParticipant(participant) {
return {
userID : "",
clientID : participant.client_id,
tcpPort : 0,
udpPort : 0,
localIPAddress : participant.ip_address, // ?
globalIPAddress : participant.ip_address, // ?
latency : 0,
natType : ""
};
}
function sendClientParticipantChanges(oldSession, newSession) {
var joins = [], leaves = []; // Will hold JamClientParticipants
var oldParticipants = []; // will be set to session.participants if session
var oldParticipantIds = [];
var newParticipants = [];
var newParticipantIds = [];
if (oldSession && oldSession.participants) {
oldParticipants = oldSession.participants;
$.each(oldParticipants, function() {
oldParticipantIds.push(this.client_id);
});
}
if (newSession && newSession.participants) {
newParticipants = newSession.participants;
$.each(newParticipants, function() {
newParticipantIds.push(this.client_id);
});
}
$.each(newParticipantIds, function(i,v) {
if ($.inArray(v, oldParticipantIds) === -1) {
// new participant id that's not in old participant ids: Join
joins.push(_toJamClientParticipant(newParticipants[i]));
}
});
$.each(oldParticipantIds, function(i,v) {
if ($.inArray(v, newParticipantIds) === -1) {
// old participant id that's not in new participant ids: Leave
leaves.push(_toJamClientParticipant(oldParticipants[i]));
}
});
$.each(joins, function(i,v) {
if (v.client_id != clientId) {
client.ParticipantJoined(newSession, v);
}
});
$.each(leaves, function(i,v) {
if (v.client_id != clientId) {
client.ParticipantLeft(newSession, v);
}
});
}
/**
* Ensure that we have user info for all current participants.
*/
function refreshCurrentSessionParticipantsRest(callback) {
var callCount = 0;
$.each(participants(), function(index, value) {
if (!(this.user.id in users)) {
var userInfoUrl = "/api/users/" + this.user.id;
callCount += 1;
$.ajax({
type: "GET",
url: userInfoUrl,
success: function(user) {
callCount -= 1;
users[user.id] = user;
},
error: function(jqXHR, textStatus, errorThrown) {
callCount -= 1;
logger.error('Error getting user info from ' + userInfoUrl);
}
});
}
});
if (!(callback)) {
return;
}
context.JK.joinCalls(
function() { return callCount === 0; }, callback, 10);
}
function participantForClientId(clientId) {
var foundParticipant = null;
$.each(currentSession.participants, function(index, participant) {
if (participant.client_id === clientId) {
foundParticipant = participant;
return false;
}
});
return foundParticipant;
}
function addTrack(sessionId, data) {
logger.debug("track data = " + JSON.stringify(data));
var url = "/api/sessions/" + sessionId + "/tracks";
$.ajax({
type: "POST",
dataType: "json",
contentType: 'application/json',
url: url,
data: JSON.stringify(data),
processData:false,
success: function(response) {
logger.debug("Successfully added track (" + JSON.stringify(data) + ")");
},
error: ajaxError
});
}
function updateTrack(sessionId, trackId, data) {
var url = "/api/sessions/" + sessionId + "/tracks/" + trackId;
$.ajax({
type: "POST",
dataType: "json",
contentType: 'application/json',
url: url,
data: JSON.stringify(data),
processData:false,
success: function(response) {
logger.debug("Successfully updated track info (" + JSON.stringify(data) + ")");
},
error: ajaxError
});
}
function deleteTrack(trackId) {
if (trackId) {
$.ajax({
type: "DELETE",
url: "/api/sessions/" + sessionId + "/tracks/" + trackId,
success: function(response) {
// TODO: if in recording, more cleanup to do???
// refresh Session screen
refreshCurrentSession();
},
error: function(jqXHR, textStatus, errorThrown) {
logger.error("Error deleting track " + trackId);
}
});
}
}
/**
* Make the server calls to join the current user to
* the session provided.
*/
function joinSessionRest(sessionId, callback) {
var tracks = getUserTracks();
var data = {
client_id: clientId,
ip_address: server.publicIP,
as_musician: true,
tracks: tracks
};
var url = "/api/sessions/" + sessionId + "/participants";
$.ajax({
type: "POST",
dataType: "json",
contentType: 'application/json',
url: url,
data: JSON.stringify(data),
processData:false,
success: function(response) {
logger.debug("calling jamClient.JoinSession");
client.JoinSession({ sessionID: sessionId });
callback();
},
error: ajaxError
});
}
function leaveSessionRest(sessionId, callback) {
var url = "/api/participants/" + clientId;
$.ajax({
type: "DELETE",
url: url,
success: function (response) {
logger.debug("calling jamClient.LeaveSession");
client.LeaveSession({ sessionID: sessionId });
callback();
},
error: ajaxError
});
}
function ajaxError(jqXHR, textStatus, errorMessage) {
logger.error("Unexpected ajax error: " + textStatus);
}
function getUserTracks() {
// FIXME. Setting tracks for join session. Only looking at profile
// for now. Needs to check jamClient instruments and if set, use those
// as first preference. Also need to support jamClient possibly having
// multiple tracks.
// TODO: Defaulting to electric guitar...
var track = { instrument_id: "electric guitar", sound: "stereo" };
if (currentUser.instruments && currentUser.instruments.length) {
track = {
instrument_id: currentUser.instruments[0].instrument_id,
sound: "stereo"
};
}
return [track];
}
// Public interface
this.id = id;
this.participants = participants;
this.joinSession = joinSession;
this.leaveCurrentSession = leaveCurrentSession;
this.refreshCurrentSession = refreshCurrentSession;
this.subscribe = subscribe;
this.participantForClientId = participantForClientId;
this.addTrack = addTrack;
this.updateTrack = updateTrack;
this.deleteTrack = deleteTrack;
};
})(window,jQuery);