Merge branch 'develop' into matthew/preview_urls

This commit is contained in:
Matthew Hodgson 2016-04-07 17:25:48 +01:00
commit 7884c13d0d
12 changed files with 566 additions and 71 deletions

32
src/KeyCode.js Normal file
View file

@ -0,0 +1,32 @@
/*
Copyright 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.
*/
/* a selection of key codes, as used in KeyboardEvent.keyCode */
module.exports = {
BACKSPACE: 8,
TAB: 9,
ENTER: 13,
SHIFT: 16,
PAGE_UP: 33,
PAGE_DOWN: 34,
END: 35,
HOME: 36,
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
DELETE: 46,
};

View file

@ -35,6 +35,7 @@ var Tinter = require("../../Tinter");
var sdk = require('../../index');
var MatrixTools = require('../../MatrixTools');
var linkifyMatrix = require("../../linkify-matrix");
var KeyCode = require('../../KeyCode');
module.exports = React.createClass({
displayName: 'MatrixChat',
@ -722,11 +723,10 @@ module.exports = React.createClass({
},
onKeyDown: function(ev) {
if (ev.altKey) {
/*
// Remove this for now as ctrl+alt = alt-gr so this breaks keyboards which rely on alt-gr for numbers
// Will need to find a better meta key if anyone actually cares about using this.
if (ev.ctrlKey && ev.keyCode > 48 && ev.keyCode < 58) {
if (ev.altKey && ev.ctrlKey && ev.keyCode > 48 && ev.keyCode < 58) {
dis.dispatch({
action: 'view_indexed_room',
roomIndex: ev.keyCode - 49,
@ -736,18 +736,45 @@ module.exports = React.createClass({
return;
}
*/
switch (ev.keyCode) {
case 38:
dis.dispatch({action: 'view_prev_room'});
ev.stopPropagation();
ev.preventDefault();
break;
case 40:
dis.dispatch({action: 'view_next_room'});
ev.stopPropagation();
ev.preventDefault();
break;
}
var handled = false;
switch (ev.keyCode) {
case KeyCode.UP:
case KeyCode.DOWN:
if (ev.altKey) {
var action = ev.keyCode == KeyCode.UP ?
'view_prev_room' : 'view_next_room';
dis.dispatch({action: action});
handled = true;
}
break;
case KeyCode.PAGE_UP:
case KeyCode.PAGE_DOWN:
this._onScrollKeyPressed(ev);
handled = true;
break;
case KeyCode.HOME:
case KeyCode.END:
if (ev.ctrlKey) {
this._onScrollKeyPressed(ev);
handled = true;
}
break;
}
if (handled) {
ev.stopPropagation();
ev.preventDefault();
}
},
/** dispatch a page-up/page-down/etc to the appropriate component */
_onScrollKeyPressed(ev) {
if (this.refs.roomView) {
this.refs.roomView.handleScrollKey(ev);
}
},

View file

@ -132,6 +132,14 @@ module.exports = React.createClass({
}
},
/* jump to the top of the content.
*/
scrollToTop: function() {
if (this.refs.scrollPanel) {
this.refs.scrollPanel.scrollToTop();
}
},
/* jump to the bottom of the content.
*/
scrollToBottom: function() {
@ -139,6 +147,26 @@ module.exports = React.createClass({
this.refs.scrollPanel.scrollToBottom();
}
},
/**
* Page up/down.
*
* mult: -1 to page up, +1 to page down
*/
scrollRelative: function(mult) {
if (this.refs.scrollPanel) {
this.refs.scrollPanel.scrollRelative(mult);
}
},
/**
* Scroll up/down in response to a scroll key
*/
handleScrollKey: function(ev) {
if (this.refs.scrollPanel) {
this.refs.scrollPanel.handleScrollKey(ev);
}
},
/* jump to the given event id.
*
@ -205,6 +233,13 @@ module.exports = React.createClass({
// assume there is no read marker until proven otherwise
var readMarkerVisible = false;
// if the readmarker has moved, cancel any active ghost.
if (this.currentReadMarkerEventId && this.props.readMarkerEventId &&
this.props.readMarkerVisible &&
this.currentReadMarkerEventId != this.props.readMarkerEventId) {
this.currentGhostEventId = null;
}
for (i = 0; i < this.props.events.length; i++) {
var mxEv = this.props.events[i];
var wantTile = true;
@ -337,21 +372,16 @@ module.exports = React.createClass({
);
},
_getReadMarkerGhostTile: function() {
// reset the ghostEventId when the animation finishes, so that
// we can make a new one (and so that we don't run the
// animation code every time we render)
var completeFunc = () => {
this.currentGhostEventId = null;
};
_startAnimation: function(ghostNode) {
Velocity(ghostNode, {opacity: '0', width: '10%'},
{duration: 400, easing: 'easeInSine',
delay: 1000});
},
_getReadMarkerGhostTile: function() {
var hr = <hr className="mx_RoomView_myReadMarker"
style={{opacity: 1, width: '99%'}}
ref={function(n) {
Velocity(n, {opacity: '0', width: '10%'},
{duration: 400, easing: 'easeInSine',
delay: 1000, complete: completeFunc});
}}
ref={this._startAnimation}
/>;
// give it a key which depends on the event id. That will ensure that

View file

@ -1115,6 +1115,24 @@ module.exports = React.createClass({
}
},
/**
* called by the parent component when PageUp/Down/etc is pressed.
*
* We pass it down to the scroll panel.
*/
handleScrollKey: function(ev) {
var panel;
if(this.refs.searchResultsPanel) {
panel = this.refs.searchResultsPanel;
} else if(this.refs.messagePanel) {
panel = this.refs.messagePanel;
}
if(panel) {
panel.handleScrollKey(ev);
}
},
// this has to be a proper method rather than an unnamed function,
// otherwise react calls it with null on each update.
_gatherTimelinePanelRef: function(r) {

View file

@ -18,8 +18,10 @@ var React = require("react");
var ReactDOM = require("react-dom");
var GeminiScrollbar = require('react-gemini-scrollbar');
var q = require("q");
var KeyCode = require('../../KeyCode');
var DEBUG_SCROLL = false;
// var DEBUG_SCROLL = true;
if (DEBUG_SCROLL) {
// using bind means that we get to keep useful line numbers in the console
@ -144,7 +146,8 @@ module.exports = React.createClass({
onScroll: function(ev) {
var sn = this._getScrollNode();
debuglog("Scroll event: offset now:", sn.scrollTop, "recentEventScroll:", this.recentEventScroll);
debuglog("Scroll event: offset now:", sn.scrollTop,
"_lastSetScroll:", this._lastSetScroll);
// Sometimes we see attempts to write to scrollTop essentially being
// ignored. (Or rather, it is successfully written, but on the next
@ -158,13 +161,10 @@ module.exports = React.createClass({
// By way of a workaround, we detect this situation and just keep
// resetting scrollTop until we see the scroll node have the right
// value.
if (this.recentEventScroll !== undefined) {
if(sn.scrollTop < this.recentEventScroll-200) {
console.log("Working around vector-im/vector-web#528");
this._restoreSavedScrollState();
return;
}
this.recentEventScroll = undefined;
if (this._lastSetScroll !== undefined && sn.scrollTop < this._lastSetScroll-200) {
console.log("Working around vector-im/vector-web#528");
this._restoreSavedScrollState();
return;
}
// If there weren't enough children to fill the viewport, the scroll we
@ -327,6 +327,17 @@ module.exports = React.createClass({
this.scrollState = {stuckAtBottom: true};
},
/**
* jump to the top of the content.
*/
scrollToTop: function() {
this._setScrollTop(0);
this._saveScrollState();
},
/**
* jump to the bottom of the content.
*/
scrollToBottom: function() {
// the easiest way to make sure that the scroll state is correctly
// saved is to do the scroll, then save the updated state. (Calculating
@ -336,6 +347,45 @@ module.exports = React.createClass({
this._saveScrollState();
},
/**
* Page up/down.
*
* mult: -1 to page up, +1 to page down
*/
scrollRelative: function(mult) {
var scrollNode = this._getScrollNode();
var delta = mult * scrollNode.clientHeight * 0.5;
this._setScrollTop(scrollNode.scrollTop + delta);
this._saveScrollState();
},
/**
* Scroll up/down in response to a scroll key
*/
handleScrollKey: function(ev) {
switch (ev.keyCode) {
case KeyCode.PAGE_UP:
this.scrollRelative(-1);
break;
case KeyCode.PAGE_DOWN:
this.scrollRelative(1);
break;
case KeyCode.HOME:
if (ev.ctrlKey) {
this.scrollToTop();
}
break;
case KeyCode.END:
if (ev.ctrlKey) {
this.scrollToBottom();
}
break;
}
},
/* Scroll the panel to bring the DOM node with the scroll token
* `scrollToken` into view.
*
@ -395,17 +445,14 @@ module.exports = React.createClass({
var wrapperRect = ReactDOM.findDOMNode(this).getBoundingClientRect();
var boundingRect = node.getBoundingClientRect();
var scrollDelta = boundingRect.bottom + pixelOffset - wrapperRect.bottom;
debuglog("Scrolling to token '" + node.dataset.scrollToken + "'+" +
pixelOffset + " (delta: "+scrollDelta+")");
if(scrollDelta != 0) {
this._setScrollTop(scrollNode.scrollTop + scrollDelta);
// see the comments in onScroll regarding recentEventScroll
this.recentEventScroll = scrollNode.scrollTop;
}
debuglog("Scrolled to token", node.dataset.scrollToken, "+",
pixelOffset+":", scrollNode.scrollTop,
"(delta: "+scrollDelta+")");
debuglog("recentEventScroll now "+this.recentEventScroll);
},
_saveScrollState: function() {

View file

@ -27,6 +27,7 @@ var dis = require("../../dispatcher");
var ObjectUtils = require('../../ObjectUtils');
var Modal = require("../../Modal");
var UserActivity = require("../../UserActivity");
var KeyCode = require('../../KeyCode');
var PAGINATE_SIZE = 20;
var INITIAL_SIZE = 20;
@ -520,6 +521,23 @@ var TimelinePanel = React.createClass({
return null;
},
/**
* called by the parent component when PageUp/Down/etc is pressed.
*
* We pass it down to the scroll panel.
*/
handleScrollKey: function(ev) {
if (!this.refs.messagePanel) { return; }
// jump to the live timeline on ctrl-end, rather than the end of the
// timeline window.
if (ev.ctrlKey && ev.keyCode == KeyCode.END) {
this.jumpToLiveTimeline();
} else {
this.refs.messagePanel.handleScrollKey(ev);
}
},
_initTimeline: function(props) {
var initialEvent = props.eventId;
var pixelOffset = props.eventPixelOffset;

View file

@ -38,8 +38,8 @@ module.exports = React.createClass({
getInitialState: function() {
return {
enteredHomeserverUrl: this.props.homeserverUrl,
enteredIdentityServerUrl: this.props.identityServerUrl,
enteredHomeserverUrl: this.props.customHsUrl || this.props.defaultHsUrl,
enteredIdentityServerUrl: this.props.customIsUrl || this.props.defaultIsUrl,
progress: null
};
},

View file

@ -34,15 +34,7 @@ var MemberEntry = require("../../../TabCompleteEntries").MemberEntry;
var sdk = require('../../../index');
var dis = require("../../../dispatcher");
var KeyCode = {
ENTER: 13,
BACKSPACE: 8,
DELETE: 46,
TAB: 9,
SHIFT: 16,
UP: 38,
DOWN: 40
};
var KeyCode = require("../../../KeyCode");
var TYPING_USER_TIMEOUT = 10000;
var TYPING_SERVER_TIMEOUT = 30000;