From 77401e215edb496e426bbf5f35986ae6c85682b2 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 11 Sep 2015 15:49:47 +0100 Subject: [PATCH] First working outbound conference calling This has a number of failings currently: 1) It needs to hide the 1:1 conference room, 2) Swapping tabs on the outbound call mutes audio (this just seems to be a vector bug since I can repro this on a normal 1:1 voip call), 3) Needs a big plinth/etc to say the conf call is in progress. --- src/CallHandler.js | 59 ++++++++++++++++++++------------- src/ConferenceHandler.js | 70 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 23 deletions(-) create mode 100644 src/ConferenceHandler.js diff --git a/src/CallHandler.js b/src/CallHandler.js index 0915a65af2..dbdb84e459 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -57,6 +57,7 @@ var MatrixClientPeg = require("./MatrixClientPeg"); var Modal = require("./Modal"); var ComponentBroker = require('./ComponentBroker'); var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog"); +var ConferenceHandler = require("./ConferenceHandler"); var Matrix = require("matrix-js-sdk"); var dis = require("./dispatcher"); @@ -161,37 +162,49 @@ dis.register(function(payload) { console.error("Room %s does not exist.", payload.room_id); return; } + + function placeCall(newCall) { + _setCallListeners(newCall); + _setCallState(newCall, newCall.roomId, "ringback"); + if (payload.type === 'voice') { + newCall.placeVoiceCall(); + } + else if (payload.type === 'video') { + newCall.placeVideoCall( + payload.remote_element, + payload.local_element + ); + } + else { + console.error("Unknown conf call type: %s", payload.type); + } + } + var members = room.getJoinedMembers(); - if (members.length !== 2) { - var text = members.length === 1 ? "yourself." : "more than 2 people."; + if (members.length <= 1) { Modal.createDialog(ErrorDialog, { - description: "You cannot place a call with " + text + description: "You cannot place a call with yourself." }); - console.error( - "Fail: There are %s joined members in this room, not 2.", - room.getJoinedMembers().length - ); return; } - console.log("Place %s call in %s", payload.type, payload.room_id); - var call = Matrix.createNewMatrixCall( - MatrixClientPeg.get(), payload.room_id - ); - _setCallListeners(call); - _setCallState(call, call.roomId, "ringback"); - if (payload.type === 'voice') { - call.placeVoiceCall(); - } - else if (payload.type === 'video') { - call.placeVideoCall( - payload.remote_element, - payload.local_element + else if (members.length === 2) { + console.log("Place %s call in %s", payload.type, payload.room_id); + var call = Matrix.createNewMatrixCall( + MatrixClientPeg.get(), payload.room_id ); + placeCall(call); } - else { - console.error("Unknown call type: %s", payload.type); + else { // > 2 + console.log("Place conference call in %s", payload.room_id); + var confHandler = new ConferenceHandler( + MatrixClientPeg.get(), payload.room_id + ); + confHandler.setup().done(function(call) { + placeCall(call); + }, function(err) { + console.error("Failed to setup conference call: %s", err); + }); } - break; case 'incoming_call': if (calls[payload.call.roomId]) { diff --git a/src/ConferenceHandler.js b/src/ConferenceHandler.js new file mode 100644 index 0000000000..e4d0f1f79b --- /dev/null +++ b/src/ConferenceHandler.js @@ -0,0 +1,70 @@ +"use strict"; +var q = require("q"); +var Matrix = require("matrix-js-sdk"); +var Room = Matrix.Room; + +var USER_PREFIX = "fs_"; +var DOMAIN = "matrix.org"; + +function ConferenceHandler(matrixClient, groupChatRoomId) { + this.client = matrixClient; + this.groupRoomId = groupChatRoomId; + // abuse browserify's core node Buffer support (strip padding ='s) + this.base64RoomId = new Buffer(this.groupRoomId).toString("base64").replace(/=/g, ""); + this.confUserId = "@" + USER_PREFIX + this.base64RoomId + ":" + DOMAIN; +} + +ConferenceHandler.prototype.setup = function() { + var self = this; + return this._joinConferenceUser().then(function() { + return self._getConferenceUserRoom(); + }).then(function(room) { + // return a call for *this* room to be placed. + return Matrix.createNewMatrixCall(self.client, room.roomId); + }); +}; + +ConferenceHandler.prototype._joinConferenceUser = function() { + // Make sure the conference user is in the group chat room + var groupRoom = this.client.getRoom(this.groupRoomId); + if (!groupRoom) { + return q.reject("Bad group room ID"); + } + var members = groupRoom.getJoinedMembers(); + var confUserExists = false; + for (var i = 0; i < members.length; i++) { + if (members[i].userId === this.confUserId) { + confUserExists = true; + break; + } + } + if (confUserExists) { + return q(); + } + return this.client.invite(this.groupRoomId, this.confUserId); +}; + +ConferenceHandler.prototype._getConferenceUserRoom = function() { + // Use an existing 1:1 with the conference user; else make one + var rooms = this.client.getRooms(); + var confRoom = null; + for (var i = 0; i < rooms.length; i++) { + if (rooms[i].hasMembershipState(this.confUserId, "join") && + rooms[i].getJoinedMembers().length === 2) { + confRoom = rooms[i]; + break; + } + } + if (confRoom) { + return q(confRoom); + } + return this.client.createRoom({ + preset: "private_chat", + invite: [this.confUserId] + }).then(function(res) { + return new Room(res.room_id); + }); +}; + +module.exports = ConferenceHandler; +