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 } +
+
+
+
+ Decline +
+
+
+
+ Accept +
+
+
+
+ ); + } +}); + 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 - }); - } -}; -