Add CallHandler singleton and add CallView.
CallView is the container for either VideoViews or WaveformViews. All UI elements listen for 'call_state' payloads and then call CallHandler.getCall(roomId) to extract the current MatrixCall for that room. We can't do this via stateful dispatches because dispatching does not preserve ordering empirically (probably due to setTimeout).
This commit is contained in:
parent
6316f1b195
commit
37c9c8fbb4
|
@ -49,6 +49,13 @@ module.exports = React.createClass({
|
|||
<div className="mx_RoomHeader_button">
|
||||
<img src="img/search.png" width="32" height="32"/>
|
||||
</div>
|
||||
{
|
||||
this.state && this.state.inCall ?
|
||||
<div className="mx_RoomHeader_button" onClick={this.onHangupClick}>
|
||||
<img src="img/video.png" width="64" height="32"/>
|
||||
</div>
|
||||
: null
|
||||
}
|
||||
<div className="mx_RoomHeader_button" onClick={this.onVideoClick}>
|
||||
<img src="img/video.png" width="32" height="32"/>
|
||||
</div>
|
||||
|
|
|
@ -20,20 +20,24 @@ var React = require('react');
|
|||
|
||||
var MatrixClientPeg = require("../../../../../src/MatrixClientPeg");
|
||||
var ComponentBroker = require('../../../../../src/ComponentBroker');
|
||||
var CallHandlerController = require(
|
||||
"../../../../../src/controllers/molecules/voip/CallHandler"
|
||||
var CallViewController = require(
|
||||
"../../../../../src/controllers/molecules/voip/CallView"
|
||||
);
|
||||
var VideoView = ComponentBroker.get('molecules/voip/VideoView');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'CallHandler',
|
||||
mixins: [CallHandlerController],
|
||||
displayName: 'CallView',
|
||||
mixins: [CallViewController],
|
||||
|
||||
getVideoView: function() {
|
||||
return this.refs.video;
|
||||
},
|
||||
|
||||
render: function(){
|
||||
return (
|
||||
<VideoView ref="video"/>
|
||||
);
|
||||
/*
|
||||
if (this.state && this.state.call) {
|
||||
if (this.state.call.type === "video") {
|
||||
return (
|
||||
|
@ -49,6 +53,6 @@ module.exports = React.createClass({
|
|||
}
|
||||
return (
|
||||
<div></div>
|
||||
);
|
||||
); */
|
||||
}
|
||||
});
|
|
@ -26,7 +26,7 @@ var classNames = require("classnames");
|
|||
var MessageTile = ComponentBroker.get('molecules/MessageTile');
|
||||
var RoomHeader = ComponentBroker.get('molecules/RoomHeader');
|
||||
var MessageComposer = ComponentBroker.get('molecules/MessageComposer');
|
||||
var CallHandler = ComponentBroker.get("molecules/voip/CallHandler");
|
||||
var CallView = ComponentBroker.get("molecules/voip/CallView");
|
||||
|
||||
var RoomViewController = require("../../../../src/controllers/organisms/RoomView");
|
||||
|
||||
|
@ -69,7 +69,7 @@ module.exports = React.createClass({
|
|||
<div className="mx_RoomView">
|
||||
<RoomHeader room={this.state.room} />
|
||||
<div className="mx_RoomView_auxPanel">
|
||||
<CallHandler room={this.state.room}/>
|
||||
<CallView room={this.state.room}/>
|
||||
</div>
|
||||
<div ref="messageWrapper" className="mx_RoomView_messagePanel" onScroll={this.onMessageListScroll}>
|
||||
<div className="mx_RoomView_messageListWrapper">
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
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/removed from this list:
|
||||
* {
|
||||
* action: 'call_state'
|
||||
* room_id: <room ID of the call>
|
||||
* }
|
||||
*
|
||||
* To know if the call was added/removed, this handler exposes a getter to
|
||||
* obtain the call for a room:
|
||||
* CallHandler.getCall(roomId)
|
||||
*
|
||||
* 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("./MatrixClientPeg");
|
||||
var Matrix = require("matrix-js-sdk");
|
||||
var dis = require("./dispatcher");
|
||||
|
||||
var calls = {
|
||||
//room_id: MatrixCall
|
||||
};
|
||||
|
||||
function _setCallListeners(call) {
|
||||
call.on("error", function(err) {
|
||||
console.error("Call error: %s", err);
|
||||
console.error(err.stack);
|
||||
call.hangup();
|
||||
_setCallState(undefined, call.roomId);
|
||||
});
|
||||
call.on("hangup", function() {
|
||||
_setCallState(undefined, call.roomId);
|
||||
});
|
||||
}
|
||||
|
||||
function _setCallState(call, roomId) {
|
||||
console.log("_setState >>> %s >>> %s ", call, roomId);
|
||||
calls[roomId] = call;
|
||||
dis.dispatch({
|
||||
action: 'call_state',
|
||||
room_id: roomId
|
||||
});
|
||||
}
|
||||
|
||||
dis.register(function(payload) {
|
||||
switch (payload.action) {
|
||||
case 'place_call':
|
||||
if (calls[payload.room_id]) {
|
||||
return; // don't allow >1 call to be placed.
|
||||
}
|
||||
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);
|
||||
if (payload.type === 'voice') {
|
||||
call.placeVoiceCall();
|
||||
}
|
||||
else if (payload.type === 'video') {
|
||||
call.placeVideoCall(
|
||||
payload.remote_element,
|
||||
payload.local_element
|
||||
);
|
||||
}
|
||||
else {
|
||||
console.error("Unknown call type: %s", payload.type);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'incoming_call':
|
||||
if (calls[payload.call.roomId]) {
|
||||
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);
|
||||
break;
|
||||
case 'hangup':
|
||||
if (!calls[payload.room_id]) {
|
||||
return; // no call to hangup
|
||||
}
|
||||
calls[payload.room_id].hangup();
|
||||
_setCallState(null, payload.room_id);
|
||||
break;
|
||||
case 'answer':
|
||||
if (!calls[payload.room_id]) {
|
||||
return; // no call to answer
|
||||
}
|
||||
calls[payload.room_id].answer();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
getCall: function(roomId) {
|
||||
return calls[roomId] || null;
|
||||
}
|
||||
};
|
|
@ -96,7 +96,7 @@ require('../skins/base/views/molecules/RoomDropTarget');
|
|||
require('../skins/base/views/molecules/DirectoryMenu');
|
||||
require('../skins/base/views/atoms/voip/VideoFeed');
|
||||
require('../skins/base/views/molecules/voip/VideoView');
|
||||
require('../skins/base/views/molecules/voip/CallHandler');
|
||||
require('../skins/base/views/molecules/voip/CallView');
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -16,9 +16,40 @@ limitations under the License.
|
|||
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* State vars:
|
||||
* this.state.inCall = boolean
|
||||
*/
|
||||
|
||||
var dis = require("../../dispatcher");
|
||||
var CallHandler = require("../../CallHandler");
|
||||
|
||||
module.exports = {
|
||||
componentDidMount: function() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
this.setState({
|
||||
inCall: false
|
||||
});
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
dis.unregister(this.dispatcherRef);
|
||||
},
|
||||
|
||||
onAction: function(payload) {
|
||||
// if we were given a room_id to track, don't handle anything else.
|
||||
if (payload.room_id && this.props.room &&
|
||||
this.props.room.roomId !== payload.room_id) {
|
||||
return;
|
||||
}
|
||||
if (payload.action !== 'call_state') {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
inCall: (CallHandler.getCall(payload.room_id) !== null)
|
||||
});
|
||||
},
|
||||
|
||||
onVideoClick: function() {
|
||||
dis.dispatch({
|
||||
action: 'place_call',
|
||||
|
@ -32,6 +63,12 @@ module.exports = {
|
|||
type: "voice",
|
||||
room_id: this.props.room.roomId
|
||||
});
|
||||
},
|
||||
onHangupClick: function() {
|
||||
dis.dispatch({
|
||||
action: 'hangup',
|
||||
room_id: this.props.room.roomId
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,123 +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';
|
||||
var MatrixClientPeg = require("../../../MatrixClientPeg");
|
||||
var Matrix = require("matrix-js-sdk");
|
||||
var dis = require("../../../dispatcher");
|
||||
|
||||
/*
|
||||
* State vars:
|
||||
* this.state.call = MatrixCall|null
|
||||
*
|
||||
* Props:
|
||||
* this.props.room = Room (JS SDK) - can be null (for singleton views)
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
|
||||
componentDidMount: function() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
this.setState({
|
||||
call: null
|
||||
});
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
dis.unregister(this.dispatcherRef);
|
||||
},
|
||||
|
||||
onAction: function(payload) {
|
||||
// if we were given a room_id to track, don't handle anything else.
|
||||
if (payload.room_id && this.props.room &&
|
||||
this.props.room.roomId !== payload.room_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (payload.action) {
|
||||
case 'place_call':
|
||||
if (this.state.call) {
|
||||
return; // don't allow >1 call to be placed.
|
||||
}
|
||||
console.log("Place %s call in %s", payload.type, payload.room_id);
|
||||
var call = Matrix.createNewMatrixCall(
|
||||
MatrixClientPeg.get(), payload.room_id
|
||||
);
|
||||
this._setCallListeners(call);
|
||||
this.setState({
|
||||
call: call
|
||||
});
|
||||
if (payload.type === 'voice') {
|
||||
call.placeVoiceCall();
|
||||
}
|
||||
else if (payload.type === 'video') {
|
||||
var videoView = this.getVideoView();
|
||||
call.placeVideoCall(
|
||||
videoView.getRemoteVideoElement(),
|
||||
videoView.getLocalVideoElement()
|
||||
);
|
||||
}
|
||||
else {
|
||||
console.error("Unknown call type: %s", payload.type);
|
||||
}
|
||||
break;
|
||||
case 'incoming_call':
|
||||
if (this.state.call) {
|
||||
payload.call.hangup("busy");
|
||||
return; // don't allow >1 call to be received.
|
||||
}
|
||||
this._setCallListeners(call);
|
||||
this.setState({
|
||||
call: call
|
||||
});
|
||||
console.log("Incoming call: %s", payload.call);
|
||||
break;
|
||||
case 'hangup':
|
||||
if (!this.state.call) {
|
||||
return; // no call to hangup
|
||||
}
|
||||
this.state.call.hangup();
|
||||
this.setState({
|
||||
call: null
|
||||
});
|
||||
break;
|
||||
case 'answer':
|
||||
if (!this.state.call) {
|
||||
return; // no call to answer
|
||||
}
|
||||
this.state.call.answer();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_setCallListeners: function(call) {
|
||||
var self = this;
|
||||
call.on("error", function(err) {
|
||||
console.error("Call error: %s", err);
|
||||
console.error(err.stack);
|
||||
call.hangup();
|
||||
self.setState({
|
||||
call: null
|
||||
});
|
||||
});
|
||||
call.on("hangup", function() {
|
||||
self.setState({
|
||||
call: null
|
||||
});
|
||||
})
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
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 dis = require("../../../dispatcher");
|
||||
var CallHandler = require("../../../CallHandler");
|
||||
|
||||
/*
|
||||
* State vars:
|
||||
* this.state.call = MatrixCall|null
|
||||
*
|
||||
* Props:
|
||||
* this.props.room = Room (JS SDK)
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
|
||||
componentDidMount: function() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
this.setState({
|
||||
call: null
|
||||
});
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
dis.unregister(this.dispatcherRef);
|
||||
},
|
||||
|
||||
onAction: function(payload) {
|
||||
// if we were given a room_id to track, don't handle anything else.
|
||||
if (payload.room_id && this.props.room &&
|
||||
this.props.room.roomId !== payload.room_id) {
|
||||
return;
|
||||
}
|
||||
if (payload.action !== 'call_state') {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
call: CallHandler.getCall(payload.room_id)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue