From 99d2392b6ff9d139ef2c595d3196028864dc9df6 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 22 Feb 2016 17:19:04 +0000 Subject: [PATCH] Update the scroll offset when images load In order to deal with image-loading reshaping the DOM, wire up ScrollPanel.checkScroll to the image load events. Fixes https://github.com/vector-im/vector-web/issues/984 --- src/components/structures/RoomView.js | 27 ++++++++++++++++--- src/components/structures/ScrollPanel.js | 14 +++++++--- src/components/views/messages/MImageBody.js | 11 +++++++- src/components/views/messages/MessageEvent.js | 6 ++++- src/components/views/rooms/EventTile.js | 6 ++++- .../views/rooms/SearchResultTile.js | 5 +++- 6 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 07a8da5eac..856ab955a4 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -921,6 +921,15 @@ module.exports = React.createClass({ } } + // once images in the search results load, make the scrollPanel check + // the scroll offsets. + var onImageLoad = () => { + var scrollPanel = this.refs.searchResultsPanel; + if (scrollPanel) { + scrollPanel.checkScroll(); + } + } + var lastRoomId; for (var i = this.state.searchResults.results.length - 1; i >= 0; i--) { @@ -957,19 +966,28 @@ module.exports = React.createClass({ ret.push(); + resultLink={resultLink} + onImageLoad={onImageLoad}/>); } return ret; }, getEventTiles: function() { var DateSeparator = sdk.getComponent('messages.DateSeparator'); + var EventTile = sdk.getComponent('rooms.EventTile'); + + // once images in the events load, make the scrollPanel check the + // scroll offsets. + var onImageLoad = () => { + var scrollPanel = this.refs.messagePanel; + if (scrollPanel) { + scrollPanel.checkScroll(); + } + } var ret = []; var count = 0; - var EventTile = sdk.getComponent('rooms.EventTile'); - var prevEvent = null; // the last event we showed var ghostIndex; var readMarkerIndex; @@ -1056,7 +1074,8 @@ module.exports = React.createClass({ ref={this._collectEventNode.bind(this, eventId)} data-scroll-token={scrollToken}> + last={last} isSelectedEvent={highlight} + onImageLoad={onImageLoad} /> ); diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index 514937f877..044ef48687 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -124,10 +124,9 @@ module.exports = React.createClass({ // 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). - this._restoreSavedScrollState(); - - // we also re-check the fill state, in case the paginate was inadequate - this.checkFillState(); + // + // This will also re-check the fill state, in case the paginate was inadequate + this.checkScroll(); }, componentWillUnmount: function() { @@ -178,6 +177,13 @@ module.exports = React.createClass({ this.checkFillState(); }, + // after an update to the contents of the panel, check that the scroll is + // where it ought to be, and set off pagination requests if necessary. + checkScroll: function() { + this._restoreSavedScrollState(); + this.checkFillState(); + }, + // return true if the content is fully scrolled down right now; else false. // // note that this is independent of the 'stuckAtBottom' state - it is simply diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js index b54a51c45b..b60098295a 100644 --- a/src/components/views/messages/MImageBody.js +++ b/src/components/views/messages/MImageBody.js @@ -27,6 +27,14 @@ var dis = require("../../../dispatcher"); module.exports = React.createClass({ displayName: 'MImageBody', + propTypes: { + /* the MatrixEvent to show */ + mxEvent: React.PropTypes.object.isRequired, + + /* callback called when images in events are loaded */ + onImageLoad: React.PropTypes.func, + }, + thumbHeight: function(fullWidth, fullHeight, thumbWidth, thumbHeight) { if (!fullWidth || !fullHeight) { // Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even @@ -116,7 +124,8 @@ module.exports = React.createClass({ {content.body} + onMouseLeave={this.onImageLeave} + onLoad={this.props.onImageLoad} />
diff --git a/src/components/views/messages/MessageEvent.js b/src/components/views/messages/MessageEvent.js index 9cc0e22c59..34d6d53924 100644 --- a/src/components/views/messages/MessageEvent.js +++ b/src/components/views/messages/MessageEvent.js @@ -37,6 +37,9 @@ module.exports = React.createClass({ /* link URL for the highlights */ highlightLink: React.PropTypes.string, + + /* callback called when images in events are loaded */ + onImageLoad: React.PropTypes.func, }, @@ -60,6 +63,7 @@ module.exports = React.createClass({ } return ; + highlightLink={this.props.highlightLink} + onImageLoad={this.props.onImageLoad} />; }, }); diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 0205062f84..8ac7ab7996 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -102,6 +102,9 @@ module.exports = React.createClass({ /* is this the focussed event */ isSelectedEvent: React.PropTypes.bool, + + /* callback called when images in events are loaded */ + onImageLoad: React.PropTypes.func, }, getInitialState: function() { @@ -323,7 +326,8 @@ module.exports = React.createClass({ { sender }
+ highlightLink={this.props.highlightLink} + onImageLoad={this.props.onImageLoad} />
); diff --git a/src/components/views/rooms/SearchResultTile.js b/src/components/views/rooms/SearchResultTile.js index 9c793e8705..1fc0384433 100644 --- a/src/components/views/rooms/SearchResultTile.js +++ b/src/components/views/rooms/SearchResultTile.js @@ -31,6 +31,8 @@ module.exports = React.createClass({ // href for the highlights in this result resultLink: React.PropTypes.string, + + onImageLoad: React.PropTypes.func, }, render: function() { @@ -53,7 +55,8 @@ module.exports = React.createClass({ } if (EventTile.haveTileForEvent(ev)) { ret.push(); + highlightLink={this.props.resultLink} + onImageLoad={this.props.onImageLoad} />); } } return (