Merge branch 'develop' into kegan/archived-rooms
commit
a2872deb53
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 dis = require('./dispatcher');
|
||||
|
||||
|
|
|
@ -78,6 +78,10 @@ module.exports = React.createClass({
|
|||
|
||||
componentWillUnmount: function() {
|
||||
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);
|
||||
messagePanel.removeEventListener('drop', this.onDrop);
|
||||
messagePanel.removeEventListener('dragover', this.onDragOver);
|
||||
|
@ -285,16 +289,7 @@ module.exports = React.createClass({
|
|||
|
||||
componentDidMount: function() {
|
||||
if (this.refs.messagePanel) {
|
||||
var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel);
|
||||
|
||||
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();
|
||||
this._initialiseMessagePanel();
|
||||
}
|
||||
|
||||
var call = CallHandler.getCallForRoom(this.props.roomId);
|
||||
|
@ -309,23 +304,37 @@ module.exports = React.createClass({
|
|||
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() {
|
||||
// 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
|
||||
// keep at the bottom of the timeline, or to maintain the view after
|
||||
// adding events to the top).
|
||||
|
||||
if (!this.refs.messagePanel) return;
|
||||
|
||||
if (this.state.searchResults) return;
|
||||
|
||||
if (this.needsScrollReset) {
|
||||
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();
|
||||
this._restoreSavedScrollState();
|
||||
},
|
||||
|
||||
_paginateCompleted: function() {
|
||||
|
@ -481,75 +490,65 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onSearch: 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
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
var self = this;
|
||||
self.setState({
|
||||
searchInProgress: true
|
||||
this.setState({
|
||||
searchTerm: term,
|
||||
searchScope: scope,
|
||||
searchResults: [],
|
||||
searchHighlights: [],
|
||||
searchCount: null,
|
||||
});
|
||||
|
||||
MatrixClientPeg.get().search({
|
||||
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) {
|
||||
this._getSearchBatch(term, scope);
|
||||
},
|
||||
|
||||
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");
|
||||
return;
|
||||
}
|
||||
|
||||
// for debugging:
|
||||
// data.search_categories.room_events.highlights = ["hello", "everybody"];
|
||||
var results = data.search_categories.room_events;
|
||||
|
||||
var highlights;
|
||||
if (data.search_categories.room_events.highlights &&
|
||||
data.search_categories.room_events.highlights.length > 0)
|
||||
{
|
||||
// postgres on synapse returns us precise details of the
|
||||
// strings which actually got matched for highlighting.
|
||||
// for overlapping highlights, favour longer (more specific) terms first
|
||||
highlights = data.search_categories.room_events.highlights
|
||||
.sort(function(a, b) { b.length - a.length });
|
||||
}
|
||||
else {
|
||||
// sqlite doesn't, so just try to highlight the literal search term
|
||||
// postgres on synapse returns us precise details of the
|
||||
// strings which actually got matched for highlighting.
|
||||
|
||||
// combine the highlight list with our existing list; build an object
|
||||
// to avoid O(N^2) fail
|
||||
var highlights = {};
|
||||
results.highlights.forEach(function(hl) { highlights[hl] = 1; });
|
||||
self.state.searchHighlights.forEach(function(hl) { highlights[hl] = 1; });
|
||||
|
||||
// turn it back into an ordered list. For overlapping highlights,
|
||||
// favour longer (more specific) terms first
|
||||
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 ];
|
||||
}
|
||||
|
||||
// append the new results to our existing results
|
||||
var events = self.state.searchResults.concat(results.results);
|
||||
|
||||
self.setState({
|
||||
highlights: highlights,
|
||||
searchTerm: term,
|
||||
searchResults: data,
|
||||
searchScope: scope,
|
||||
searchCount: data.search_categories.room_events.count,
|
||||
searchHighlights: highlights,
|
||||
searchResults: events,
|
||||
searchCount: results.count,
|
||||
});
|
||||
}, function(error) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
|
@ -561,7 +560,35 @@ module.exports = React.createClass({
|
|||
self.setState({
|
||||
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() {
|
||||
|
@ -576,57 +603,44 @@ module.exports = React.createClass({
|
|||
|
||||
if (this.state.searchResults)
|
||||
{
|
||||
if (!this.state.searchResults.search_categories.room_events.results ||
|
||||
!this.state.searchResults.search_categories.room_events.groups)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
// XXX: todo: merge overlapping results somehow?
|
||||
// XXX: why doesn't searching on name work?
|
||||
|
||||
// XXX: this dance is foul, due to the results API not directly returning sorted results
|
||||
var results = this.state.searchResults.search_categories.room_events.results;
|
||||
var roomIdGroups = this.state.searchResults.search_categories.room_events.groups.room_id;
|
||||
var lastRoomId;
|
||||
|
||||
if (Array.isArray(results)) {
|
||||
// Old search API used to return results as a event_id -> result dict, but now
|
||||
// returns a straightforward list.
|
||||
results = results.reduce(function(prev, curr) {
|
||||
prev[curr.result.event_id] = curr;
|
||||
return prev;
|
||||
}, {});
|
||||
}
|
||||
for (var i = this.state.searchResults.length - 1; i >= 0; i--) {
|
||||
var result = this.state.searchResults[i];
|
||||
var mxEv = new Matrix.MatrixEvent(result.result);
|
||||
|
||||
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') {
|
||||
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]; });
|
||||
for (var i = resultList.length - 1; i >= 0; i--) {
|
||||
var ts1 = resultList[i].result.origin_server_ts;
|
||||
ret.push(<li key={ts1 + "-search"}><DateSeparator ts={ts1}/></li>); // Rank: {resultList[i].rank}
|
||||
var mxEv = new Matrix.MatrixEvent(resultList[i].result);
|
||||
if (resultList[i].context.events_before[0]) {
|
||||
var mxEv2 = new Matrix.MatrixEvent(resultList[i].context.events_before[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.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>);
|
||||
}
|
||||
var ts1 = result.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 mxEv2 = new Matrix.MatrixEvent(result.context.events_before[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;
|
||||
}
|
||||
|
||||
|
@ -683,10 +697,6 @@ module.exports = React.createClass({
|
|||
}
|
||||
++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;
|
||||
return ret;
|
||||
},
|
||||
|
@ -1282,8 +1292,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;
|
||||
|
||||
|
|
|
@ -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 =
|
||||
<div className="mx_MessageComposer_hangup" onClick={this.onHangupClick}>
|
||||
|
|
|
@ -103,7 +103,9 @@ module.exports = React.createClass({
|
|||
// <EditableText label={this.props.room.name} initialValue={actual_name} placeHolder="Name" onValueChanged={this.onNameChange} />
|
||||
|
||||
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"> ({ this.props.searchInfo.searchCount } results)</div>;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
@ -40,6 +41,7 @@ module.exports = React.createClass({
|
|||
activityMap: null,
|
||||
isLoadingLeftRooms: false,
|
||||
lists: {},
|
||||
incomingCall: null,
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -68,7 +70,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;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -272,10 +288,58 @@ 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) { // 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;
|
||||
|
||||
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">
|
||||
{ expandButton }
|
||||
|
||||
|
@ -304,6 +368,7 @@ module.exports = React.createClass({
|
|||
order="recent"
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
||||
<RoomSubList list={ self.state.lists['m.favourite'] }
|
||||
|
@ -314,6 +379,7 @@ module.exports = React.createClass({
|
|||
order="manual"
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
||||
<RoomSubList list={ self.state.lists['im.vector.fake.recent'] }
|
||||
|
@ -323,6 +389,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) {
|
||||
|
@ -336,6 +403,7 @@ module.exports = React.createClass({
|
|||
order="manual"
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
||||
}
|
||||
|
@ -350,6 +418,7 @@ module.exports = React.createClass({
|
|||
bottommost={ false }
|
||||
activityMap={ self.state.activityMap }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed } />
|
||||
|
||||
<RoomSubList list={ self.state.lists['im.vector.fake.archived'] }
|
||||
|
@ -363,7 +432,8 @@ module.exports = React.createClass({
|
|||
alwaysShowHeader={ true }
|
||||
startAsHidden={ true }
|
||||
showSpinner={ self.state.isLoadingLeftRooms }
|
||||
onHeaderClick= { self.onArchivedHeaderClick } />
|
||||
onHeaderClick= { self.onArchivedHeaderClick }
|
||||
incomingCall={ self.state.incomingCall } />
|
||||
</div>
|
||||
</GeminiScrollbar>
|
||||
);
|
||||
|
|
|
@ -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 = <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');
|
||||
|
||||
// These props are injected by React DnD,
|
||||
|
@ -120,6 +127,7 @@ module.exports = React.createClass({
|
|||
{ badge }
|
||||
</div>
|
||||
{ label }
|
||||
{ incomingCallBox }
|
||||
</div>
|
||||
));
|
||||
}
|
||||
|
|
|
@ -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 = (
|
||||
|
|
|
@ -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 = (
|
||||
<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) {
|
||||
return (
|
||||
<div>
|
||||
{audioBlock}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
var caller = MatrixClientPeg.get().getRoom(this.state.incomingCall.roomId).name;
|
||||
var room = this.props.incomingCall ? MatrixClientPeg.get().getRoom(this.props.incomingCall.roomId) : null;
|
||||
var caller = room ? room.name : "unknown";
|
||||
return (
|
||||
<div className="mx_IncomingCallBox">
|
||||
{audioBlock}
|
||||
<div className="mx_IncomingCallBox" id="incomingCallBox">
|
||||
<img className="mx_IncomingCallBox_chevron" src="img/chevron-left.png" width="9" height="16" />
|
||||
<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 className="mx_IncomingCallBox_buttons">
|
||||
<div className="mx_IncomingCallBox_buttons_cell">
|
||||
|
|
Loading…
Reference in New Issue