Merge branch 'develop' into kegan/archived-rooms

This commit is contained in:
Kegan Dougal 2015-12-18 16:59:25 +00:00
commit a2872deb53
9 changed files with 258 additions and 205 deletions

View file

@ -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;
} }

View file

@ -1,3 +1,19 @@
/*
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 MatrixClientPeg = require('./MatrixClientPeg'); var MatrixClientPeg = require('./MatrixClientPeg');
var dis = require('./dispatcher'); var dis = require('./dispatcher');

View file

@ -78,6 +78,10 @@ module.exports = React.createClass({
componentWillUnmount: function() { componentWillUnmount: function() {
if (this.refs.messagePanel) { if (this.refs.messagePanel) {
// disconnect the D&D event listeners from the message panel. This
// is really just for hygiene - the messagePanel is going to be
// deleted anyway, so it doesn't matter if the event listeners
// don't get cleaned up.
var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel); var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel);
messagePanel.removeEventListener('drop', this.onDrop); messagePanel.removeEventListener('drop', this.onDrop);
messagePanel.removeEventListener('dragover', this.onDragOver); messagePanel.removeEventListener('dragover', this.onDragOver);
@ -285,16 +289,7 @@ module.exports = React.createClass({
componentDidMount: function() { componentDidMount: function() {
if (this.refs.messagePanel) { if (this.refs.messagePanel) {
var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel); this._initialiseMessagePanel();
messagePanel.addEventListener('drop', this.onDrop);
messagePanel.addEventListener('dragover', this.onDragOver);
messagePanel.addEventListener('dragleave', this.onDragLeaveOrEnd);
messagePanel.addEventListener('dragend', this.onDragLeaveOrEnd);
this.scrollToBottom();
this.sendReadReceipt();
this.fillSpace();
} }
var call = CallHandler.getCallForRoom(this.props.roomId); var call = CallHandler.getCallForRoom(this.props.roomId);
@ -309,23 +304,37 @@ module.exports = React.createClass({
this.onResize(); this.onResize();
}, },
_initialiseMessagePanel: function() {
var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel);
this.refs.messagePanel.initialised = true;
messagePanel.addEventListener('drop', this.onDrop);
messagePanel.addEventListener('dragover', this.onDragOver);
messagePanel.addEventListener('dragleave', this.onDragLeaveOrEnd);
messagePanel.addEventListener('dragend', this.onDragLeaveOrEnd);
this.scrollToBottom();
this.sendReadReceipt();
this.fillSpace();
},
componentDidUpdate: function() { componentDidUpdate: function() {
// we need to initialise the messagepanel if we've just joined the
// room. TODO: we really really ought to factor out messagepanel to a
// separate component to avoid this ridiculous dance.
if (!this.refs.messagePanel) return;
if (!this.refs.messagePanel.initialised) {
this._initialiseMessagePanel();
}
// after adding event tiles, we may need to tweak the scroll (either to // after adding event tiles, we may need to tweak the scroll (either to
// keep at the bottom of the timeline, or to maintain the view after // keep at the bottom of the timeline, or to maintain the view after
// adding events to the top). // adding events to the top).
if (!this.refs.messagePanel) return;
if (this.state.searchResults) return; if (this.state.searchResults) return;
if (this.needsScrollReset) { this._restoreSavedScrollState();
if (DEBUG_SCROLL) console.log("Resetting scroll position after tile count change");
this._restoreSavedScrollState();
this.needsScrollReset = false;
}
// have to fill space in case we're accepting an invite
if (!this.state.paginating) this.fillSpace();
}, },
_paginateCompleted: function() { _paginateCompleted: function() {
@ -481,75 +490,65 @@ module.exports = React.createClass({
}, },
onSearch: function(term, scope) { onSearch: function(term, scope) {
var filter; this.setState({
if (scope === "Room") { searchTerm: term,
filter = { searchScope: scope,
// XXX: it's unintuitive that the filter for searching doesn't have the same shape as the v2 filter API :( searchResults: [],
rooms: [ searchHighlights: [],
this.props.roomId searchCount: null,
]
};
}
var self = this;
self.setState({
searchInProgress: true
}); });
MatrixClientPeg.get().search({ this._getSearchBatch(term, scope);
body: { },
search_categories: {
room_events: {
search_term: term,
filter: filter,
order_by: "recent",
include_state: true,
groupings: {
group_by: [
{
key: "room_id"
}
]
},
event_context: {
before_limit: 1,
after_limit: 1,
include_profile: true,
}
}
}
}
}).then(function(data) {
if (!self.state.searching || term !== self.refs.search_bar.refs.search_term.value) { // fire off a request for a batch of search results
_getSearchBatch: function(term, scope) {
this.setState({
searchInProgress: true,
});
// make sure that we don't end up merging results from
// different searches by keeping a unique id.
//
// todo: should cancel any previous search requests.
var searchId = this.searchId = new Date().getTime();
var self = this;
MatrixClientPeg.get().search({ body: this._getSearchCondition(term, scope) })
.then(function(data) {
if (!self.state.searching || self.searchId != searchId) {
console.error("Discarding stale search results"); console.error("Discarding stale search results");
return; return;
} }
// for debugging: var results = data.search_categories.room_events;
// data.search_categories.room_events.highlights = ["hello", "everybody"];
var highlights; // postgres on synapse returns us precise details of the
if (data.search_categories.room_events.highlights && // strings which actually got matched for highlighting.
data.search_categories.room_events.highlights.length > 0)
{ // combine the highlight list with our existing list; build an object
// postgres on synapse returns us precise details of the // to avoid O(N^2) fail
// strings which actually got matched for highlighting. var highlights = {};
// for overlapping highlights, favour longer (more specific) terms first results.highlights.forEach(function(hl) { highlights[hl] = 1; });
highlights = data.search_categories.room_events.highlights self.state.searchHighlights.forEach(function(hl) { highlights[hl] = 1; });
.sort(function(a, b) { b.length - a.length });
} // turn it back into an ordered list. For overlapping highlights,
else { // favour longer (more specific) terms first
// sqlite doesn't, so just try to highlight the literal search term highlights = Object.keys(highlights).sort(function(a, b) { b.length - a.length });
// sqlite doesn't give us any highlights, so just try to highlight the literal search term
if (highlights.length == 0) {
highlights = [ term ]; highlights = [ term ];
} }
// append the new results to our existing results
var events = self.state.searchResults.concat(results.results);
self.setState({ self.setState({
highlights: highlights, searchHighlights: highlights,
searchTerm: term, searchResults: events,
searchResults: data, searchCount: results.count,
searchScope: scope,
searchCount: data.search_categories.room_events.count,
}); });
}, function(error) { }, function(error) {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
@ -561,7 +560,35 @@ module.exports = React.createClass({
self.setState({ self.setState({
searchInProgress: false searchInProgress: false
}); });
}); }).done();
},
_getSearchCondition: function(term, scope) {
var filter;
if (scope === "Room") {
filter = {
// XXX: it's unintuitive that the filter for searching doesn't have the same shape as the v2 filter API :(
rooms: [
this.props.roomId
]
};
}
return {
search_categories: {
room_events: {
search_term: term,
filter: filter,
order_by: "recent",
event_context: {
before_limit: 1,
after_limit: 1,
include_profile: true,
}
}
}
}
}, },
getEventTiles: function() { getEventTiles: function() {
@ -576,57 +603,44 @@ module.exports = React.createClass({
if (this.state.searchResults) if (this.state.searchResults)
{ {
if (!this.state.searchResults.search_categories.room_events.results || // XXX: todo: merge overlapping results somehow?
!this.state.searchResults.search_categories.room_events.groups) // XXX: why doesn't searching on name work?
{
return ret;
}
// XXX: this dance is foul, due to the results API not directly returning sorted results var lastRoomId;
var results = this.state.searchResults.search_categories.room_events.results;
var roomIdGroups = this.state.searchResults.search_categories.room_events.groups.room_id;
if (Array.isArray(results)) { for (var i = this.state.searchResults.length - 1; i >= 0; i--) {
// Old search API used to return results as a event_id -> result dict, but now var result = this.state.searchResults[i];
// returns a straightforward list. var mxEv = new Matrix.MatrixEvent(result.result);
results = results.reduce(function(prev, curr) {
prev[curr.result.event_id] = curr;
return prev;
}, {});
}
Object.keys(roomIdGroups)
.sort(function(a, b) { roomIdGroups[a].order - roomIdGroups[b].order }) // WHY NOT RETURN AN ORDERED ARRAY?!?!?!
.forEach(function(roomId)
{
// XXX: todo: merge overlapping results somehow?
// XXX: why doesn't searching on name work?
if (self.state.searchScope === 'All') { if (self.state.searchScope === 'All') {
ret.push(<li key={ roomId }><h1>Room: { cli.getRoom(roomId).name }</h1></li>); var roomId = result.result.room_id;
if(roomId != lastRoomId) {
ret.push(<li key={mxEv.getId() + "-room"}><h1>Room: { cli.getRoom(roomId).name }</h1></li>);
lastRoomId = roomId;
}
} }
var resultList = roomIdGroups[roomId].results.map(function(eventId) { return results[eventId]; }); var ts1 = result.result.origin_server_ts;
for (var i = resultList.length - 1; i >= 0; i--) { ret.push(<li key={ts1 + "-search"}><DateSeparator ts={ts1}/></li>); // Rank: {resultList[i].rank}
var ts1 = resultList[i].result.origin_server_ts;
ret.push(<li key={ts1 + "-search"}><DateSeparator ts={ts1}/></li>); // Rank: {resultList[i].rank} if (result.context.events_before[0]) {
var mxEv = new Matrix.MatrixEvent(resultList[i].result); var mxEv2 = new Matrix.MatrixEvent(result.context.events_before[0]);
if (resultList[i].context.events_before[0]) { if (EventTile.haveTileForEvent(mxEv2)) {
var mxEv2 = new Matrix.MatrixEvent(resultList[i].context.events_before[0]); ret.push(<li key={mxEv.getId() + "-1"}><EventTile mxEvent={mxEv2} contextual={true} /></li>);
if (EventTile.haveTileForEvent(mxEv2)) {
ret.push(<li key={mxEv.getId() + "-1"}><EventTile mxEvent={mxEv2} contextual={true} /></li>);
}
}
if (EventTile.haveTileForEvent(mxEv)) {
ret.push(<li key={mxEv.getId() + "+0"}><EventTile mxEvent={mxEv} highlights={self.state.highlights}/></li>);
}
if (resultList[i].context.events_after[0]) {
var mxEv2 = new Matrix.MatrixEvent(resultList[i].context.events_after[0]);
if (EventTile.haveTileForEvent(mxEv2)) {
ret.push(<li key={mxEv.getId() + "+1"}><EventTile mxEvent={mxEv2} contextual={true} /></li>);
}
} }
} }
});
if (EventTile.haveTileForEvent(mxEv)) {
ret.push(<li key={mxEv.getId() + "+0"}><EventTile mxEvent={mxEv} highlights={self.state.searchHighlights}/></li>);
}
if (result.context.events_after[0]) {
var mxEv2 = new Matrix.MatrixEvent(result.context.events_after[0]);
if (EventTile.haveTileForEvent(mxEv2)) {
ret.push(<li key={mxEv.getId() + "+1"}><EventTile mxEvent={mxEv2} contextual={true} /></li>);
}
}
}
return ret; return ret;
} }
@ -683,10 +697,6 @@ module.exports = React.createClass({
} }
++count; ++count;
} }
if (count != this.lastEventTileCount) {
if (DEBUG_SCROLL) console.log("Queuing scroll reset (event count changed; now "+count+"; was "+this.lastEventTileCount+")");
this.needsScrollReset = true;
}
this.lastEventTileCount = count; this.lastEventTileCount = count;
return ret; return ret;
}, },
@ -1282,8 +1292,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;

View file

@ -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}>

View file

@ -103,7 +103,9 @@ module.exports = React.createClass({
// <EditableText label={this.props.room.name} initialValue={actual_name} placeHolder="Name" onValueChanged={this.onNameChange} /> // <EditableText label={this.props.room.name} initialValue={actual_name} placeHolder="Name" onValueChanged={this.onNameChange} />
var searchStatus; var searchStatus;
if (this.props.searchInfo && this.props.searchInfo.searchTerm) { // don't display the search count until the search completes and
// gives us a non-null searchCount.
if (this.props.searchInfo && this.props.searchInfo.searchCount !== null) {
searchStatus = <div className="mx_RoomHeader_searchStatus">&nbsp;({ this.props.searchInfo.searchCount } results)</div>; searchStatus = <div className="mx_RoomHeader_searchStatus">&nbsp;({ this.props.searchInfo.searchCount } results)</div>;
} }

View file

@ -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");
@ -40,6 +41,7 @@ module.exports = React.createClass({
activityMap: null, activityMap: null,
isLoadingLeftRooms: false, isLoadingLeftRooms: false,
lists: {}, lists: {},
incomingCall: null,
} }
}, },
@ -68,7 +70,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;
} }
}, },
@ -272,10 +288,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";
} }
}, },
@ -294,7 +358,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 }
@ -304,6 +368,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'] }
@ -314,6 +379,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'] }
@ -323,6 +389,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) {
@ -336,6 +403,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 } />
} }
@ -350,6 +418,7 @@ module.exports = React.createClass({
bottommost={ false } bottommost={ false }
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'] }
@ -363,7 +432,8 @@ module.exports = React.createClass({
alwaysShowHeader={ true } alwaysShowHeader={ true }
startAsHidden={ true } startAsHidden={ true }
showSpinner={ self.state.isLoadingLeftRooms } showSpinner={ self.state.isLoadingLeftRooms }
onHeaderClick= { self.onArchivedHeaderClick } /> onHeaderClick= { self.onArchivedHeaderClick }
incomingCall={ self.state.incomingCall } />
</div> </div>
</GeminiScrollbar> </GeminiScrollbar>
); );

View file

@ -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>
)); ));
} }

View file

@ -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 = (

View file

@ -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">