diff --git a/src/component-index.js b/src/component-index.js
index 9b7a1e8751..869d60f204 100644
--- a/src/component-index.js
+++ b/src/component-index.js
@@ -72,6 +72,7 @@ module.exports.components['views.messages.TextualEvent'] = require('./components
module.exports.components['views.messages.UnknownBody'] = require('./components/views/messages/UnknownBody');
module.exports.components['views.room_settings.AliasSettings'] = require('./components/views/room_settings/AliasSettings');
module.exports.components['views.room_settings.ColorSettings'] = require('./components/views/room_settings/ColorSettings');
+module.exports.components['views.rooms.AuxPanel'] = require('./components/views/rooms/AuxPanel');
module.exports.components['views.rooms.EntityTile'] = require('./components/views/rooms/EntityTile');
module.exports.components['views.rooms.EventTile'] = require('./components/views/rooms/EventTile');
module.exports.components['views.rooms.InviteMemberList'] = require('./components/views/rooms/InviteMemberList');
diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js
index 2e0897e3d0..9f338d5fcd 100644
--- a/src/components/structures/RoomStatusBar.js
+++ b/src/components/structures/RoomStatusBar.js
@@ -61,11 +61,13 @@ module.exports = React.createClass({
getInitialState: function() {
return {
syncState: MatrixClientPeg.get().getSyncState(),
+ whoisTypingString: WhoIsTyping.whoIsTypingString(this.props.room),
};
},
componentWillMount: function() {
MatrixClientPeg.get().on("sync", this.onSyncStateChange);
+ MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping);
},
componentDidUpdate: function(prevProps, prevState) {
@@ -76,8 +78,10 @@ module.exports = React.createClass({
componentWillUnmount: function() {
// we may have entirely lost our client as we're logging out before clicking login on the guest bar...
- if (MatrixClientPeg.get()) {
- MatrixClientPeg.get().removeListener("sync", this.onSyncStateChange);
+ var client = MatrixClientPeg.get();
+ if (client) {
+ client.removeListener("sync", this.onSyncStateChange);
+ client.removeListener("RoomMember.typing", this.onRoomMemberTyping);
}
},
@@ -90,6 +94,12 @@ module.exports = React.createClass({
});
},
+ onRoomMemberTyping: function(ev, member) {
+ this.setState({
+ whoisTypingString: WhoIsTyping.whoIsTypingString(this.props.room),
+ });
+ },
+
// determine if we need to call onResize
_checkForResize: function(prevProps, prevState) {
// figure out the old height and the new height of the status bar. We
@@ -235,7 +245,7 @@ module.exports = React.createClass({
);
}
- var typingString = WhoIsTyping.whoIsTypingString(this.props.room);
+ var typingString = this.state.whoisTypingString;
if (typingString) {
return (
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index dbd4e3f0aa..e774121153 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -15,7 +15,6 @@ limitations under the License.
*/
// TODO: This component is enormous! There's several things which could stand-alone:
-// - Aux component
// - Search results component
// - Drag and drop
// - File uploading - uploadFile()
@@ -92,6 +91,8 @@ module.exports = React.createClass({
atEndOfLiveTimeline: true,
showTopUnreadMessagesBar: false,
+
+ auxPanelMaxHeight: undefined,
}
},
@@ -101,7 +102,6 @@ module.exports = React.createClass({
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
MatrixClientPeg.get().on("Room.name", this.onRoomName);
MatrixClientPeg.get().on("Room.accountData", this.onRoomAccountData);
- MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping);
MatrixClientPeg.get().on("RoomState.members", this.onRoomStateMember);
// xchat-style tab complete, add a colon if tab
// completing at the start of the text
@@ -173,7 +173,6 @@ module.exports = React.createClass({
MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline);
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
MatrixClientPeg.get().removeListener("Room.accountData", this.onRoomAccountData);
- MatrixClientPeg.get().removeListener("RoomMember.typing", this.onRoomMemberTyping);
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
}
@@ -342,10 +341,6 @@ module.exports = React.createClass({
}
},
- onRoomMemberTyping: function(ev, member) {
- this.forceUpdate();
- },
-
onRoomStateMember: function(ev, state, member) {
if (member.roomId === this.props.roomId) {
// a member state changed in this room, refresh the tab complete list
@@ -878,14 +873,6 @@ module.exports = React.createClass({
});
},
- onConferenceNotificationClick: function() {
- dis.dispatch({
- action: 'place_call',
- type: "video",
- room_id: this.props.roomId
- });
- },
-
// jump down to the bottom of this room, where new events are arriving
jumpToLiveTimeline: function() {
this.refs.messagePanel.jumpToLiveTimeline();
@@ -983,25 +970,10 @@ module.exports = React.createClass({
// but it's better than the video going missing entirely
if (auxPanelMaxHeight < 50) auxPanelMaxHeight = 50;
- if (this.refs.callView) {
- var fullscreenElement =
- (document.fullscreenElement ||
- document.mozFullScreenElement ||
- document.webkitFullscreenElement);
- if (!fullscreenElement) {
- var video = this.refs.callView.getVideoView().getRemoteVideoElement();
- video.style.maxHeight = auxPanelMaxHeight + "px";
- }
- }
-
- // we need to do this for general auxPanels too
- if (this.refs.auxPanel) {
- this.refs.auxPanel.style.maxHeight = auxPanelMaxHeight + "px";
- }
-
- // the above might have made the aux panel resize itself, so now
- // we need to tell the gemini panel to adapt.
- this.onChildResize();
+ // we may need to resize the gemini panel after changing the aux panel
+ // size, so add a callback to onChildResize.
+ this.setState({auxPanelMaxHeight: auxPanelMaxHeight},
+ this.onChildResize);
},
onFullscreenClick: function() {
@@ -1035,11 +1007,6 @@ module.exports = React.createClass({
});
},
- onCallViewResize: function() {
- this.onChildResize();
- this.onResize();
- },
-
onChildResize: function() {
// When the video, status bar, or the message composer resizes, the
// scroll panel also changes size. Work around GeminiScrollBar fail by
@@ -1062,8 +1029,8 @@ module.exports = React.createClass({
render: function() {
var RoomHeader = sdk.getComponent('rooms.RoomHeader');
var MessageComposer = sdk.getComponent('rooms.MessageComposer');
- var CallView = sdk.getComponent("voip.CallView");
var RoomSettings = sdk.getComponent("rooms.RoomSettings");
+ var AuxPanel = sdk.getComponent("rooms.AuxPanel");
var SearchBar = sdk.getComponent("rooms.SearchBar");
var ScrollPanel = sdk.getComponent("structures.ScrollPanel");
var TintableSvg = sdk.getComponent("elements.TintableSvg");
@@ -1206,28 +1173,16 @@ module.exports = React.createClass({
);
}
- var conferenceCallNotification = null;
- if (this.state.displayConfCallNotification) {
- var supportedText;
- if (!MatrixClientPeg.get().supportsVoip()) {
- supportedText = " (unsupported)";
- }
- conferenceCallNotification = (
-
- Ongoing conference call {supportedText}
-
- );
- }
-
- var fileDropTarget = null;
- if (this.state.draggingFile) {
- fileDropTarget =
-
-
- Drop file here to upload
-
-
;
- }
+ var auxPanel = (
+
+ { aux }
+
+ );
var messageComposer, searchInfo;
var canSpeak = (
@@ -1345,13 +1300,7 @@ module.exports = React.createClass({
onLeaveClick={
(myMember && myMember.membership === "join") ? this.onLeaveClick : null
} />
-
- { fileDropTarget }
-
- { conferenceCallNotification }
- { aux }
-
+ { auxPanel }
{ topUnreadMessagesBar }
{ messagePanel }
{ searchResultsPanel }
diff --git a/src/components/views/rooms/AuxPanel.js b/src/components/views/rooms/AuxPanel.js
new file mode 100644
index 0000000000..49c7dfc6e5
--- /dev/null
+++ b/src/components/views/rooms/AuxPanel.js
@@ -0,0 +1,104 @@
+/*
+Copyright 2015, 2016 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 sdk = require('../../../index');
+var dis = require("../../../dispatcher");
+
+module.exports = React.createClass({
+ displayName: 'AuxPanel',
+
+ propTypes: {
+ // js-sdk room object
+ room: React.PropTypes.object.isRequired,
+
+ // Conference Handler implementation
+ conferenceHandler: React.PropTypes.object,
+
+ // set to true to show the file drop target
+ draggingFile: React.PropTypes.bool,
+
+ // set to true to show the 'active conf call' banner
+ displayConfCallNotification: React.PropTypes.bool,
+
+ // maxHeight attribute for the aux panel and the video
+ // therein
+ maxHeight: React.PropTypes.number,
+
+ // a callback which is called when the video element in a voip call is
+ // resized due to a change in video metadata
+ onCallViewVideoResize: React.PropTypes.func,
+ },
+
+ onConferenceNotificationClick: function() {
+ dis.dispatch({
+ action: 'place_call',
+ type: "video",
+ room_id: this.props.room.roomId,
+ });
+ },
+
+ render: function() {
+ var CallView = sdk.getComponent("voip.CallView");
+ var TintableSvg = sdk.getComponent("elements.TintableSvg");
+
+ var fileDropTarget = null;
+ if (this.props.draggingFile) {
+ fileDropTarget = (
+
+
+
+
+ Drop file here to upload
+
+
+ );
+ }
+
+ var conferenceCallNotification = null;
+ if (this.props.displayConfCallNotification) {
+ var supportedText;
+ if (!MatrixClientPeg.get().supportsVoip()) {
+ supportedText = " (unsupported)";
+ }
+ conferenceCallNotification = (
+
+ Ongoing conference call {supportedText}
+
+ );
+ }
+
+ var callView = (
+
+ );
+
+ return (
+
+ { fileDropTarget }
+ { callView }
+ { conferenceCallNotification }
+ { this.props.children }
+
+ );
+ },
+});
diff --git a/src/components/views/voip/CallView.js b/src/components/views/voip/CallView.js
index 5958c2b278..13abcb7f9d 100644
--- a/src/components/views/voip/CallView.js
+++ b/src/components/views/voip/CallView.js
@@ -19,38 +19,32 @@ var CallHandler = require("../../../CallHandler");
var sdk = require('../../../index');
var MatrixClientPeg = require("../../../MatrixClientPeg");
-/*
- * State vars:
- * this.state.call = MatrixCall|null
- *
- * 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 = React.createClass({
displayName: 'CallView',
propTypes: {
- // a callback which is called when the video within the callview
- // due to a change in video metadata
+ // js-sdk room object
+ room: React.PropTypes.object.isRequired,
+
+ // A Conference Handler implementation
+ // Must have a function signature:
+ // getConferenceCallForRoom(roomId: string): MatrixCall
+ ConferenceHandler: React.PropTypes.object,
+
+ // maxHeight style attribute for the video panel
+ maxVideoHeight: React.PropTypes.number,
+
+ // a callback which is called when the user clicks on the video div
+ onClick: React.PropTypes.func,
+
+ // a callback which is called when the video within the callview is
+ // resized due to a change in video metadata
onResize: React.PropTypes.func,
},
componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction);
- if (this.props.room) {
- this.showCall(this.props.room.roomId);
- }
- else {
- // XXX: why would we ever not have a this.props.room?
- var call = CallHandler.getAnyActiveCall();
- if (call) {
- this.showCall(call.roomId);
- }
- }
+ this.showCall(this.props.room.roomId);
},
componentWillUnmount: function() {
@@ -103,7 +97,10 @@ module.exports = React.createClass({
render: function(){
var VideoView = sdk.getComponent('voip.VideoView');
return (
-
+
);
}
});
diff --git a/src/components/views/voip/VideoFeed.js b/src/components/views/voip/VideoFeed.js
index c4a65d1145..0b8d0b20fc 100644
--- a/src/components/views/voip/VideoFeed.js
+++ b/src/components/views/voip/VideoFeed.js
@@ -22,6 +22,9 @@ module.exports = React.createClass({
displayName: 'VideoFeed',
propTypes: {
+ // maxHeight style attribute for the video element
+ maxHeight: React.PropTypes.number,
+
// a callback which is called when the video element is resized
// due to a change in video metadata
onResize: React.PropTypes.func,
@@ -43,7 +46,7 @@ module.exports = React.createClass({
render: function() {
return (
-