diff --git a/src/CallHandler.js b/src/CallHandler.js index 187449924f..189e99b307 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -138,9 +138,17 @@ function _setCallListeners(call) { function _setCallState(call, roomId, status) { console.log( - "Call state in %s changed to %s (%s)", roomId, status, (call ? call.state : "-") + "Call state in %s changed to %s (%s)", roomId, status, (call ? call.call_state : "-") ); calls[roomId] = call; + + if (status === "ringing") { + play("ringAudio") + } + else if (call && call.call_state === "ringing") { + pause("ringAudio") + } + if (call) { call.call_state = status; } diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 6db2659986..01b877e6cf 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1207,8 +1207,9 @@ module.exports = React.createClass({ } var call = CallHandler.getCallForRoom(this.props.roomId); + //var call = CallHandler.getAnyActiveCall(); var inCall = false; - if (call && this.state.callState != 'ended') { + if (call && (this.state.callState !== 'ended' && this.state.callState !== 'ringing')) { inCall = true; var zoomButton, voiceMuteButton, videoMuteButton; diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 73f553af6e..7c228b5c9d 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -529,6 +529,7 @@ module.exports = React.createClass({ onHangupClick: function() { var call = CallHandler.getCallForRoom(this.props.room.roomId); + //var call = CallHandler.getAnyActiveCall(); if (!call) { return; } @@ -563,6 +564,7 @@ module.exports = React.createClass({ var callButton, videoCallButton, hangupButton; var call = CallHandler.getCallForRoom(this.props.room.roomId); + //var call = CallHandler.getAnyActiveCall(); if (this.props.callState && this.props.callState !== 'ended') { hangupButton =
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index a89dd55f1a..9ec2bf0d45 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -19,6 +19,7 @@ var React = require("react"); var ReactDOM = require("react-dom"); var GeminiScrollbar = require('react-gemini-scrollbar'); var MatrixClientPeg = require("../../../MatrixClientPeg"); +var CallHandler = require('../../../CallHandler'); var RoomListSorter = require("../../../RoomListSorter"); var UnreadStatus = require('../../../UnreadStatus'); var dis = require("../../../dispatcher"); @@ -39,6 +40,7 @@ module.exports = React.createClass({ return { activityMap: null, lists: {}, + incomingCall: null, } }, @@ -66,7 +68,21 @@ module.exports = React.createClass({ this.tooltip = payload.tooltip; this._repositionTooltip(); if (this.tooltip) this.tooltip.style.display = 'block'; - break + break; + case 'call_state': + var call = CallHandler.getCall(payload.room_id); + if (call && call.call_state === 'ringing') { + this.setState({ + incomingCall: call + }); + this._repositionIncomingCallBox(undefined, true); + } + else { + this.setState({ + incomingCall: null + }); + } + break; } }, @@ -212,10 +228,49 @@ module.exports = React.createClass({ return s; }, + _getScrollNode: function() { + var panel = ReactDOM.findDOMNode(this); + if (!panel) return null; + + if (panel.classList.contains('gm-prevented')) { + return panel; + } else { + return panel.children[2]; // XXX: Fragile! + } + }, + + _repositionTooltips: function(e) { + this._repositionTooltip(e); + this._repositionIncomingCallBox(e, false); + }, + _repositionTooltip: function(e) { if (this.tooltip && this.tooltip.parentElement) { var scroll = ReactDOM.findDOMNode(this); - this.tooltip.style.top = (scroll.parentElement.offsetTop + this.tooltip.parentElement.offsetTop - scroll.children[2].scrollTop) + "px"; + this.tooltip.style.top = (scroll.parentElement.offsetTop + this.tooltip.parentElement.offsetTop - this._getScrollNode().scrollTop) + "px"; + } + }, + + _repositionIncomingCallBox: function(e, firstTime) { + var incomingCallBox = document.getElementById("incomingCallBox"); + if (incomingCallBox && incomingCallBox.parentElement) { + var scroll = this._getScrollNode(); + var top = (scroll.offsetTop + incomingCallBox.parentElement.offsetTop - scroll.scrollTop); + + if (firstTime) { + // scroll to make sure the callbox is on the screen... + if (top < 10) { + scroll.scrollTop = incomingCallBox.parentElement.offsetTop - 10; + } + else if (top > scroll.clientHeight - incomingCallBox.offsetHeight) { + scroll.scrollTop = incomingCallBox.parentElement.offsetTop - scroll.offsetHeight + incomingCallBox.offsetHeight; + } + // recalculate top in case we clipped it. + top = (scroll.offsetTop + incomingCallBox.parentElement.offsetTop - scroll.scrollTop); + } + + incomingCallBox.style.top = top + "px"; + incomingCallBox.style.left = scroll.offsetLeft + scroll.offsetWidth + "px"; } }, @@ -234,7 +289,7 @@ module.exports = React.createClass({ var self = this; return ( - +
{ expandButton } @@ -244,6 +299,7 @@ module.exports = React.createClass({ order="recent" activityMap={ self.state.activityMap } selectedRoom={ self.props.selectedRoom } + incomingCall={ self.state.incomingCall } collapsed={ self.props.collapsed } /> { Object.keys(self.state.lists).map(function(tagName) { @@ -276,6 +334,7 @@ module.exports = React.createClass({ order="manual" activityMap={ self.state.activityMap } selectedRoom={ self.props.selectedRoom } + incomingCall={ self.state.incomingCall } collapsed={ self.props.collapsed } /> } @@ -290,6 +349,7 @@ module.exports = React.createClass({ bottommost={ self.state.lists['im.vector.fake.archived'].length === 0 } activityMap={ self.state.activityMap } selectedRoom={ self.props.selectedRoom } + incomingCall={ self.state.incomingCall } collapsed={ self.props.collapsed } />
diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 0a03ebe89d..37a77f9561 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -38,6 +38,7 @@ module.exports = React.createClass({ highlight: React.PropTypes.bool.isRequired, isInvite: React.PropTypes.bool.isRequired, roomSubList: React.PropTypes.object.isRequired, + incomingCall: React.PropTypes.object, }, getInitialState: function() { @@ -105,6 +106,12 @@ module.exports = React.createClass({ label = ; } + var incomingCallBox; + if (this.props.incomingCall) { + var IncomingCallBox = sdk.getComponent("voip.IncomingCallBox"); + incomingCallBox = ; + } + var RoomAvatar = sdk.getComponent('avatars.RoomAvatar'); // These props are injected by React DnD, @@ -120,6 +127,7 @@ module.exports = React.createClass({ { badge }
{ label } + { incomingCallBox } )); } diff --git a/src/components/views/voip/CallView.js b/src/components/views/voip/CallView.js index fbaed1dcd7..d67147dd1e 100644 --- a/src/components/views/voip/CallView.js +++ b/src/components/views/voip/CallView.js @@ -35,19 +35,13 @@ module.exports = React.createClass({ componentDidMount: function() { this.dispatcherRef = dis.register(this.onAction); - this._trackedRoom = null; if (this.props.room) { - this._trackedRoom = this.props.room; - this.showCall(this._trackedRoom.roomId); + this.showCall(this.props.room.roomId); } else { + // XXX: why would we ever not have a this.props.room? 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); } } @@ -81,7 +75,7 @@ module.exports = React.createClass({ // and for the voice stream of screen captures call.setRemoteAudioElement(this.getVideoView().getRemoteAudioElement()); } - if (call && call.type === "video" && call.state !== 'ended') { + if (call && call.type === "video" && call.call_state !== "ended" && call.call_state !== "ringing") { // 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 = ( diff --git a/src/components/views/voip/IncomingCallBox.js b/src/components/views/voip/IncomingCallBox.js index 263bbf543c..b110d45043 100644 --- a/src/components/views/voip/IncomingCallBox.js +++ b/src/components/views/voip/IncomingCallBox.js @@ -21,87 +21,29 @@ 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 + room_id: this.props.incomingCall.roomId }); }, onRejectClick: function() { dis.dispatch({ action: 'hangup', - room_id: this.state.incomingCall.roomId + room_id: this.props.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; + var room = MatrixClientPeg.get().getRoom(this.props.incomingCall.roomId); + var caller = room ? room.name : "unknown"; return ( -
- {audioBlock} +
- Incoming { this.state.incomingCall ? this.state.incomingCall.type : '' } call from { caller } + Incoming { this.props.incomingCall ? this.props.incomingCall.type : '' } call from { caller }