track RoomTile focus in RoomList, and stop the RoomList from updating during mouseOver

This commit is contained in:
Matthew Hodgson 2017-04-15 13:23:52 +01:00
parent 0a91511f05
commit 691639d1e0
2 changed files with 69 additions and 4 deletions

View file

@ -63,12 +63,15 @@ module.exports = React.createClass({
var s = this.getRoomLists(); var s = this.getRoomLists();
this.setState(s); this.setState(s);
this.focusedRoomTileRoomId = null;
}, },
componentDidMount: function() { componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction); this.dispatcherRef = dis.register(this.onAction);
// Initialise the stickyHeaders when the component is created // Initialise the stickyHeaders when the component is created
this._updateStickyHeaders(true); this._updateStickyHeaders(true);
document.addEventListener('keydown', this._onKeyDown);
}, },
componentDidUpdate: function() { componentDidUpdate: function() {
@ -100,6 +103,8 @@ module.exports = React.createClass({
// Force an update because the notif count state is too deep to cause // Force an update because the notif count state is too deep to cause
// an update. This forces the local echo of reading notifs to be // an update. This forces the local echo of reading notifs to be
// reflected by the RoomTiles. // reflected by the RoomTiles.
//
// FIXME: we should surely just be refreshing the right tile...
this.forceUpdate(); this.forceUpdate();
break; break;
} }
@ -120,6 +125,8 @@ module.exports = React.createClass({
} }
// cancel any pending calls to the rate_limited_funcs // cancel any pending calls to the rate_limited_funcs
this._delayedRefreshRoomList.cancelPendingCall(); this._delayedRefreshRoomList.cancelPendingCall();
document.removeEventListener('keydown', this._onKeyDown);
}, },
onRoom: function(room) { onRoom: function(room) {
@ -149,6 +156,35 @@ module.exports = React.createClass({
} }
}, },
_onMouseOver: function(ev) {
this._lastMouseOverTs = Date.now();
},
_onKeyDown: function(ev) {
if (!this.focusedRoomTileRoomId) return;
let handled = false;
switch (ev.keyCode) {
case KeyCode.UP:
this._onMoveFocus(true);
handled = true;
break;
case KeyCode.DOWN:
this._onMoveFocus(false);
handled = true;
break;
}
if (handled) {
ev.stopPropagation();
ev.preventDefault();
}
},
_onMoveFocus: function(up) {
},
onSubListHeaderClick: function(isHidden, scrollToPosition) { onSubListHeaderClick: function(isHidden, scrollToPosition) {
// The scroll area has expanded or contracted, so re-calculate sticky headers positions // The scroll area has expanded or contracted, so re-calculate sticky headers positions
this._updateStickyHeaders(true, scrollToPosition); this._updateStickyHeaders(true, scrollToPosition);
@ -192,7 +228,15 @@ module.exports = React.createClass({
}, },
_delayedRefreshRoomList: new rate_limited_func(function() { _delayedRefreshRoomList: new rate_limited_func(function() {
this.refreshRoomList(); // if the mouse has been moving over the RoomList in the last 500ms
// then delay the refresh further to avoid bouncing around under the
// cursor
if (Date.now() - this._lastMouseOverTs > 500) {
this.refreshRoomList();
}
else {
this._delayedRefreshRoomList();
}
}, 500), }, 500),
refreshRoomList: function() { refreshRoomList: function() {
@ -207,7 +251,8 @@ module.exports = React.createClass({
// us re-rendering all the sublists every time anything changes anywhere // us re-rendering all the sublists every time anything changes anywhere
// in the state of the client. // in the state of the client.
this.setState(this.getRoomLists()); this.setState(this.getRoomLists());
this._lastRefreshRoomListTs = Date.now();
// this._lastRefreshRoomListTs = Date.now();
}, },
getRoomLists: function() { getRoomLists: function() {
@ -457,6 +502,10 @@ module.exports = React.createClass({
this.refs.gemscroll.forceUpdate(); this.refs.gemscroll.forceUpdate();
}, },
onRoomTileFocus: function(roomId) {
this.focusedRoomTileRoomId = roomId;
},
render: function() { render: function() {
var RoomSubList = sdk.getComponent('structures.RoomSubList'); var RoomSubList = sdk.getComponent('structures.RoomSubList');
var self = this; var self = this;
@ -464,7 +513,7 @@ module.exports = React.createClass({
return ( return (
<GeminiScrollbar className="mx_RoomList_scrollbar" <GeminiScrollbar className="mx_RoomList_scrollbar"
autoshow={true} onScroll={ self._whenScrolling } ref="gemscroll"> autoshow={true} onScroll={ self._whenScrolling } ref="gemscroll">
<div className="mx_RoomList"> <div className="mx_RoomList" onMouseOver={ this._onMouseOver }>
<RoomSubList list={ self.state.lists['im.vector.fake.invite'] } <RoomSubList list={ self.state.lists['im.vector.fake.invite'] }
label="Invites" label="Invites"
editable={ false } editable={ false }
@ -474,6 +523,7 @@ module.exports = React.createClass({
collapsed={ self.props.collapsed } collapsed={ self.props.collapsed }
searchFilter={ self.props.searchFilter } searchFilter={ self.props.searchFilter }
onHeaderClick={ self.onSubListHeaderClick } onHeaderClick={ self.onSubListHeaderClick }
onRoomTileFocus={ self.onRoomTileFocus }
onShowMoreRooms={ self.onShowMoreRooms } /> onShowMoreRooms={ self.onShowMoreRooms } />
<RoomSubList list={ self.state.lists['m.favourite'] } <RoomSubList list={ self.state.lists['m.favourite'] }
@ -487,6 +537,7 @@ module.exports = React.createClass({
collapsed={ self.props.collapsed } collapsed={ self.props.collapsed }
searchFilter={ self.props.searchFilter } searchFilter={ self.props.searchFilter }
onHeaderClick={ self.onSubListHeaderClick } onHeaderClick={ self.onSubListHeaderClick }
onRoomTileFocus={ self.onRoomTileFocus }
onShowMoreRooms={ self.onShowMoreRooms } /> onShowMoreRooms={ self.onShowMoreRooms } />
<RoomSubList list={ self.state.lists['im.vector.fake.direct'] } <RoomSubList list={ self.state.lists['im.vector.fake.direct'] }
@ -501,6 +552,7 @@ module.exports = React.createClass({
alwaysShowHeader={ true } alwaysShowHeader={ true }
searchFilter={ self.props.searchFilter } searchFilter={ self.props.searchFilter }
onHeaderClick={ self.onSubListHeaderClick } onHeaderClick={ self.onSubListHeaderClick }
onRoomTileFocus={ self.onRoomTileFocus }
onShowMoreRooms={ self.onShowMoreRooms } /> onShowMoreRooms={ self.onShowMoreRooms } />
<RoomSubList list={ self.state.lists['im.vector.fake.recent'] } <RoomSubList list={ self.state.lists['im.vector.fake.recent'] }
@ -513,6 +565,7 @@ module.exports = React.createClass({
collapsed={ self.props.collapsed } collapsed={ self.props.collapsed }
searchFilter={ self.props.searchFilter } searchFilter={ self.props.searchFilter }
onHeaderClick={ self.onSubListHeaderClick } onHeaderClick={ self.onSubListHeaderClick }
onRoomTileFocus={ self.onRoomTileFocus }
onShowMoreRooms={ self.onShowMoreRooms } /> onShowMoreRooms={ self.onShowMoreRooms } />
{ Object.keys(self.state.lists).map(function(tagName) { { Object.keys(self.state.lists).map(function(tagName) {
@ -529,6 +582,7 @@ module.exports = React.createClass({
collapsed={ self.props.collapsed } collapsed={ self.props.collapsed }
searchFilter={ self.props.searchFilter } searchFilter={ self.props.searchFilter }
onHeaderClick={ self.onSubListHeaderClick } onHeaderClick={ self.onSubListHeaderClick }
onRoomTileFocus={ self.onRoomTileFocus }
onShowMoreRooms={ self.onShowMoreRooms } />; onShowMoreRooms={ self.onShowMoreRooms } />;
} }
@ -545,6 +599,7 @@ module.exports = React.createClass({
collapsed={ self.props.collapsed } collapsed={ self.props.collapsed }
searchFilter={ self.props.searchFilter } searchFilter={ self.props.searchFilter }
onHeaderClick={ self.onSubListHeaderClick } onHeaderClick={ self.onSubListHeaderClick }
onRoomTileFocus={ self.onRoomTileFocus }
onShowMoreRooms={ self.onShowMoreRooms } /> onShowMoreRooms={ self.onShowMoreRooms } />
<RoomSubList list={ self.state.lists['im.vector.fake.archived'] } <RoomSubList list={ self.state.lists['im.vector.fake.archived'] }
@ -559,6 +614,7 @@ module.exports = React.createClass({
onHeaderClick= { self.onArchivedHeaderClick } onHeaderClick= { self.onArchivedHeaderClick }
incomingCall={ self.state.incomingCall } incomingCall={ self.state.incomingCall }
searchFilter={ self.props.searchFilter } searchFilter={ self.props.searchFilter }
onRoomTileFocus={ self.onRoomTileFocus }
onShowMoreRooms={ self.onShowMoreRooms } /> onShowMoreRooms={ self.onShowMoreRooms } />
</div> </div>
</GeminiScrollbar> </GeminiScrollbar>

View file

@ -35,6 +35,7 @@ module.exports = React.createClass({
connectDragSource: React.PropTypes.func, connectDragSource: React.PropTypes.func,
connectDropTarget: React.PropTypes.func, connectDropTarget: React.PropTypes.func,
onClick: React.PropTypes.func, onClick: React.PropTypes.func,
onFocus: React.PropTypes.func,
isDragging: React.PropTypes.bool, isDragging: React.PropTypes.bool,
room: React.PropTypes.object.isRequired, room: React.PropTypes.object.isRequired,
@ -104,6 +105,12 @@ module.exports = React.createClass({
} }
}, },
onFocus: function() {
if (this.props.onFocus) {
this.props.onFocus(this.props.room.roomId);
}
},
onMouseEnter: function() { onMouseEnter: function() {
this.setState( { hover : true }); this.setState( { hover : true });
this.badgeOnMouseEnter(); this.badgeOnMouseEnter();
@ -255,7 +262,9 @@ module.exports = React.createClass({
let ret = ( let ret = (
<div> { /* Only native elements can be wrapped in a DnD object. */} <div> { /* Only native elements can be wrapped in a DnD object. */}
<AccessibleButton className={classes} tabIndex="0" onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}> <AccessibleButton className={classes} tabIndex="0" onClick={this.onClick}
onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}
onFocus={this.onFocus} onBlur={this.onFocus.bind(this, null)}>
<div className={avatarClasses}> <div className={avatarClasses}>
<div className="mx_RoomTile_avatar_container"> <div className="mx_RoomTile_avatar_container">
<RoomAvatar room={this.props.room} width={24} height={24} /> <RoomAvatar room={this.props.room} width={24} height={24} />