зеркало из https://github.com/nextcloud/spreed.git
978 строки
26 KiB
JavaScript
978 строки
26 KiB
JavaScript
/** @global console */
|
|
(function(OCA, OC, $) {
|
|
'use strict';
|
|
|
|
OCA.Talk = OCA.Talk || {};
|
|
OCA.Talk.Signaling = {
|
|
Base: {},
|
|
Internal: {},
|
|
Standalone: {},
|
|
|
|
createConnection: function() {
|
|
var settings = $("#app #signaling-settings").text();
|
|
if (settings) {
|
|
settings = JSON.parse(settings);
|
|
} else {
|
|
settings = {};
|
|
}
|
|
var urls = settings.server;
|
|
if (urls && urls.length) {
|
|
return new OCA.Talk.Signaling.Standalone(settings, urls);
|
|
} else {
|
|
return new OCA.Talk.Signaling.Internal(settings);
|
|
}
|
|
}
|
|
};
|
|
|
|
function Base(settings) {
|
|
this.settings = settings;
|
|
this.sessionId = '';
|
|
this.currentRoomToken = null;
|
|
this.currentCallToken = null;
|
|
this.handlers = {};
|
|
this.features = {};
|
|
}
|
|
|
|
OCA.Talk.Signaling.Base = Base;
|
|
OCA.Talk.Signaling.Base.prototype.on = function(ev, handler) {
|
|
if (!this.handlers.hasOwnProperty(ev)) {
|
|
this.handlers[ev] = [handler];
|
|
} else {
|
|
this.handlers[ev].push(handler);
|
|
}
|
|
|
|
switch (ev) {
|
|
case 'stunservers':
|
|
case 'turnservers':
|
|
var servers = this.settings[ev] || [];
|
|
if (servers.length) {
|
|
// The caller expects the handler to be called when the data
|
|
// is available, so defer to simulate a delayed response.
|
|
_.defer(function() {
|
|
handler(servers);
|
|
});
|
|
}
|
|
break;
|
|
}
|
|
};
|
|
|
|
OCA.Talk.Signaling.Base.prototype._trigger = function(ev, args) {
|
|
var handlers = this.handlers[ev];
|
|
if (!handlers) {
|
|
return;
|
|
}
|
|
|
|
handlers = handlers.slice(0);
|
|
for (var i = 0, len = handlers.length; i < len; i++) {
|
|
var handler = handlers[i];
|
|
handler.apply(handler, args);
|
|
}
|
|
};
|
|
|
|
OCA.Talk.Signaling.Base.prototype.getSessionid = function() {
|
|
return this.sessionId;
|
|
};
|
|
|
|
OCA.Talk.Signaling.Base.prototype.disconnect = function() {
|
|
this.sessionId = '';
|
|
this.currentCallToken = null;
|
|
};
|
|
|
|
OCA.Talk.Signaling.Base.prototype.hasFeature = function(feature) {
|
|
return this.features && this.features[feature];
|
|
};
|
|
|
|
OCA.Talk.Signaling.Base.prototype.emit = function(ev, data) {
|
|
switch (ev) {
|
|
case 'joinRoom':
|
|
this.joinRoom(data);
|
|
break;
|
|
case 'joinCall':
|
|
this.joinCall(data, arguments[2]);
|
|
break;
|
|
case 'leaveRoom':
|
|
this.leaveCurrentRoom();
|
|
break;
|
|
case 'leaveCall':
|
|
this.leaveCurrentCall();
|
|
break;
|
|
case 'message':
|
|
this.sendCallMessage(data);
|
|
break;
|
|
}
|
|
};
|
|
|
|
OCA.Talk.Signaling.Base.prototype.leaveCurrentRoom = function() {
|
|
if (this.currentRoomToken) {
|
|
this.leaveRoom(this.currentRoomToken);
|
|
this.currentRoomToken = null;
|
|
}
|
|
};
|
|
|
|
OCA.Talk.Signaling.Base.prototype.leaveCurrentCall = function() {
|
|
if (this.currentCallToken) {
|
|
this.leaveCall(this.currentCallToken);
|
|
this.currentCallToken = null;
|
|
}
|
|
};
|
|
|
|
OCA.Talk.Signaling.Base.prototype.leaveAllCalls = function() {
|
|
// Override if necessary.
|
|
};
|
|
|
|
OCA.Talk.Signaling.Base.prototype.setRoomCollection = function(rooms) {
|
|
this.roomCollection = rooms;
|
|
return this.syncRooms();
|
|
};
|
|
|
|
/**
|
|
* Sets a single room to be synced.
|
|
*
|
|
* If there is a RoomCollection set the synchronization will be performed on
|
|
* the RoomCollection instead and the given room will be ignored; setting a
|
|
* single room is intended to be used only on public pages.
|
|
*
|
|
* @param OCA.SpreedMe.Models.Room room the room to sync.
|
|
*/
|
|
OCA.Talk.Signaling.Base.prototype.setRoom = function(room) {
|
|
this.room = room;
|
|
return this.syncRooms();
|
|
};
|
|
|
|
OCA.Talk.Signaling.Base.prototype.syncRooms = function() {
|
|
var defer = $.Deferred();
|
|
if (this.roomCollection && OC.getCurrentUser().uid) {
|
|
this.roomCollection.fetch({
|
|
success: function(data) {
|
|
defer.resolve(data);
|
|
}
|
|
});
|
|
} else if (this.room) {
|
|
this.room.fetch({
|
|
success: function(data) {
|
|
defer.resolve(data);
|
|
}
|
|
});
|
|
} else {
|
|
defer.resolve([]);
|
|
}
|
|
return defer;
|
|
};
|
|
|
|
OCA.Talk.Signaling.Base.prototype.joinRoom = function(token, password) {
|
|
$.ajax({
|
|
url: OC.linkToOCS('apps/spreed/api/v1/room', 2) + token + '/participants/active',
|
|
type: 'POST',
|
|
beforeSend: function (request) {
|
|
request.setRequestHeader('Accept', 'application/json');
|
|
},
|
|
data: {
|
|
password: password
|
|
},
|
|
success: function (result) {
|
|
console.log("Joined", result);
|
|
this.currentRoomToken = token;
|
|
this._trigger('joinRoom', [token]);
|
|
this._joinRoomSuccess(token, result.ocs.data.sessionId);
|
|
}.bind(this),
|
|
error: function (result) {
|
|
if (result.status === 404 || result.status === 503) {
|
|
// Room not found or maintenance mode
|
|
OC.redirect(OC.generateUrl('apps/spreed'));
|
|
}
|
|
|
|
if (result.status === 403) {
|
|
// This should not happen anymore since we ask for the password before
|
|
// even trying to join the call, but let's keep it for now.
|
|
OC.dialogs.prompt(
|
|
t('spreed', 'Please enter the password for this call'),
|
|
t('spreed','Password required'),
|
|
function (result, password) {
|
|
if (result && password !== '') {
|
|
this.joinRoom(token, password);
|
|
}
|
|
}.bind(this),
|
|
true,
|
|
t('spreed','Password'),
|
|
true
|
|
).then(function() {
|
|
var $dialog = $('.oc-dialog:visible');
|
|
$dialog.find('.ui-icon').remove();
|
|
|
|
var $buttons = $dialog.find('button');
|
|
$buttons.eq(0).text(t('core', 'Cancel'));
|
|
$buttons.eq(1).text(t('core', 'Submit'));
|
|
});
|
|
}
|
|
}.bind(this)
|
|
});
|
|
};
|
|
|
|
OCA.Talk.Signaling.Base.prototype._leaveRoomSuccess = function(/* token */) {
|
|
// Override in subclasses if necessary.
|
|
};
|
|
|
|
OCA.Talk.Signaling.Base.prototype.leaveRoom = function(token) {
|
|
this.leaveCurrentCall();
|
|
|
|
this._trigger('leaveRoom', [token]);
|
|
this._doLeaveRoom(token);
|
|
|
|
$.ajax({
|
|
url: OC.linkToOCS('apps/spreed/api/v1/room', 2) + token + '/participants/active',
|
|
method: 'DELETE',
|
|
async: false,
|
|
success: function () {
|
|
this._leaveRoomSuccess(token);
|
|
// We left the current room.
|
|
if (token === this.currentRoomToken) {
|
|
this.currentRoomToken = null;
|
|
}
|
|
}.bind(this)
|
|
});
|
|
};
|
|
|
|
OCA.Talk.Signaling.Base.prototype._joinCallSuccess = function(/* token */) {
|
|
// Override in subclasses if necessary.
|
|
};
|
|
|
|
OCA.Talk.Signaling.Base.prototype.joinCall = function(token) {
|
|
$.ajax({
|
|
url: OC.linkToOCS('apps/spreed/api/v1/call', 2) + token,
|
|
type: 'POST',
|
|
beforeSend: function (request) {
|
|
request.setRequestHeader('Accept', 'application/json');
|
|
},
|
|
success: function () {
|
|
this.currentCallToken = token;
|
|
this._trigger('joinCall', [token]);
|
|
this._joinCallSuccess(token);
|
|
}.bind(this),
|
|
error: function () {
|
|
// Room not found or maintenance mode
|
|
OC.redirect(OC.generateUrl('apps/spreed'));
|
|
}.bind(this)
|
|
});
|
|
};
|
|
|
|
OCA.Talk.Signaling.Base.prototype._leaveCallSuccess = function(/* token */) {
|
|
// Override in subclasses if necessary.
|
|
};
|
|
|
|
OCA.Talk.Signaling.Base.prototype.leaveCall = function(token) {
|
|
|
|
if (!token) {
|
|
return;
|
|
}
|
|
|
|
$.ajax({
|
|
url: OC.linkToOCS('apps/spreed/api/v1/call', 2) + token,
|
|
method: 'DELETE',
|
|
async: false,
|
|
success: function () {
|
|
this._trigger('leaveCall', [token]);
|
|
this._leaveCallSuccess(token);
|
|
// We left the current call.
|
|
if (token === this.currentCallToken) {
|
|
this.currentCallToken = null;
|
|
}
|
|
}.bind(this)
|
|
});
|
|
};
|
|
|
|
// Connection to the internal signaling server provided by the app.
|
|
function Internal(/*settings*/) {
|
|
OCA.Talk.Signaling.Base.prototype.constructor.apply(this, arguments);
|
|
this.spreedArrayConnection = [];
|
|
|
|
this.pingFails = 0;
|
|
this.pingInterval = null;
|
|
this.isSendingMessages = false;
|
|
|
|
this.pullMessagesRequest = null;
|
|
|
|
this.sendInterval = window.setInterval(function(){
|
|
this.sendPendingMessages();
|
|
}.bind(this), 500);
|
|
}
|
|
|
|
Internal.prototype = new OCA.Talk.Signaling.Base();
|
|
Internal.prototype.constructor = Internal;
|
|
OCA.Talk.Signaling.Internal = Internal;
|
|
|
|
OCA.Talk.Signaling.Internal.prototype.disconnect = function() {
|
|
this.spreedArrayConnection = [];
|
|
if (this.source) {
|
|
this.source.close();
|
|
this.source = null;
|
|
}
|
|
if (this.sendInterval) {
|
|
window.clearInterval(this.sendInterval);
|
|
this.sendInterval = null;
|
|
}
|
|
if (this.pingInterval) {
|
|
window.clearInterval(this.pingInterval);
|
|
this.pingInterval = null;
|
|
}
|
|
if (this.roomPoller) {
|
|
window.clearInterval(this.roomPoller);
|
|
this.roomPoller = null;
|
|
}
|
|
OCA.Talk.Signaling.Base.prototype.disconnect.apply(this, arguments);
|
|
};
|
|
|
|
OCA.Talk.Signaling.Internal.prototype.on = function(ev/*, handler*/) {
|
|
OCA.Talk.Signaling.Base.prototype.on.apply(this, arguments);
|
|
|
|
switch (ev) {
|
|
case 'connect':
|
|
// A connection is established if we can perform a request
|
|
// through it.
|
|
this._sendMessageWithCallback(ev);
|
|
break;
|
|
}
|
|
};
|
|
|
|
OCA.Talk.Signaling.Internal.prototype._sendMessageWithCallback = function(ev) {
|
|
var message = [{
|
|
ev: ev
|
|
}];
|
|
|
|
this._sendMessages(message).done(function(result) {
|
|
this._trigger(ev, [result.ocs.data]);
|
|
}.bind(this)).fail(function(/*xhr, textStatus, errorThrown*/) {
|
|
console.log('Sending signaling message with callback has failed.');
|
|
// TODO: Add error handling
|
|
});
|
|
};
|
|
|
|
OCA.Talk.Signaling.Internal.prototype._sendMessages = function(messages) {
|
|
var defer = $.Deferred();
|
|
$.ajax({
|
|
url: OC.linkToOCS('apps/spreed/api/v1', 2) + 'signaling',
|
|
type: 'POST',
|
|
data: {messages: JSON.stringify(messages)},
|
|
beforeSend: function (request) {
|
|
request.setRequestHeader('Accept', 'application/json');
|
|
},
|
|
success: function (result) {
|
|
defer.resolve(result);
|
|
},
|
|
error: function (xhr, textStatus, errorThrown) {
|
|
defer.reject(xhr, textStatus, errorThrown);
|
|
}
|
|
});
|
|
return defer;
|
|
};
|
|
|
|
OCA.Talk.Signaling.Internal.prototype._joinRoomSuccess = function(token, sessionId) {
|
|
this.sessionId = sessionId;
|
|
this._startPingCall();
|
|
this._startPullingMessages();
|
|
};
|
|
|
|
OCA.Talk.Signaling.Internal.prototype._doLeaveRoom = function(token) {
|
|
if (!token) {
|
|
return;
|
|
}
|
|
|
|
if (token === this.currentRoomToken) {
|
|
this._stopPingCall();
|
|
this._closeEventSource();
|
|
}
|
|
};
|
|
|
|
OCA.Talk.Signaling.Internal.prototype.sendCallMessage = function(data) {
|
|
if(data.type === 'answer') {
|
|
console.log("ANSWER", data);
|
|
} else if(data.type === 'offer') {
|
|
console.log("OFFER", data);
|
|
}
|
|
this.spreedArrayConnection.push({
|
|
ev: "message",
|
|
fn: JSON.stringify(data),
|
|
sessionId: this.sessionId
|
|
});
|
|
};
|
|
|
|
OCA.Talk.Signaling.Internal.prototype.setRoomCollection = function(/*rooms*/) {
|
|
this._pollForRoomChanges();
|
|
return OCA.Talk.Signaling.Base.prototype.setRoomCollection.apply(this, arguments);
|
|
};
|
|
|
|
OCA.Talk.Signaling.Internal.prototype.setRoom = function(/*room*/) {
|
|
this._pollForRoomChanges();
|
|
return OCA.Talk.Signaling.Base.prototype.setRoom.apply(this, arguments);
|
|
};
|
|
|
|
OCA.Talk.Signaling.Internal.prototype._pollForRoomChanges = function() {
|
|
if (this.roomPoller) {
|
|
window.clearInterval(this.roomPoller);
|
|
}
|
|
this.roomPoller = window.setInterval(function() {
|
|
this.syncRooms();
|
|
}.bind(this), 10000);
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
OCA.Talk.Signaling.Internal.prototype._getCallPeers = function(token) {
|
|
var defer = $.Deferred();
|
|
$.ajax({
|
|
beforeSend: function (request) {
|
|
request.setRequestHeader('Accept', 'application/json');
|
|
},
|
|
url: OC.linkToOCS('apps/spreed/api/v1/call', 2) + token,
|
|
success: function (result) {
|
|
var peers = result.ocs.data;
|
|
defer.resolve(peers);
|
|
}
|
|
});
|
|
return defer;
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
OCA.Talk.Signaling.Internal.prototype._startPullingMessages = function() {
|
|
// Abort ongoing request
|
|
if (this.pullMessagesRequest !== null) {
|
|
this.pullMessagesRequest.abort();
|
|
}
|
|
|
|
// Connect to the messages endpoint and pull for new messages
|
|
this.pullMessagesRequest =
|
|
$.ajax({
|
|
url: OC.linkToOCS('apps/spreed/api/v1', 2) + 'signaling',
|
|
type: 'GET',
|
|
dataType: 'json',
|
|
beforeSend: function (request) {
|
|
request.setRequestHeader('Accept', 'application/json');
|
|
},
|
|
success: function (result) {
|
|
$.each(result.ocs.data, function(id, message) {
|
|
switch(message.type) {
|
|
case "usersInRoom":
|
|
this._trigger('usersInRoom', [message.data]);
|
|
break;
|
|
case "message":
|
|
if (typeof(message.data) === 'string') {
|
|
message.data = JSON.parse(message.data);
|
|
}
|
|
this._trigger('message', [message.data]);
|
|
break;
|
|
default:
|
|
console.log('Unknown Signaling Message');
|
|
break;
|
|
}
|
|
}.bind(this));
|
|
this._startPullingMessages();
|
|
}.bind(this),
|
|
error: function (jqXHR, textStatus/*, errorThrown*/) {
|
|
if (jqXHR.status === 0 && textStatus === 'abort') {
|
|
// Resquest has been aborted. Ignore.
|
|
} else {
|
|
//Retry to pull messages after 5 seconds
|
|
window.setTimeout(function() {
|
|
this._startPullingMessages();
|
|
}.bind(this), 5000);
|
|
}
|
|
}.bind(this)
|
|
});
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
OCA.Talk.Signaling.Internal.prototype._closeEventSource = function() {
|
|
if (this.source) {
|
|
this.source.close();
|
|
this.source = null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
OCA.Talk.Signaling.Internal.prototype.sendPendingMessages = function() {
|
|
if (!this.spreedArrayConnection.length || this.isSendingMessages) {
|
|
return;
|
|
}
|
|
|
|
var pendingMessagesLength = this.spreedArrayConnection.length;
|
|
this.isSendingMessages = true;
|
|
|
|
this._sendMessages(this.spreedArrayConnection).done(function(/*result*/) {
|
|
this.spreedArrayConnection.splice(0, pendingMessagesLength);
|
|
this.isSendingMessages = false;
|
|
}.bind(this)).fail(function(/*xhr, textStatus, errorThrown*/) {
|
|
console.log('Sending pending signaling messages has failed.');
|
|
this.isSendingMessages = false;
|
|
}.bind(this));
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
OCA.Talk.Signaling.Internal.prototype._startPingCall = function() {
|
|
this._pingCall();
|
|
|
|
// Send a ping to the server all 5 seconds to ensure that the connection
|
|
// is still alive.
|
|
this.pingInterval = window.setInterval(function() {
|
|
this._pingCall();
|
|
}.bind(this), 5000);
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
OCA.Talk.Signaling.Internal.prototype._stopPingCall = function() {
|
|
if (this.pingInterval) {
|
|
window.clearInterval(this.pingInterval);
|
|
this.pingInterval = null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
OCA.Talk.Signaling.Internal.prototype._pingCall = function() {
|
|
if (!this.currentRoomToken) {
|
|
return;
|
|
}
|
|
|
|
$.ajax({
|
|
url: OC.linkToOCS('apps/spreed/api/v1/call', 2) + this.currentRoomToken + '/ping',
|
|
method: 'POST'
|
|
}).done(function() {
|
|
this.pingFails = 0;
|
|
}.bind(this)).fail(function(xhr) {
|
|
// If there is an error when pinging, retry for 3 times.
|
|
if (xhr.status !== 404 && this.pingFails < 3) {
|
|
this.pingFails++;
|
|
return;
|
|
}
|
|
// FIXME this sounds wrong…
|
|
this.leaveCurrentCall(false);
|
|
}.bind(this));
|
|
};
|
|
|
|
function Standalone(settings, urls) {
|
|
OCA.Talk.Signaling.Base.prototype.constructor.apply(this, arguments);
|
|
if (typeof(urls) === "string") {
|
|
urls = [urls];
|
|
}
|
|
// We can connect to any of the servers.
|
|
var idx = Math.floor(Math.random() * urls.length);
|
|
// TODO(jojo): Try other server if connection fails.
|
|
var url = urls[idx];
|
|
// Make sure we are using websocket urls.
|
|
if (url.indexOf("https://") === 0) {
|
|
url = "wss://" + url.substr(8);
|
|
} else if (url.indexOf("http://") === 0) {
|
|
url = "ws://" + url.substr(7);
|
|
}
|
|
if (url[url.length - 1] === "/") {
|
|
url = url.substr(0, url.length - 1);
|
|
}
|
|
this.url = url + "/spreed";
|
|
this.initialReconnectIntervalMs = 1000;
|
|
this.maxReconnectIntervalMs = 16000;
|
|
this.reconnectIntervalMs = this.initialReconnectIntervalMs;
|
|
this.joinedUsers = {};
|
|
this.rooms = [];
|
|
this.connect();
|
|
}
|
|
|
|
Standalone.prototype = new OCA.Talk.Signaling.Base();
|
|
Standalone.prototype.constructor = Standalone;
|
|
OCA.Talk.Signaling.Standalone = Standalone;
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype.reconnect = function() {
|
|
if (this.reconnectTimer) {
|
|
return;
|
|
}
|
|
|
|
// Wiggle interval a little bit to prevent all clients from connecting
|
|
// simultaneously in case the server connection is interrupted.
|
|
var interval = this.reconnectIntervalMs - (this.reconnectIntervalMs / 2) + (this.reconnectIntervalMs * Math.random());
|
|
console.log("Reconnect in", interval);
|
|
this.reconnected = true;
|
|
this.reconnectTimer = window.setTimeout(function() {
|
|
this.reconnectTimer = null;
|
|
this.connect();
|
|
}.bind(this), interval);
|
|
this.reconnectIntervalMs = this.reconnectIntervalMs * 2;
|
|
if (this.reconnectIntervalMs > this.maxReconnectIntervalMs) {
|
|
this.reconnectIntervalMs = this.maxReconnectIntervalMs;
|
|
}
|
|
if (this.socket) {
|
|
this.socket.close();
|
|
this.socket = null;
|
|
}
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype.connect = function() {
|
|
console.log("Connecting to", this.url);
|
|
this.callbacks = {};
|
|
this.id = 1;
|
|
this.pendingMessages = [];
|
|
this.connected = false;
|
|
this.socket = new WebSocket(this.url);
|
|
window.signalingSocket = this.socket;
|
|
this.socket.onopen = function(event) {
|
|
console.log("Connected", event);
|
|
this.reconnectIntervalMs = this.initialReconnectIntervalMs;
|
|
this.sendHello();
|
|
}.bind(this);
|
|
this.socket.onerror = function(event) {
|
|
console.log("Error", event);
|
|
this.reconnect();
|
|
}.bind(this);
|
|
this.socket.onclose = function(event) {
|
|
console.log("Close", event);
|
|
this.reconnect();
|
|
}.bind(this);
|
|
this.socket.onmessage = function(event) {
|
|
var data = event.data;
|
|
if (typeof(data) === "string") {
|
|
data = JSON.parse(data);
|
|
}
|
|
console.log("Received", data);
|
|
var id = data.id;
|
|
if (id && this.callbacks.hasOwnProperty(id)) {
|
|
var cb = this.callbacks[id];
|
|
delete this.callbacks[id];
|
|
cb(data);
|
|
}
|
|
switch (data.type) {
|
|
case "hello":
|
|
if (!id) {
|
|
// Only process if not received as result of our "hello".
|
|
this.helloResponseReceived(data);
|
|
}
|
|
break;
|
|
case "room":
|
|
if (this.currentRoomToken && data.room.roomid !== this.currentRoomToken) {
|
|
this._trigger('roomChanged', [this.currentRoomToken, data.room.roomid]);
|
|
this.joinedUsers = {};
|
|
this.currentRoomToken = null;
|
|
} else {
|
|
// TODO(fancycode): Only fetch properties of room that was modified.
|
|
this.internalSyncRooms();
|
|
}
|
|
break;
|
|
case "event":
|
|
this.processEvent(data);
|
|
break;
|
|
case "message":
|
|
data.message.data.from = data.message.sender.sessionid;
|
|
this._trigger("message", [data.message.data]);
|
|
break;
|
|
default:
|
|
if (!id) {
|
|
console.log("Ignore unknown event", data);
|
|
}
|
|
break;
|
|
}
|
|
}.bind(this);
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype.disconnect = function() {
|
|
if (this.socket) {
|
|
this.doSend({
|
|
"type": "bye",
|
|
"bye": {}
|
|
});
|
|
this.socket.close();
|
|
this.socket = null;
|
|
}
|
|
OCA.Talk.Signaling.Base.prototype.disconnect.apply(this, arguments);
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype.sendCallMessage = function(data) {
|
|
this.doSend({
|
|
"type": "message",
|
|
"message": {
|
|
"recipient": {
|
|
"type": "session",
|
|
"sessionid": data.to
|
|
},
|
|
"data": data
|
|
}
|
|
});
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype.doSend = function(msg, callback) {
|
|
if (!this.connected && msg.type !== "hello") {
|
|
// Defer sending any messages until the hello rsponse has been
|
|
// received.
|
|
this.pendingMessages.push([msg, callback]);
|
|
return;
|
|
}
|
|
|
|
if (callback) {
|
|
var id = this.id++;
|
|
this.callbacks[id] = callback;
|
|
msg["id"] = ""+id;
|
|
}
|
|
console.log("Sending", msg);
|
|
this.socket.send(JSON.stringify(msg));
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype.sendHello = function() {
|
|
var msg;
|
|
if (this.resumeId) {
|
|
console.log("Trying to resume session", this.sessionId);
|
|
msg = {
|
|
"type": "hello",
|
|
"hello": {
|
|
"version": "1.0",
|
|
"resumeid": this.resumeId
|
|
}
|
|
};
|
|
} else {
|
|
var user = OC.getCurrentUser();
|
|
var url = OC.linkToOCS('apps/spreed/api/v1/signaling', 2) + 'backend';
|
|
msg = {
|
|
"type": "hello",
|
|
"hello": {
|
|
"version": "1.0",
|
|
"auth": {
|
|
"url": url,
|
|
"params": {
|
|
"userid": user.uid,
|
|
"ticket": this.settings.ticket
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
this.doSend(msg, this.helloResponseReceived.bind(this));
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype.helloResponseReceived = function(data) {
|
|
console.log("Hello response received", data);
|
|
if (data.type !== "hello") {
|
|
if (this.resumeId) {
|
|
// Resuming the session failed, reconnect as new session.
|
|
this.resumeId = '';
|
|
this.sendHello();
|
|
return;
|
|
}
|
|
|
|
// TODO(fancycode): How should this be handled better?
|
|
console.error("Could not connect to server", data);
|
|
this.reconnect();
|
|
return;
|
|
}
|
|
|
|
var resumedSession = !!this.resumeId;
|
|
this.connected = true;
|
|
this.sessionId = data.hello.sessionid;
|
|
this.resumeId = data.hello.resumeid;
|
|
this.features = {};
|
|
var i;
|
|
if (data.hello.server && data.hello.server.features) {
|
|
var features = data.hello.server.features;
|
|
for (i = 0; i < features.length; i++) {
|
|
this.features[features[i]] = true;
|
|
}
|
|
}
|
|
|
|
var messages = this.pendingMessages;
|
|
this.pendingMessages = [];
|
|
for (i = 0; i < messages.length; i++) {
|
|
var msg = messages[i][0];
|
|
var callback = messages[i][1];
|
|
this.doSend(msg, callback);
|
|
}
|
|
|
|
this._trigger("connect");
|
|
if (this.reconnected) {
|
|
// The list of rooms might have changed while we were not connected,
|
|
// so perform resync once.
|
|
this.internalSyncRooms();
|
|
}
|
|
if (!resumedSession && this.currentRoomToken) {
|
|
this.joinRoom(this.currentRoomToken);
|
|
}
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype.setRoom = function(/* room */) {
|
|
OCA.Talk.Signaling.Base.prototype.setRoom.apply(this, arguments);
|
|
return this.internalSyncRooms();
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype._joinRoomSuccess = function(token, nextcloudSessionId) {
|
|
console.log("Join room", token);
|
|
this.doSend({
|
|
"type": "room",
|
|
"room": {
|
|
"roomid": token,
|
|
// Pass the Nextcloud session id to the signaling server. The
|
|
// session id will be passed through to Nextcloud to check if
|
|
// the (Nextcloud) user is allowed to join the room.
|
|
"sessionid": nextcloudSessionId,
|
|
}
|
|
}, function(data) {
|
|
this.joinResponseReceived(data, token);
|
|
}.bind(this));
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype._joinCallSuccess = function(/* token */) {
|
|
// Update room list to fetch modified properties.
|
|
this.internalSyncRooms();
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype._leaveCallSuccess = function(/* token */) {
|
|
// Update room list to fetch modified properties.
|
|
this.internalSyncRooms();
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype.joinResponseReceived = function(data, token) {
|
|
console.log("Joined", data, token);
|
|
if (this.roomCollection) {
|
|
// The list of rooms is not fetched from the server. Update ping
|
|
// of joined room so it gets sorted to the top.
|
|
this.roomCollection.forEach(function(room) {
|
|
if (room.get('token') === token) {
|
|
room.set('lastPing', (new Date()).getTime() / 1000);
|
|
}
|
|
});
|
|
this.roomCollection.sort();
|
|
}
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype._doLeaveRoom = function(token) {
|
|
console.log("Leave room", token);
|
|
this.doSend({
|
|
"type": "room",
|
|
"room": {
|
|
"roomid": ""
|
|
}
|
|
}, function(data) {
|
|
console.log("Left", data);
|
|
// Any users we previously had in the room also "left" for us.
|
|
var leftUsers = _.keys(this.joinedUsers);
|
|
if (leftUsers.length) {
|
|
this._trigger("usersLeft", [leftUsers]);
|
|
}
|
|
this.joinedUsers = {};
|
|
}.bind(this));
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype.processEvent = function(data) {
|
|
switch (data.event.target) {
|
|
case "room":
|
|
this.processRoomEvent(data);
|
|
break;
|
|
case "roomlist":
|
|
this.processRoomListEvent(data);
|
|
break;
|
|
case "participants":
|
|
this.processRoomParticipantsEvent(data);
|
|
break;
|
|
default:
|
|
console.log("Unsupported event target", data);
|
|
break;
|
|
}
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype.processRoomEvent = function(data) {
|
|
var i;
|
|
switch (data.event.type) {
|
|
case "join":
|
|
var joinedUsers = data.event.join || [];
|
|
if (joinedUsers.length) {
|
|
console.log("Users joined", joinedUsers);
|
|
var leftUsers = {};
|
|
if (this.reconnected) {
|
|
this.reconnected = false;
|
|
// The browser reconnected, some of the previous sessions
|
|
// may now no longer exist.
|
|
leftUsers = _.extend({}, this.joinedUsers);
|
|
}
|
|
for (i = 0; i < joinedUsers.length; i++) {
|
|
this.joinedUsers[joinedUsers[i].sessionid] = true;
|
|
delete leftUsers[joinedUsers[i].sessionid];
|
|
}
|
|
leftUsers = _.keys(leftUsers);
|
|
if (leftUsers.length) {
|
|
this._trigger("usersLeft", [leftUsers]);
|
|
}
|
|
this._trigger("usersJoined", [joinedUsers]);
|
|
}
|
|
break;
|
|
case "leave":
|
|
var leftSessionIds = data.event.leave || [];
|
|
if (leftSessionIds.length) {
|
|
console.log("Users left", leftSessionIds);
|
|
for (i = 0; i < leftSessionIds.length; i++) {
|
|
delete this.joinedUsers[leftSessionIds[i]];
|
|
}
|
|
this._trigger("usersLeft", [leftSessionIds]);
|
|
}
|
|
break;
|
|
default:
|
|
console.log("Unknown room event", data);
|
|
break;
|
|
}
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype.setRoomCollection = function(/* rooms */) {
|
|
OCA.Talk.Signaling.Base.prototype.setRoomCollection.apply(this, arguments);
|
|
// Retrieve initial list of rooms for this user.
|
|
return this.internalSyncRooms();
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype.syncRooms = function() {
|
|
if (this.pending_sync) {
|
|
// A sync request is already in progress, don't start another one.
|
|
return this.pending_sync;
|
|
}
|
|
|
|
// Never manually sync rooms, will be done based on notifications
|
|
// from the signaling server.
|
|
var defer = $.Deferred();
|
|
defer.resolve(this.rooms);
|
|
return defer;
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype.internalSyncRooms = function() {
|
|
if (this.pending_sync) {
|
|
// A sync request is already in progress, don't start another one.
|
|
return this.pending_sync;
|
|
}
|
|
|
|
var defer = $.Deferred();
|
|
this.pending_sync = OCA.Talk.Signaling.Base.prototype.syncRooms.apply(this, arguments);
|
|
this.pending_sync.then(function(rooms) {
|
|
this.pending_sync = null;
|
|
this.rooms = rooms;
|
|
defer.resolve(rooms);
|
|
}.bind(this));
|
|
return defer;
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype.processRoomListEvent = function(data) {
|
|
console.log("Room list event", data);
|
|
this.internalSyncRooms();
|
|
};
|
|
|
|
OCA.Talk.Signaling.Standalone.prototype.processRoomParticipantsEvent = function(data) {
|
|
switch (data.event.type) {
|
|
case "update":
|
|
this._trigger("usersChanged", [data.event.update.users]);
|
|
this.internalSyncRooms();
|
|
break;
|
|
default:
|
|
console.log("Unknown room participant event", data);
|
|
break;
|
|
}
|
|
};
|
|
|
|
})(OCA, OC, $);
|