Use the new interface for providing conf call functionality. Doesn't shoe it in the right room yet.

This commit is contained in:
David Baker 2015-09-30 16:52:45 +01:00
parent fd6e7663cb
commit 94a6f856d1
6 changed files with 92 additions and 351 deletions
package.json
src
CallHandler.js
controllers/molecules/voip
modules
skins/vector
vector

View File

@ -23,14 +23,15 @@
"prepublish": "npm run build:css && npm run build:compile"
},
"dependencies": {
"matrix-react-sdk": "^0.0.1",
"classnames": "^2.1.2",
"filesize": "^3.1.2",
"flux": "~2.0.3",
"linkifyjs": "^2.0.0-beta.4",
"matrix-js-sdk": "^0.2.1",
"matrix-react-sdk": "^0.0.1",
"q": "^1.4.1",
"react": "^0.13.3",
"react-loader": "^1.4.0",
"linkifyjs": "^2.0.0-beta.4"
"react-loader": "^1.4.0"
},
"devDependencies": {
"babel": "^5.8.23",

View File

@ -1,281 +0,0 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
/*
* Manages a list of all the currently active calls.
*
* This handler dispatches when voip calls are added/updated/removed from this list:
* {
* action: 'call_state'
* room_id: <room ID of the call>
* }
*
* To know the state of the call, this handler exposes a getter to
* obtain the call for a room:
* var call = CallHandler.getCall(roomId)
* var state = call.call_state; // ringing|ringback|connected|ended|busy|stop_ringback|stop_ringing
*
* This handler listens for and handles the following actions:
* {
* action: 'place_call',
* type: 'voice|video',
* room_id: <room that the place call button was pressed in>
* }
*
* {
* action: 'incoming_call'
* call: MatrixCall
* }
*
* {
* action: 'hangup'
* room_id: <room that the hangup button was pressed in>
* }
*
* {
* action: 'answer'
* room_id: <room that the answer button was pressed in>
* }
*/
var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg");
var Modal = require("matrix-react-sdk/lib/Modal");
var sdk = require('matrix-react-sdk');
var ConferenceCall = require("./ConferenceHandler").ConferenceCall;
var ConferenceHandler = require("./ConferenceHandler");
var Matrix = require("matrix-js-sdk");
var dis = require("./dispatcher");
var calls = {
//room_id: MatrixCall
};
function play(audioId) {
// TODO: Attach an invisible element for this instead
// which listens?
var audio = document.getElementById(audioId);
if (audio) {
audio.load();
audio.play();
}
}
function pause(audioId) {
// TODO: Attach an invisible element for this instead
// which listens?
var audio = document.getElementById(audioId);
if (audio) {
audio.pause();
}
}
function _setCallListeners(call) {
call.on("error", function(err) {
console.error("Call error: %s", err);
console.error(err.stack);
call.hangup();
_setCallState(undefined, call.roomId, "ended");
});
call.on("hangup", function() {
_setCallState(undefined, call.roomId, "ended");
});
// map web rtc states to dummy UI state
// ringing|ringback|connected|ended|busy|stop_ringback|stop_ringing
call.on("state", function(newState, oldState) {
if (newState === "ringing") {
_setCallState(call, call.roomId, "ringing");
pause("ringbackAudio");
}
else if (newState === "invite_sent") {
_setCallState(call, call.roomId, "ringback");
play("ringbackAudio");
}
else if (newState === "ended" && oldState === "connected") {
_setCallState(undefined, call.roomId, "ended");
pause("ringbackAudio");
play("callendAudio");
}
else if (newState === "ended" && oldState === "invite_sent" &&
(call.hangupParty === "remote" ||
(call.hangupParty === "local" && call.hangupReason === "invite_timeout")
)) {
_setCallState(call, call.roomId, "busy");
pause("ringbackAudio");
play("busyAudio");
var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: "Call Timeout",
description: "The remote side failed to pick up."
});
}
else if (oldState === "invite_sent") {
_setCallState(call, call.roomId, "stop_ringback");
pause("ringbackAudio");
}
else if (oldState === "ringing") {
_setCallState(call, call.roomId, "stop_ringing");
pause("ringbackAudio");
}
else if (newState === "connected") {
_setCallState(call, call.roomId, "connected");
pause("ringbackAudio");
}
});
}
function _setCallState(call, roomId, status) {
console.log(
"Call state in %s changed to %s (%s)", roomId, status, (call ? call.state : "-")
);
calls[roomId] = call;
if (call) {
call.call_state = status;
}
dis.dispatch({
action: 'call_state',
room_id: roomId
});
}
dis.register(function(payload) {
switch (payload.action) {
case 'place_call':
if (module.exports.getAnyActiveCall()) {
Modal.createDialog(ErrorDialog, {
title: "Existing Call",
description: "You are already in a call."
});
return; // don't allow >1 call to be placed.
}
var room = MatrixClientPeg.get().getRoom(payload.room_id);
if (!room) {
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 <= 1) {
Modal.createDialog(ErrorDialog, {
description: "You cannot place a call with yourself."
});
return;
}
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 { // > 2
console.log("Place conference call in %s", payload.room_id);
var confCall = new ConferenceCall(
MatrixClientPeg.get(), payload.room_id
);
confCall.setup().done(function(call) {
placeCall(call);
}, function(err) {
console.error("Failed to setup conference call: %s", err);
});
}
break;
case 'incoming_call':
if (module.exports.getAnyActiveCall()) {
payload.call.hangup("busy");
return; // don't allow >1 call to be received, hangup newer one.
}
var call = payload.call;
_setCallListeners(call);
_setCallState(call, call.roomId, "ringing");
break;
case 'hangup':
if (!calls[payload.room_id]) {
return; // no call to hangup
}
calls[payload.room_id].hangup();
_setCallState(null, payload.room_id, "ended");
break;
case 'answer':
if (!calls[payload.room_id]) {
return; // no call to answer
}
calls[payload.room_id].answer();
_setCallState(calls[payload.room_id], payload.room_id, "connected");
dis.dispatch({
action: "view_room",
room_id: payload.room_id
});
break;
}
});
module.exports = {
getCallForRoom: function(roomId) {
return (
module.exports.getCall(roomId) ||
module.exports.getConferenceCall(roomId)
);
},
getCall: function(roomId) {
return calls[roomId] || null;
},
getConferenceCall: function(roomId) {
// search for a conference 1:1 call for this group chat room ID
var activeCall = module.exports.getAnyActiveCall();
if (activeCall && activeCall.confUserId) {
var thisRoomConfUserId = ConferenceHandler.getConferenceUserIdForRoom(
roomId
);
if (thisRoomConfUserId === activeCall.confUserId) {
return activeCall;
}
}
return null;
},
getAnyActiveCall: function() {
var roomsWithCalls = Object.keys(calls);
for (var i = 0; i < roomsWithCalls.length; i++) {
if (calls[roomsWithCalls[i]] &&
calls[roomsWithCalls[i]].call_state !== "ended") {
return calls[roomsWithCalls[i]];
}
}
return null;
}
};

