diff --git a/src/controllers/molecules/voip/CallView.js b/src/components/views/voip/CallView.js
similarity index 51%
rename from src/controllers/molecules/voip/CallView.js
rename to src/components/views/voip/CallView.js
index 4dd488c2dc..fbaed1dcd7 100644
--- a/src/controllers/molecules/voip/CallView.js
+++ b/src/components/views/voip/CallView.js
@@ -13,9 +13,11 @@ 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.
*/
-
+var React = require("react");
var dis = require("../../../dispatcher");
var CallHandler = require("../../../CallHandler");
+var sdk = require('../../../index');
+var MatrixClientPeg = require("../../../MatrixClientPeg");
/*
* State vars:
@@ -23,14 +25,31 @@ var CallHandler = require("../../../CallHandler");
*
* Props:
* this.props.room = Room (JS SDK)
+ * this.props.ConferenceHandler = A Conference Handler implementation
+ * Must have a function signature:
+ * getConferenceCallForRoom(roomId: string): MatrixCall
*/
-module.exports = {
+module.exports = React.createClass({
+ displayName: 'CallView',
componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction);
+ this._trackedRoom = null;
if (this.props.room) {
- this.showCall(this.props.room.roomId);
+ this._trackedRoom = this.props.room;
+ this.showCall(this._trackedRoom.roomId);
+ }
+ else {
+ var call = CallHandler.getAnyActiveCall();
+ if (call) {
+ console.log(
+ "Global CallView is now tracking active call in room %s",
+ call.roomId
+ );
+ this._trackedRoom = MatrixClientPeg.get().getRoom(call.roomId);
+ this.showCall(call.roomId);
+ }
}
},
@@ -39,19 +58,22 @@ module.exports = {
},
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') {
+ // don't filter out payloads for room IDs other than props.room because
+ // we may be interested in the conf 1:1 room
+ if (payload.action !== 'call_state' || !payload.room_id) {
return;
}
this.showCall(payload.room_id);
},
showCall: function(roomId) {
- var call = CallHandler.getCall(roomId);
+ var call = (
+ CallHandler.getCallForRoom(roomId) ||
+ (this.props.ConferenceHandler ?
+ this.props.ConferenceHandler.getConferenceCallForRoom(roomId) :
+ null
+ )
+ );
if (call) {
call.setLocalVideoElement(this.getVideoView().getLocalVideoElement());
call.setRemoteVideoElement(this.getVideoView().getRemoteVideoElement());
@@ -60,13 +82,29 @@ module.exports = {
call.setRemoteAudioElement(this.getVideoView().getRemoteAudioElement());
}
if (call && call.type === "video" && call.state !== 'ended') {
- this.getVideoView().getLocalVideoElement().style.display = "initial";
+ // if this call is a conf call, don't display local video as the
+ // conference will have us in it
+ this.getVideoView().getLocalVideoElement().style.display = (
+ call.confUserId ? "none" : "initial"
+ );
this.getVideoView().getRemoteVideoElement().style.display = "initial";
}
else {
this.getVideoView().getLocalVideoElement().style.display = "none";
this.getVideoView().getRemoteVideoElement().style.display = "none";
+ dis.dispatch({action: 'video_fullscreen', fullscreen: false});
}
- }
-};
+ },
+
+ getVideoView: function() {
+ return this.refs.video;
+ },
+
+ render: function(){
+ var VideoView = sdk.getComponent('voip.VideoView');
+ return (
+
+ );
+ }
+});
diff --git a/src/components/views/voip/IncomingCallBox.js b/src/components/views/voip/IncomingCallBox.js
new file mode 100644
index 0000000000..263bbf543c
--- /dev/null
+++ b/src/components/views/voip/IncomingCallBox.js
@@ -0,0 +1,122 @@
+/*
+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.
+*/
+var React = require('react');
+var MatrixClientPeg = require('../../../MatrixClientPeg');
+var dis = require("../../../dispatcher");
+var CallHandler = require("../../../CallHandler");
+
+module.exports = React.createClass({
+ displayName: 'IncomingCallBox',
+
+ componentDidMount: function() {
+ this.dispatcherRef = dis.register(this.onAction);
+ },
+
+ componentWillUnmount: function() {
+ dis.unregister(this.dispatcherRef);
+ },
+
+ getInitialState: function() {
+ return {
+ incomingCall: null
+ }
+ },
+
+ onAction: function(payload) {
+ if (payload.action !== 'call_state') {
+ return;
+ }
+ var call = CallHandler.getCall(payload.room_id);
+ if (!call || call.call_state !== 'ringing') {
+ this.setState({
+ incomingCall: null,
+ });
+ this.getRingAudio().pause();
+ return;
+ }
+ if (call.call_state === "ringing") {
+ this.getRingAudio().load();
+ this.getRingAudio().play();
+ }
+ else {
+ this.getRingAudio().pause();
+ }
+
+ this.setState({
+ incomingCall: call
+ });
+ },
+
+ onAnswerClick: function() {
+ dis.dispatch({
+ action: 'answer',
+ room_id: this.state.incomingCall.roomId
+ });
+ },
+
+ onRejectClick: function() {
+ dis.dispatch({
+ action: 'hangup',
+ room_id: this.state.incomingCall.roomId
+ });
+ },
+
+ getRingAudio: function() {
+ return this.refs.ringAudio;
+ },
+
+ render: function() {
+ // NB: This block MUST have a "key" so React doesn't clobber the elements
+ // between in-call / not-in-call.
+ var audioBlock = (
+
+ );
+
+ if (!this.state.incomingCall || !this.state.incomingCall.roomId) {
+ return (
+
+ {audioBlock}
+
+ );
+ }
+ var caller = MatrixClientPeg.get().getRoom(this.state.incomingCall.roomId).name;
+ return (
+
+ {audioBlock}
+
+
+ Incoming { this.state.incomingCall ? this.state.incomingCall.type : '' } call from { caller }
+
+
+
+ );
+ }
+});
+
diff --git a/src/components/views/voip/VideoView.js b/src/components/views/voip/VideoView.js
new file mode 100644
index 0000000000..0a95e0d0c8
--- /dev/null
+++ b/src/components/views/voip/VideoView.js
@@ -0,0 +1,93 @@
+/*
+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 React = require('react');
+var ReactDOM = require('react-dom');
+
+var sdk = require('../../../index');
+var dis = require('../../../dispatcher');
+
+module.exports = React.createClass({
+ displayName: 'VideoView',
+
+ componentWillMount: function() {
+ dis.register(this.onAction);
+ },
+
+ getRemoteVideoElement: function() {
+ return ReactDOM.findDOMNode(this.refs.remote);
+ },
+
+ getRemoteAudioElement: function() {
+ return this.refs.remoteAudio;
+ },
+
+ getLocalVideoElement: function() {
+ return ReactDOM.findDOMNode(this.refs.local);
+ },
+
+ setContainer: function(c) {
+ this.container = c;
+ },
+
+ onAction: function(payload) {
+ switch (payload.action) {
+ case 'video_fullscreen':
+ if (!this.container) {
+ return;
+ }
+ var element = this.container;
+ if (payload.fullscreen) {
+ var requestMethod = (
+ element.requestFullScreen ||
+ element.webkitRequestFullScreen ||
+ element.mozRequestFullScreen ||
+ element.msRequestFullscreen
+ );
+ requestMethod.call(element);
+ }
+ else {
+ var exitMethod = (
+ document.exitFullscreen ||
+ document.mozCancelFullScreen ||
+ document.webkitExitFullscreen ||
+ document.msExitFullscreen
+ );
+ if (exitMethod) {
+ exitMethod.call(document);
+ }
+ }
+ break;
+ }
+ },
+
+ render: function() {
+ var VideoFeed = sdk.getComponent('voip.VideoFeed');
+ return (
+
+ );
+ }
+});
diff --git a/src/controllers/molecules/voip/IncomingCallBox.js b/src/controllers/molecules/voip/IncomingCallBox.js
deleted file mode 100644
index 9ecced56c5..0000000000
--- a/src/controllers/molecules/voip/IncomingCallBox.js
+++ /dev/null
@@ -1,73 +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.
-*/
-
-var dis = require("../../../dispatcher");
-var CallHandler = require("../../../CallHandler");
-
-module.exports = {
- componentDidMount: function() {
- this.dispatcherRef = dis.register(this.onAction);
- },
-
- componentWillUnmount: function() {
- dis.unregister(this.dispatcherRef);
- },
-
- getInitialState: function() {
- return {
- incomingCall: null
- }
- },
-
- onAction: function(payload) {
- if (payload.action !== 'call_state') {
- return;
- }
- var call = CallHandler.getCall(payload.room_id);
- if (!call || call.call_state !== 'ringing') {
- this.setState({
- incomingCall: null,
- });
- this.getRingAudio().pause();
- return;
- }
- if (call.call_state === "ringing") {
- this.getRingAudio().load();
- this.getRingAudio().play();
- }
- else {
- this.getRingAudio().pause();
- }
-
- this.setState({
- incomingCall: call
- });
- },
-
- onAnswerClick: function() {
- dis.dispatch({
- action: 'answer',
- room_id: this.state.incomingCall.roomId
- });
- },
- onRejectClick: function() {
- dis.dispatch({
- action: 'hangup',
- room_id: this.state.incomingCall.roomId
- });
- }
-};
-