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
This commit is contained in:
Richard van der Hoff 2016-02-22 17:19:04 +00:00
parent 26e66326a2
commit 99d2392b6f
6 changed files with 57 additions and 12 deletions

View file

@ -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; var lastRoomId;
for (var i = this.state.searchResults.results.length - 1; i >= 0; i--) { for (var i = this.state.searchResults.results.length - 1; i >= 0; i--) {
@ -957,19 +966,28 @@ module.exports = React.createClass({
ret.push(<SearchResultTile key={mxEv.getId()} ret.push(<SearchResultTile key={mxEv.getId()}
searchResult={result} searchResult={result}
searchHighlights={this.state.searchHighlights} searchHighlights={this.state.searchHighlights}
resultLink={resultLink}/>); resultLink={resultLink}
onImageLoad={onImageLoad}/>);
} }
return ret; return ret;
}, },
getEventTiles: function() { getEventTiles: function() {
var DateSeparator = sdk.getComponent('messages.DateSeparator'); 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 ret = [];
var count = 0; var count = 0;
var EventTile = sdk.getComponent('rooms.EventTile');
var prevEvent = null; // the last event we showed var prevEvent = null; // the last event we showed
var ghostIndex; var ghostIndex;
var readMarkerIndex; var readMarkerIndex;
@ -1056,7 +1074,8 @@ module.exports = React.createClass({
ref={this._collectEventNode.bind(this, eventId)} ref={this._collectEventNode.bind(this, eventId)}
data-scroll-token={scrollToken}> data-scroll-token={scrollToken}>
<EventTile mxEvent={mxEv} continuation={continuation} <EventTile mxEvent={mxEv} continuation={continuation}
last={last} isSelectedEvent={highlight}/> last={last} isSelectedEvent={highlight}
onImageLoad={onImageLoad} />
</li> </li>
); );

View file

@ -124,10 +124,9 @@ module.exports = React.createClass({
// after adding event tiles, we may need to tweak the scroll (either to // 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 // keep at the bottom of the timeline, or to maintain the view after
// adding events to the top). // adding events to the top).
this._restoreSavedScrollState(); //
// This will also re-check the fill state, in case the paginate was inadequate
// we also re-check the fill state, in case the paginate was inadequate this.checkScroll();
this.checkFillState();
}, },
componentWillUnmount: function() { componentWillUnmount: function() {
@ -178,6 +177,13 @@ module.exports = React.createClass({
this.checkFillState(); 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. // 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 // note that this is independent of the 'stuckAtBottom' state - it is simply

View file

@ -27,6 +27,14 @@ var dis = require("../../../dispatcher");
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'MImageBody', 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) { thumbHeight: function(fullWidth, fullHeight, thumbWidth, thumbHeight) {
if (!fullWidth || !fullHeight) { if (!fullWidth || !fullHeight) {
// Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even // Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even
@ -116,7 +124,8 @@ module.exports = React.createClass({
<img className="mx_MImageBody_thumbnail" src={thumbUrl} <img className="mx_MImageBody_thumbnail" src={thumbUrl}
alt={content.body} style={imgStyle} alt={content.body} style={imgStyle}
onMouseEnter={this.onImageEnter} onMouseEnter={this.onImageEnter}
onMouseLeave={this.onImageLeave} /> onMouseLeave={this.onImageLeave}
onLoad={this.props.onImageLoad} />
</a> </a>
<div className="mx_MImageBody_download"> <div className="mx_MImageBody_download">
<a href={cli.mxcUrlToHttp(content.url)} target="_blank"> <a href={cli.mxcUrlToHttp(content.url)} target="_blank">

View file

@ -37,6 +37,9 @@ module.exports = React.createClass({
/* link URL for the highlights */ /* link URL for the highlights */
highlightLink: React.PropTypes.string, 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 <TileType mxEvent={this.props.mxEvent} highlights={this.props.highlights} return <TileType mxEvent={this.props.mxEvent} highlights={this.props.highlights}
highlightLink={this.props.highlightLink} />; highlightLink={this.props.highlightLink}
onImageLoad={this.props.onImageLoad} />;
}, },
}); });

View file

@ -102,6 +102,9 @@ module.exports = React.createClass({
/* is this the focussed event */ /* is this the focussed event */
isSelectedEvent: React.PropTypes.bool, isSelectedEvent: React.PropTypes.bool,
/* callback called when images in events are loaded */
onImageLoad: React.PropTypes.func,
}, },
getInitialState: function() { getInitialState: function() {
@ -323,7 +326,8 @@ module.exports = React.createClass({
{ sender } { sender }
<div className="mx_EventTile_line"> <div className="mx_EventTile_line">
<EventTileType mxEvent={this.props.mxEvent} highlights={this.props.highlights} <EventTileType mxEvent={this.props.mxEvent} highlights={this.props.highlights}
highlightLink={this.props.highlightLink}/> highlightLink={this.props.highlightLink}
onImageLoad={this.props.onImageLoad} />
</div> </div>
</div> </div>
); );

View file

@ -31,6 +31,8 @@ module.exports = React.createClass({
// href for the highlights in this result // href for the highlights in this result
resultLink: React.PropTypes.string, resultLink: React.PropTypes.string,
onImageLoad: React.PropTypes.func,
}, },
render: function() { render: function() {
@ -53,7 +55,8 @@ module.exports = React.createClass({
} }
if (EventTile.haveTileForEvent(ev)) { if (EventTile.haveTileForEvent(ev)) {
ret.push(<EventTile key={eventId+"+"+j} mxEvent={ev} contextual={contextual} highlights={highlights} ret.push(<EventTile key={eventId+"+"+j} mxEvent={ev} contextual={contextual} highlights={highlights}
highlightLink={this.props.resultLink}/>); highlightLink={this.props.resultLink}
onImageLoad={this.props.onImageLoad} />);
} }
} }
return ( return (