diff --git a/src/KeyCode.js b/src/KeyCode.js index b80703d39e..bbe1ddcefa 100644 --- a/src/KeyCode.js +++ b/src/KeyCode.js @@ -29,4 +29,6 @@ module.exports = { RIGHT: 39, DOWN: 40, DELETE: 46, + KEY_D: 68, + KEY_E: 69, }; diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index ae1b0febe8..c6b577da5f 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -38,6 +38,8 @@ var rate_limited_func = require('../../ratelimitedfunc'); var ObjectUtils = require('../../ObjectUtils'); var Rooms = require('../../Rooms'); +import KeyCode from '../../KeyCode'; + import UserProvider from '../../autocomplete/UserProvider'; var DEBUG = false; @@ -239,11 +241,65 @@ module.exports = React.createClass({ } }, + componentDidMount: function() { + var call = this._getCallForRoom(); + var callState = call ? call.call_state : "ended"; + this.setState({ + callState: callState + }); + + this._updateConfCallNotification(); + + window.addEventListener('resize', this.onResize); + this.onResize(); + + document.addEventListener("keydown", this.onKeyDown); + + // XXX: EVIL HACK to autofocus inviting on empty rooms. + // We use the setTimeout to avoid racing with focus_composer. + if (this.state.room && + this.state.room.getJoinedMembers().length == 1 && + this.state.room.getLiveTimeline() && + this.state.room.getLiveTimeline().getEvents() && + this.state.room.getLiveTimeline().getEvents().length <= 6) + { + var inviteBox = document.getElementById("mx_SearchableEntityList_query"); + setTimeout(function() { + if (inviteBox) { + inviteBox.focus(); + } + }, 50); + } + }, + + componentWillReceiveProps: function(newProps) { + if (newProps.roomAddress != this.props.roomAddress) { + throw new Error("changing room on a RoomView is not supported"); + } + + if (newProps.eventId != this.props.eventId) { + // when we change focussed event id, hide the search results. + this.setState({searchResults: null}); + } + }, + shouldComponentUpdate: function(nextProps, nextState) { return (!ObjectUtils.shallowEqual(this.props, nextProps) || !ObjectUtils.shallowEqual(this.state, nextState)); }, + componentDidUpdate: function() { + if (this.refs.roomView) { + var roomView = ReactDOM.findDOMNode(this.refs.roomView); + if (!roomView.ondrop) { + roomView.addEventListener('drop', this.onDrop); + roomView.addEventListener('dragover', this.onDragOver); + roomView.addEventListener('dragleave', this.onDragLeaveOrEnd); + roomView.addEventListener('dragend', this.onDragLeaveOrEnd); + } + } + }, + componentWillUnmount: function() { // set a boolean to say we've been unmounted, which any pending // promises can use to throw away their results. @@ -273,6 +329,8 @@ module.exports = React.createClass({ window.removeEventListener('resize', this.onResize); + document.removeEventListener("keydown", this.onKeyDown); + // cancel any pending calls to the rate_limited_funcs this._updateRoomMembers.cancelPendingCall(); @@ -281,6 +339,31 @@ module.exports = React.createClass({ // Tinter.tint(); // reset colourscheme }, + onKeyDown: function(ev) { + var handled = false; + + switch (ev.keyCode) { + case KeyCode.KEY_D: + if (ev.ctrlKey) { + this.onMuteAudioClick(); + handled = true; + } + break; + + case KeyCode.KEY_E: + if (ev.ctrlKey) { + this.onMuteVideoClick(); + handled = true; + } + break; + } + + if (handled) { + ev.stopPropagation(); + ev.preventDefault(); + } + }, + onAction: function(payload) { switch (payload.action) { case 'message_send_failed': @@ -326,17 +409,6 @@ module.exports = React.createClass({ } }, - componentWillReceiveProps: function(newProps) { - if (newProps.roomAddress != this.props.roomAddress) { - throw new Error("changing room on a RoomView is not supported"); - } - - if (newProps.eventId != this.props.eventId) { - // when we change focussed event id, hide the search results. - this.setState({searchResults: null}); - } - }, - onRoomTimeline: function(ev, room, toStartOfTimeline, removed, data) { if (this.unmounted) return; @@ -573,47 +645,6 @@ module.exports = React.createClass({ }); }, - componentDidMount: function() { - var call = this._getCallForRoom(); - var callState = call ? call.call_state : "ended"; - this.setState({ - callState: callState - }); - - this._updateConfCallNotification(); - - window.addEventListener('resize', this.onResize); - this.onResize(); - - // XXX: EVIL HACK to autofocus inviting on empty rooms. - // We use the setTimeout to avoid racing with focus_composer. - if (this.state.room && - this.state.room.getJoinedMembers().length == 1 && - this.state.room.getLiveTimeline() && - this.state.room.getLiveTimeline().getEvents() && - this.state.room.getLiveTimeline().getEvents().length <= 6) - { - var inviteBox = document.getElementById("mx_SearchableEntityList_query"); - setTimeout(function() { - if (inviteBox) { - inviteBox.focus(); - } - }, 50); - } - }, - - componentDidUpdate: function() { - if (this.refs.roomView) { - var roomView = ReactDOM.findDOMNode(this.refs.roomView); - if (!roomView.ondrop) { - roomView.addEventListener('drop', this.onDrop); - roomView.addEventListener('dragover', this.onDragOver); - roomView.addEventListener('dragleave', this.onDragLeaveOrEnd); - roomView.addEventListener('dragend', this.onDragLeaveOrEnd); - } - } - }, - onSearchResultsResize: function() { dis.dispatch({ action: 'timeline_resize' }, true); }, @@ -1261,9 +1292,7 @@ module.exports = React.createClass({ } var newState = !call.isMicrophoneMuted(); call.setMicrophoneMuted(newState); - this.setState({ - audioMuted: newState - }); + this.forceUpdate(); // TODO: just update the voip buttons }, onMuteVideoClick: function() { @@ -1273,9 +1302,7 @@ module.exports = React.createClass({ } var newState = !call.isLocalVideoMuted(); call.setLocalVideoMuted(newState); - this.setState({ - videoMuted: newState - }); + this.forceUpdate(); // TODO: just update the voip buttons }, onChildResize: function() {