View File

@ -16,9 +16,11 @@ limitations under the License.
'use strict';
var dis = require("../../../dispatcher");
var CallHandler = require("../../../CallHandler");
var CallHandler = require("matrix-react-sdk/lib/CallHandler");
var MatrixClientPeg = require("../../../MatrixClientPeg");
var VectorConferenceHandler = require('./VectorConferenceHandler');
/*
* State vars:
* this.state.call = MatrixCall|null
@ -66,7 +68,10 @@ module.exports = {
},
showCall: function(roomId) {
var call = CallHandler.getCallForRoom(roomId);
var call = (
CallHandler.getCallForRoom(roomId) ||
VectorConferenceHandler.getConferenceCallForRoom(roomId)
);
if (call) {
call.setLocalVideoElement(this.getVideoView().getLocalVideoElement());
// N.B. the remote video element is used for playback for audio for voice calls

View File

@ -1,4 +1,21 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
"use strict";
var q = require("q");
var Matrix = require("matrix-js-sdk");
var Room = Matrix.Room;
@ -90,5 +107,12 @@ module.exports.getConferenceUserIdForRoom = function(roomId) {
return "@" + USER_PREFIX + base64RoomId + ":" + DOMAIN;
};
module.exports.createNewMatrixCall = function(client, roomId) {
return new ConferenceCall(
client, roomId
);
};
module.exports.ConferenceCall = ConferenceCall;
module.exports.slot = 'conference';

View File

@ -21,71 +21,62 @@ limitations under the License.
* You are not a salmon.
*/
var skin = {
atoms: {},
molecules: {},
organisms: {},
templates: {},
pages: {}
};
var skin = {};
skin.atoms.EditableText = require('./views/atoms/EditableText');
skin.atoms.EnableNotificationsButton = require('./views/atoms/EnableNotificationsButton');
skin.atoms.ImageView = require('./views/atoms/ImageView');
skin.atoms.LogoutButton = require('./views/atoms/LogoutButton');
skin.atoms.MemberAvatar = require('./views/atoms/MemberAvatar');
skin.atoms.MessageTimestamp = require('./views/atoms/MessageTimestamp');
skin.atoms.RoomAvatar = require('./views/atoms/RoomAvatar');
skin.atoms.create_room = {};
skin.atoms.create_room.CreateRoomButton = require('./views/atoms/create_room/CreateRoomButton');
skin.atoms.create_room.Presets = require('./views/atoms/create_room/Presets');
skin.atoms.create_room.RoomAlias = require('./views/atoms/create_room/RoomAlias');
skin.atoms.voip = {};
skin.atoms.voip.VideoFeed = require('./views/atoms/voip/VideoFeed');
skin.molecules.BottomLeftMenu = require('./views/molecules/BottomLeftMenu');
skin.molecules.ChangeAvatar = require('./views/molecules/ChangeAvatar');
skin.molecules.ChangePassword = require('./views/molecules/ChangePassword');
skin.molecules.DateSeparator = require('./views/molecules/DateSeparator');
skin.molecules.EventAsTextTile = require('./views/molecules/EventAsTextTile');
skin.molecules.MEmoteTile = require('./views/molecules/MEmoteTile');
skin.molecules.MFileTile = require('./views/molecules/MFileTile');
skin.molecules.MImageTile = require('./views/molecules/MImageTile');
skin.molecules.MNoticeTile = require('./views/molecules/MNoticeTile');
skin.molecules.MRoomMemberTile = require('./views/molecules/MRoomMemberTile');
skin.molecules.MTextTile = require('./views/molecules/MTextTile');
skin.molecules.MatrixToolbar = require('./views/molecules/MatrixToolbar');
skin.molecules.MemberInfo = require('./views/molecules/MemberInfo');
skin.molecules.MemberTile = require('./views/molecules/MemberTile');
skin.molecules.MessageComposer = require('./views/molecules/MessageComposer');
skin.molecules.MessageTile = require('./views/molecules/MessageTile');
skin.molecules.ProgressBar = require('./views/molecules/ProgressBar');
skin.molecules.RoomCreate = require('./views/molecules/RoomCreate');
skin.molecules.RoomDropTarget = require('./views/molecules/RoomDropTarget');
skin.molecules.RoomHeader = require('./views/molecules/RoomHeader');
skin.molecules.RoomSettings = require('./views/molecules/RoomSettings');
skin.molecules.RoomTile = require('./views/molecules/RoomTile');
skin.molecules.SenderProfile = require('./views/molecules/SenderProfile');
skin.molecules.ServerConfig = require('./views/molecules/ServerConfig');
skin.molecules.UnknownMessageTile = require('./views/molecules/UnknownMessageTile');
skin.molecules.UserSelector = require('./views/molecules/UserSelector');
skin.molecules.voip = {};
skin.molecules.voip.CallView = require('./views/molecules/voip/CallView');
skin.molecules.voip.IncomingCallBox = require('./views/molecules/voip/IncomingCallBox');
skin.molecules.voip.VideoView = require('./views/molecules/voip/VideoView');
skin.organisms.CreateRoom = require('./views/organisms/CreateRoom');
skin.organisms.ErrorDialog = require('./views/organisms/ErrorDialog');
skin.organisms.LeftPanel = require('./views/organisms/LeftPanel');
skin.organisms.LogoutPrompt = require('./views/organisms/LogoutPrompt');
skin.organisms.MemberList = require('./views/organisms/MemberList');
skin.organisms.Notifier = require('./views/organisms/Notifier');
skin.organisms.QuestionDialog = require('./views/organisms/QuestionDialog');
skin.organisms.RightPanel = require('./views/organisms/RightPanel');
skin.organisms.RoomDirectory = require('./views/organisms/RoomDirectory');
skin.organisms.RoomList = require('./views/organisms/RoomList');
skin.organisms.RoomView = require('./views/organisms/RoomView');
skin.organisms.UserSettings = require('./views/organisms/UserSettings');
skin.pages.MatrixChat = require('./views/pages/MatrixChat');
skin.templates.Login = require('./views/templates/Login');
skin.templates.Register = require('./views/templates/Register');
skin['atoms.EditableText'] = require('./views/atoms/EditableText');
skin['atoms.EnableNotificationsButton'] = require('./views/atoms/EnableNotificationsButton');
skin['atoms.ImageView'] = require('./views/atoms/ImageView');
skin['atoms.LogoutButton'] = require('./views/atoms/LogoutButton');
skin['atoms.MemberAvatar'] = require('./views/atoms/MemberAvatar');
skin['atoms.MessageTimestamp'] = require('./views/atoms/MessageTimestamp');
skin['atoms.RoomAvatar'] = require('./views/atoms/RoomAvatar');
skin['atoms.create_room.CreateRoomButton'] = require('./views/atoms/create_room/CreateRoomButton');
skin['atoms.create_room.Presets'] = require('./views/atoms/create_room/Presets');
skin['atoms.create_room.RoomAlias'] = require('./views/atoms/create_room/RoomAlias');
skin['atoms.voip.VideoFeed'] = require('./views/atoms/voip/VideoFeed');
skin['molecules.BottomLeftMenu'] = require('./views/molecules/BottomLeftMenu');
skin['molecules.ChangeAvatar'] = require('./views/molecules/ChangeAvatar');
skin['molecules.ChangePassword'] = require('./views/molecules/ChangePassword');
skin['molecules.DateSeparator'] = require('./views/molecules/DateSeparator');
skin['molecules.EventAsTextTile'] = require('./views/molecules/EventAsTextTile');
skin['molecules.MEmoteTile'] = require('./views/molecules/MEmoteTile');
skin['molecules.MFileTile'] = require('./views/molecules/MFileTile');
skin['molecules.MImageTile'] = require('./views/molecules/MImageTile');
skin['molecules.MNoticeTile'] = require('./views/molecules/MNoticeTile');
skin['molecules.MRoomMemberTile'] = require('./views/molecules/MRoomMemberTile');
skin['molecules.MTextTile'] = require('./views/molecules/MTextTile');
skin['molecules.MatrixToolbar'] = require('./views/molecules/MatrixToolbar');
skin['molecules.MemberInfo'] = require('./views/molecules/MemberInfo');
skin['molecules.MemberTile'] = require('./views/molecules/MemberTile');
skin['molecules.MessageComposer'] = require('./views/molecules/MessageComposer');
skin['molecules.MessageTile'] = require('./views/molecules/MessageTile');
skin['molecules.ProgressBar'] = require('./views/molecules/ProgressBar');
skin['molecules.RoomCreate'] = require('./views/molecules/RoomCreate');
skin['molecules.RoomDropTarget'] = require('./views/molecules/RoomDropTarget');
skin['molecules.RoomHeader'] = require('./views/molecules/RoomHeader');
skin['molecules.RoomSettings'] = require('./views/molecules/RoomSettings');
skin['molecules.RoomTile'] = require('./views/molecules/RoomTile');
skin['molecules.SenderProfile'] = require('./views/molecules/SenderProfile');
skin['molecules.ServerConfig'] = require('./views/molecules/ServerConfig');
skin['molecules.UnknownMessageTile'] = require('./views/molecules/UnknownMessageTile');
skin['molecules.UserSelector'] = require('./views/molecules/UserSelector');
skin['molecules.voip.CallView'] = require('./views/molecules/voip/CallView');
skin['molecules.voip.IncomingCallBox'] = require('./views/molecules/voip/IncomingCallBox');
skin['molecules.voip.VideoView'] = require('./views/molecules/voip/VideoView');
skin['organisms.CreateRoom'] = require('./views/organisms/CreateRoom');
skin['organisms.ErrorDialog'] = require('./views/organisms/ErrorDialog');
skin['organisms.LeftPanel'] = require('./views/organisms/LeftPanel');
skin['organisms.LogoutPrompt'] = require('./views/organisms/LogoutPrompt');
skin['organisms.MemberList'] = require('./views/organisms/MemberList');
skin['organisms.Notifier'] = require('./views/organisms/Notifier');
skin['organisms.QuestionDialog'] = require('./views/organisms/QuestionDialog');
skin['organisms.RightPanel'] = require('./views/organisms/RightPanel');
skin['organisms.RoomDirectory'] = require('./views/organisms/RoomDirectory');
skin['organisms.RoomList'] = require('./views/organisms/RoomList');
skin['organisms.RoomView'] = require('./views/organisms/RoomView');
skin['organisms.UserSettings'] = require('./views/organisms/UserSettings');
skin['pages.MatrixChat'] = require('./views/pages/MatrixChat');
skin['templates.Login'] = require('./views/templates/Login');
skin['templates.Register'] = require('./views/templates/Register');
module.exports = skin;

View File

@ -19,6 +19,7 @@ limitations under the License.
var React = require("react");
var sdk = require("matrix-react-sdk");
sdk.loadSkin(require('../skins/vector/skindex'));
sdk.loadModule(require('../modules/VectorConferenceHandler'));
var lastLocationHashSet = null;