Merge pull request #57 from matrix-org/matthew/inbound-calls
Position the inbound call box correctly
This commit is contained in:
commit
831aaec457
7 changed files with 103 additions and 78 deletions
|
@ -138,9 +138,17 @@ function _setCallListeners(call) {
|
||||||
|
|
||||||
function _setCallState(call, roomId, status) {
|
function _setCallState(call, roomId, status) {
|
||||||
console.log(
|
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;
|
calls[roomId] = call;
|
||||||
|
|
||||||
|
if (status === "ringing") {
|
||||||
|
play("ringAudio")
|
||||||
|
}
|
||||||
|
else if (call && call.call_state === "ringing") {
|
||||||
|
pause("ringAudio")
|
||||||
|
}
|
||||||
|
|
||||||
if (call) {
|
if (call) {
|
||||||
call.call_state = status;
|
call.call_state = status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1280,8 +1280,9 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
var call = CallHandler.getCallForRoom(this.props.roomId);
|
var call = CallHandler.getCallForRoom(this.props.roomId);
|
||||||
|
//var call = CallHandler.getAnyActiveCall();
|
||||||
var inCall = false;
|
var inCall = false;
|
||||||
if (call && this.state.callState != 'ended') {
|
if (call && (this.state.callState !== 'ended' && this.state.callState !== 'ringing')) {
|
||||||
inCall = true;
|
inCall = true;
|
||||||
var zoomButton, voiceMuteButton, videoMuteButton;
|
var zoomButton, voiceMuteButton, videoMuteButton;
|
||||||
|
|
||||||
|
|
|
@ -529,6 +529,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
onHangupClick: function() {
|
onHangupClick: function() {
|
||||||
var call = CallHandler.getCallForRoom(this.props.room.roomId);
|
var call = CallHandler.getCallForRoom(this.props.room.roomId);
|
||||||
|
//var call = CallHandler.getAnyActiveCall();
|
||||||
if (!call) {
|
if (!call) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -563,6 +564,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
var callButton, videoCallButton, hangupButton;
|
var callButton, videoCallButton, hangupButton;
|
||||||
var call = CallHandler.getCallForRoom(this.props.room.roomId);
|
var call = CallHandler.getCallForRoom(this.props.room.roomId);
|
||||||
|
//var call = CallHandler.getAnyActiveCall();
|
||||||
if (this.props.callState && this.props.callState !== 'ended') {
|
if (this.props.callState && this.props.callState !== 'ended') {
|
||||||
hangupButton =
|
hangupButton =
|
||||||
<div className="mx_MessageComposer_hangup" onClick={this.onHangupClick}>
|
<div className="mx_MessageComposer_hangup" onClick={this.onHangupClick}>
|
||||||
|
|
|
@ -19,6 +19,7 @@ var React = require("react");
|
||||||
var ReactDOM = require("react-dom");
|
var ReactDOM = require("react-dom");
|
||||||
var GeminiScrollbar = require('react-gemini-scrollbar');
|
var GeminiScrollbar = require('react-gemini-scrollbar');
|
||||||
var MatrixClientPeg = require("../../../MatrixClientPeg");
|
var MatrixClientPeg = require("../../../MatrixClientPeg");
|
||||||
|
var CallHandler = require('../../../CallHandler');
|
||||||
var RoomListSorter = require("../../../RoomListSorter");
|
var RoomListSorter = require("../../../RoomListSorter");
|
||||||
var UnreadStatus = require('../../../UnreadStatus');
|
var UnreadStatus = require('../../../UnreadStatus');
|
||||||
var dis = require("../../../dispatcher");
|
var dis = require("../../../dispatcher");
|
||||||
|
@ -39,6 +40,7 @@ module.exports = React.createClass({
|
||||||
return {
|
return {
|
||||||
activityMap: null,
|
activityMap: null,
|
||||||
lists: {},
|
lists: {},
|
||||||
|
incomingCall: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -66,7 +68,21 @@ module.exports = React.createClass({
|
||||||
this.tooltip = payload.tooltip;
|
this.tooltip = payload.tooltip;
|
||||||
this._repositionTooltip();
|
this._repositionTooltip();
|
||||||
if (this.tooltip) this.tooltip.style.display = 'block';
|
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,58 @@ module.exports = React.createClass({
|
||||||
return s;
|
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) {
|
_repositionTooltip: function(e) {
|
||||||
if (this.tooltip && this.tooltip.parentElement) {
|
if (this.tooltip && this.tooltip.parentElement) {
|
||||||
var scroll = ReactDOM.findDOMNode(this);
|
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) { // 10px of vertical margin at top of screen
|
||||||
|
scroll.scrollTop = incomingCallBox.parentElement.offsetTop - 10;
|
||||||
|
}
|
||||||
|
else if (top > scroll.clientHeight - incomingCallBox.offsetHeight + 50) {
|
||||||
|
scroll.scrollTop = incomingCallBox.parentElement.offsetTop - scroll.offsetHeight + incomingCallBox.offsetHeight - 50;
|
||||||
|
}
|
||||||
|
// recalculate top in case we clipped it.
|
||||||
|
top = (scroll.offsetTop + incomingCallBox.parentElement.offsetTop - scroll.scrollTop);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// stop the box from scrolling off the screen
|
||||||
|
if (top < 10) {
|
||||||
|
top = 10;
|
||||||
|
}
|
||||||
|
else if (top > scroll.clientHeight - incomingCallBox.offsetHeight + 50) {
|
||||||
|
top = scroll.clientHeight - incomingCallBox.offsetHeight + 50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
incomingCallBox.style.top = top + "px";
|
||||||
|
incomingCallBox.style.left = scroll.offsetLeft + scroll.offsetWidth + "px";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -234,7 +298,7 @@ module.exports = React.createClass({
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GeminiScrollbar className="mx_RoomList_scrollbar" autoshow={true} onScroll={self._repositionTooltip}>
|
<GeminiScrollbar className="mx_RoomList_scrollbar" autoshow={true} onScroll={ self._repositionTooltips }>
|
||||||
<div className="mx_RoomList">
|
<div className="mx_RoomList">
|
||||||
{ expandButton }
|
{ expandButton }
|
||||||
|
|
||||||
|
@ -244,6 +308,7 @@ module.exports = React.createClass({
|
||||||
order="recent"
|
order="recent"
|
||||||
activityMap={ self.state.activityMap }
|
activityMap={ self.state.activityMap }
|
||||||
selectedRoom={ self.props.selectedRoom }
|
selectedRoom={ self.props.selectedRoom }
|
||||||
|
incomingCall={ self.state.incomingCall }
|
||||||
collapsed={ self.props.collapsed } />
|
collapsed={ self.props.collapsed } />
|
||||||
|
|
||||||
<RoomSubList list={ self.state.lists['m.favourite'] }
|
<RoomSubList list={ self.state.lists['m.favourite'] }
|
||||||
|
@ -254,6 +319,7 @@ module.exports = React.createClass({
|
||||||
order="manual"
|
order="manual"
|
||||||
activityMap={ self.state.activityMap }
|
activityMap={ self.state.activityMap }
|
||||||
selectedRoom={ self.props.selectedRoom }
|
selectedRoom={ self.props.selectedRoom }
|
||||||
|
incomingCall={ self.state.incomingCall }
|
||||||
collapsed={ self.props.collapsed } />
|
collapsed={ self.props.collapsed } />
|
||||||
|
|
||||||
<RoomSubList list={ self.state.lists['im.vector.fake.recent'] }
|
<RoomSubList list={ self.state.lists['im.vector.fake.recent'] }
|
||||||
|
@ -263,6 +329,7 @@ module.exports = React.createClass({
|
||||||
order="recent"
|
order="recent"
|
||||||
activityMap={ self.state.activityMap }
|
activityMap={ self.state.activityMap }
|
||||||
selectedRoom={ self.props.selectedRoom }
|
selectedRoom={ self.props.selectedRoom }
|
||||||
|
incomingCall={ self.state.incomingCall }
|
||||||
collapsed={ self.props.collapsed } />
|
collapsed={ self.props.collapsed } />
|
||||||
|
|
||||||
{ Object.keys(self.state.lists).map(function(tagName) {
|
{ Object.keys(self.state.lists).map(function(tagName) {
|
||||||
|
@ -276,6 +343,7 @@ module.exports = React.createClass({
|
||||||
order="manual"
|
order="manual"
|
||||||
activityMap={ self.state.activityMap }
|
activityMap={ self.state.activityMap }
|
||||||
selectedRoom={ self.props.selectedRoom }
|
selectedRoom={ self.props.selectedRoom }
|
||||||
|
incomingCall={ self.state.incomingCall }
|
||||||
collapsed={ self.props.collapsed } />
|
collapsed={ self.props.collapsed } />
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -290,6 +358,7 @@ module.exports = React.createClass({
|
||||||
bottommost={ self.state.lists['im.vector.fake.archived'].length === 0 }
|
bottommost={ self.state.lists['im.vector.fake.archived'].length === 0 }
|
||||||
activityMap={ self.state.activityMap }
|
activityMap={ self.state.activityMap }
|
||||||
selectedRoom={ self.props.selectedRoom }
|
selectedRoom={ self.props.selectedRoom }
|
||||||
|
incomingCall={ self.state.incomingCall }
|
||||||
collapsed={ self.props.collapsed } />
|
collapsed={ self.props.collapsed } />
|
||||||
|
|
||||||
<RoomSubList list={ self.state.lists['im.vector.fake.archived'] }
|
<RoomSubList list={ self.state.lists['im.vector.fake.archived'] }
|
||||||
|
@ -299,6 +368,7 @@ module.exports = React.createClass({
|
||||||
bottommost={ true }
|
bottommost={ true }
|
||||||
activityMap={ self.state.activityMap }
|
activityMap={ self.state.activityMap }
|
||||||
selectedRoom={ self.props.selectedRoom }
|
selectedRoom={ self.props.selectedRoom }
|
||||||
|
incomingCall={ self.state.incomingCall }
|
||||||
collapsed={ self.props.collapsed } />
|
collapsed={ self.props.collapsed } />
|
||||||
</div>
|
</div>
|
||||||
</GeminiScrollbar>
|
</GeminiScrollbar>
|
||||||
|
|
|
@ -38,6 +38,7 @@ module.exports = React.createClass({
|
||||||
highlight: React.PropTypes.bool.isRequired,
|
highlight: React.PropTypes.bool.isRequired,
|
||||||
isInvite: React.PropTypes.bool.isRequired,
|
isInvite: React.PropTypes.bool.isRequired,
|
||||||
roomSubList: React.PropTypes.object.isRequired,
|
roomSubList: React.PropTypes.object.isRequired,
|
||||||
|
incomingCall: React.PropTypes.object,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
@ -105,6 +106,12 @@ module.exports = React.createClass({
|
||||||
label = <RoomTooltip room={this.props.room}/>;
|
label = <RoomTooltip room={this.props.room}/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var incomingCallBox;
|
||||||
|
if (this.props.incomingCall) {
|
||||||
|
var IncomingCallBox = sdk.getComponent("voip.IncomingCallBox");
|
||||||
|
incomingCallBox = <IncomingCallBox incomingCall={ this.props.incomingCall }/>;
|
||||||
|
}
|
||||||
|
|
||||||
var RoomAvatar = sdk.getComponent('avatars.RoomAvatar');
|
var RoomAvatar = sdk.getComponent('avatars.RoomAvatar');
|
||||||
|
|
||||||
// These props are injected by React DnD,
|
// These props are injected by React DnD,
|
||||||
|
@ -120,6 +127,7 @@ module.exports = React.createClass({
|
||||||
{ badge }
|
{ badge }
|
||||||
</div>
|
</div>
|
||||||
{ label }
|
{ label }
|
||||||
|
{ incomingCallBox }
|
||||||
</div>
|
</div>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,19 +35,13 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
this._trackedRoom = null;
|
|
||||||
if (this.props.room) {
|
if (this.props.room) {
|
||||||
this._trackedRoom = this.props.room;
|
this.showCall(this.props.room.roomId);
|
||||||
this.showCall(this._trackedRoom.roomId);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// XXX: why would we ever not have a this.props.room?
|
||||||
var call = CallHandler.getAnyActiveCall();
|
var call = CallHandler.getAnyActiveCall();
|
||||||
if (call) {
|
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);
|
this.showCall(call.roomId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +75,7 @@ module.exports = React.createClass({
|
||||||
// and for the voice stream of screen captures
|
// and for the voice stream of screen captures
|
||||||
call.setRemoteAudioElement(this.getVideoView().getRemoteAudioElement());
|
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
|
// if this call is a conf call, don't display local video as the
|
||||||
// conference will have us in it
|
// conference will have us in it
|
||||||
this.getVideoView().getLocalVideoElement().style.display = (
|
this.getVideoView().getLocalVideoElement().style.display = (
|
||||||
|
|
|
@ -21,87 +21,29 @@ var CallHandler = require("../../../CallHandler");
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'IncomingCallBox',
|
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() {
|
onAnswerClick: function() {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'answer',
|
action: 'answer',
|
||||||
room_id: this.state.incomingCall.roomId
|
room_id: this.props.incomingCall.roomId
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onRejectClick: function() {
|
onRejectClick: function() {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'hangup',
|
action: 'hangup',
|
||||||
room_id: this.state.incomingCall.roomId
|
room_id: this.props.incomingCall.roomId
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getRingAudio: function() {
|
|
||||||
return this.refs.ringAudio;
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
// NB: This block MUST have a "key" so React doesn't clobber the elements
|
|
||||||
// between in-call / not-in-call.
|
|
||||||
var audioBlock = (
|
|
||||||
<audio ref="ringAudio" key="voip_ring_audio" loop>
|
|
||||||
<source src="media/ring.ogg" type="audio/ogg" />
|
|
||||||
<source src="media/ring.mp3" type="audio/mpeg" />
|
|
||||||
</audio>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!this.state.incomingCall || !this.state.incomingCall.roomId) {
|
var room = this.props.incomingCall ? MatrixClientPeg.get().getRoom(this.props.incomingCall.roomId) : null;
|
||||||
return (
|
var caller = room ? room.name : "unknown";
|
||||||
<div>
|
|
||||||
{audioBlock}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
var caller = MatrixClientPeg.get().getRoom(this.state.incomingCall.roomId).name;
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_IncomingCallBox">
|
<div className="mx_IncomingCallBox" id="incomingCallBox">
|
||||||
{audioBlock}
|
|
||||||
<img className="mx_IncomingCallBox_chevron" src="img/chevron-left.png" width="9" height="16" />
|
<img className="mx_IncomingCallBox_chevron" src="img/chevron-left.png" width="9" height="16" />
|
||||||
<div className="mx_IncomingCallBox_title">
|
<div className="mx_IncomingCallBox_title">
|
||||||
Incoming { this.state.incomingCall ? this.state.incomingCall.type : '' } call from { caller }
|
Incoming { this.props.incomingCall ? this.props.incomingCall.type : '' } call from { caller }
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_IncomingCallBox_buttons">
|
<div className="mx_IncomingCallBox_buttons">
|
||||||
<div className="mx_IncomingCallBox_buttons_cell">
|
<div className="mx_IncomingCallBox_buttons_cell">
|
||||||
|
|
Loading…
Reference in a new issue