fix up scroll behaviour when loading widgets

This commit is contained in:
Matthew Hodgson 2016-04-02 00:36:19 +01:00
parent 62d04c38ef
commit 4388334e30
8 changed files with 55 additions and 44 deletions

View file

@ -302,6 +302,7 @@ 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}
onWidgetLoad={this._onWidgetLoad}
last={last} isSelectedEvent={highlight} /> last={last} isSelectedEvent={highlight} />
</li> </li>
); );
@ -368,6 +369,15 @@ module.exports = React.createClass({
this.eventNodes[eventId] = node; this.eventNodes[eventId] = node;
}, },
// once dynamic content in the events load, make the scrollPanel check the
// scroll offsets.
_onWidgetLoad: function() {
var scrollPanel = this.refs.scrollPanel;
if (scrollPanel) {
scrollPanel.checkScroll();
}
},
onResize: function() { onResize: function() {
dis.dispatch({ action: 'timeline_resize' }, true); dis.dispatch({ action: 'timeline_resize' }, true);
}, },

View file

@ -798,9 +798,9 @@ module.exports = React.createClass({
} }
} }
// once images in the search results load, make the scrollPanel check // once dynamic content in the search results load, make the scrollPanel check
// the scroll offsets. // the scroll offsets.
var onImageLoad = () => { var onWidgetLoad = () => {
var scrollPanel = this.refs.searchResultsPanel; var scrollPanel = this.refs.searchResultsPanel;
if (scrollPanel) { if (scrollPanel) {
scrollPanel.checkScroll(); scrollPanel.checkScroll();
@ -844,7 +844,7 @@ module.exports = React.createClass({
searchResult={result} searchResult={result}
searchHighlights={this.state.searchHighlights} searchHighlights={this.state.searchHighlights}
resultLink={resultLink} resultLink={resultLink}
onImageLoad={onImageLoad}/>); onWidgetLoad={onWidgetLoad}/>);
} }
return ret; return ret;
}, },

View file

@ -20,6 +20,7 @@ var React = require('react');
var filesize = require('filesize'); var filesize = require('filesize');
var MatrixClientPeg = require('../../../MatrixClientPeg'); var MatrixClientPeg = require('../../../MatrixClientPeg');
var ImageUtils = require('../../../ImageUtils');
var Modal = require('../../../Modal'); var Modal = require('../../../Modal');
var sdk = require('../../../index'); var sdk = require('../../../index');
var dis = require("../../../dispatcher"); var dis = require("../../../dispatcher");
@ -32,29 +33,7 @@ module.exports = React.createClass({
mxEvent: React.PropTypes.object.isRequired, mxEvent: React.PropTypes.object.isRequired,
/* callback called when images in events are loaded */ /* callback called when images in events are loaded */
onImageLoad: React.PropTypes.func, onWidgetLoad: 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
// log this because it's spammy
return undefined;
}
if (fullWidth < thumbWidth && fullHeight < thumbHeight) {
// no scaling needs to be applied
return fullHeight;
}
var widthMulti = thumbWidth / fullWidth;
var heightMulti = thumbHeight / fullHeight;
if (widthMulti < heightMulti) {
// width is the dominant dimension so scaling will be fixed on that
return Math.floor(widthMulti * fullHeight);
}
else {
// height is the dominant dimension so scaling will be fixed on that
return Math.floor(heightMulti * fullHeight);
}
}, },
onClick: function onClick(ev) { onClick: function onClick(ev) {
@ -134,7 +113,9 @@ module.exports = React.createClass({
// the alternative here would be 600*timelineWidth/800; to scale them down to fit inside a 4:3 bounding box // the alternative here would be 600*timelineWidth/800; to scale them down to fit inside a 4:3 bounding box
//console.log("trying to fit image into timelineWidth of " + this.refs.body.offsetWidth + " or " + this.refs.body.clientWidth); //console.log("trying to fit image into timelineWidth of " + this.refs.body.offsetWidth + " or " + this.refs.body.clientWidth);
if (content.info) thumbHeight = this.thumbHeight(content.info.w, content.info.h, timelineWidth, maxHeight); if (content.info) {
thumbHeight = ImageUtils.thumbHeight(content.info.w, content.info.h, timelineWidth, maxHeight);
}
this.refs.image.style.height = thumbHeight + "px"; this.refs.image.style.height = thumbHeight + "px";
// console.log("Image height now", thumbHeight); // console.log("Image height now", thumbHeight);
}, },
@ -152,8 +133,7 @@ module.exports = React.createClass({
<img className="mx_MImageBody_thumbnail" src={thumbUrl} ref="image" <img className="mx_MImageBody_thumbnail" src={thumbUrl} ref="image"
alt={content.body} alt={content.body}
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

@ -39,7 +39,7 @@ module.exports = React.createClass({
highlightLink: React.PropTypes.string, highlightLink: React.PropTypes.string,
/* callback called when images in events are loaded */ /* callback called when images in events are loaded */
onImageLoad: React.PropTypes.func, onWidgetLoad: React.PropTypes.func,
}, },
@ -64,6 +64,6 @@ 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} />; onWidgetLoad={this.props.onWidgetLoad} />;
}, },
}); });

View file

@ -38,6 +38,9 @@ module.exports = React.createClass({
/* link URL for the highlights */ /* link URL for the highlights */
highlightLink: React.PropTypes.string, highlightLink: React.PropTypes.string,
/* callback for when our widget has loaded */
onWidgetLoad: React.PropTypes.func,
}, },
getInitialState: function() { getInitialState: function() {
@ -88,7 +91,7 @@ module.exports = React.createClass({
var widget; var widget;
if (this.state.link) { if (this.state.link) {
var LinkPreviewWidget = sdk.getComponent('rooms.LinkPreviewWidget'); var LinkPreviewWidget = sdk.getComponent('rooms.LinkPreviewWidget');
widget = <LinkPreviewWidget link={ this.state.link }/>; widget = <LinkPreviewWidget link={ this.state.link } onWidgetLoad={ this.props.onWidgetLoad }/>;
} }
switch (content.msgtype) { switch (content.msgtype) {

View file

@ -105,8 +105,8 @@ module.exports = React.createClass({
/* is this the focused event */ /* is this the focused event */
isSelectedEvent: React.PropTypes.bool, isSelectedEvent: React.PropTypes.bool,
/* callback called when images in events are loaded */ /* callback called when dynamic content in events are loaded */
onImageLoad: React.PropTypes.func, onWidgetLoad: React.PropTypes.func,
}, },
getInitialState: function() { getInitialState: function() {
@ -345,7 +345,7 @@ module.exports = React.createClass({
<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} /> onWidgetLoad={this.props.onWidgetLoad} />
</div> </div>
</div> </div>
); );

View file

@ -19,6 +19,7 @@ limitations under the License.
var React = require('react'); var React = require('react');
var MatrixClientPeg = require('../../../MatrixClientPeg'); var MatrixClientPeg = require('../../../MatrixClientPeg');
var ImageUtils = require('../../../ImageUtils');
var linkify = require('linkifyjs'); var linkify = require('linkifyjs');
var linkifyElement = require('linkifyjs/element'); var linkifyElement = require('linkifyjs/element');
@ -29,7 +30,8 @@ module.exports = React.createClass({
displayName: 'LinkPreviewWidget', displayName: 'LinkPreviewWidget',
propTypes: { propTypes: {
link: React.PropTypes.string.isRequired link: React.PropTypes.string.isRequired,
onWidgetLoad: React.PropTypes.func,
}, },
getInitialState: function() { getInitialState: function() {
@ -41,6 +43,7 @@ module.exports = React.createClass({
componentWillMount: function() { componentWillMount: function() {
MatrixClientPeg.get().getUrlPreview(this.props.link).then((res)=>{ MatrixClientPeg.get().getUrlPreview(this.props.link).then((res)=>{
this.setState({ preview: res }); this.setState({ preview: res });
this.props.onWidgetLoad();
}, (error)=>{ }, (error)=>{
console.error("Failed to get preview for " + this.props.link + " " + error); console.error("Failed to get preview for " + this.props.link + " " + error);
}); });
@ -59,13 +62,28 @@ module.exports = React.createClass({
render: function() { render: function() {
var p = this.state.preview; var p = this.state.preview;
if (!p) return <div/>; if (!p) return <div/>;
var img = p["og:image"]
if (img && img.startsWith("mxc://")) img = MatrixClientPeg.get().mxcUrlToHttp(img, 100, 100) var image = p["og:image"];
var imageMaxWidth = 100, imageMaxHeight = 100;
if (image && image.startsWith("mxc://")) {
image = MatrixClientPeg.get().mxcUrlToHttp(image, imageMaxWidth, imageMaxHeight);
}
var thumbHeight = imageMaxHeight;
if (p["og:image:width"] && p["og:image:height"]) {
thumbHeight = ImageUtils.thumbHeight(p["og:image:width"], p["og:image:height"], imageMaxWidth, imageMaxHeight);
}
var img;
if (image) {
img = <div className="mx_LinkPreviewWidget_image" style={{ height: thumbHeight }}>
<a href={ this.props.link } target="_blank"><img style={{ maxWidth: imageMaxWidth, maxHeight: imageMaxHeight }} src={ image }/></a>
</div>
}
return ( return (
<div className="mx_LinkPreviewWidget"> <div className="mx_LinkPreviewWidget" >
<div className="mx_LinkPreviewWidget_image"> { img }
<a href={ this.props.link } target="_blank"><img style={{ maxWidth: 100, maxHeight: 100 }} src={ img }/></a>
</div>
<div className="mx_LinkPreviewWidget_caption"> <div className="mx_LinkPreviewWidget_caption">
<div className="mx_LinkPreviewWidget_title"><a href={ this.props.link } target="_blank">{ p["og:title"] }</a></div> <div className="mx_LinkPreviewWidget_title"><a href={ this.props.link } target="_blank">{ p["og:title"] }</a></div>
<div className="mx_LinkPreviewWidget_siteName">{ p["og:site_name"] ? (" - " + p["og:site_name"]) : null }</div> <div className="mx_LinkPreviewWidget_siteName">{ p["og:site_name"] ? (" - " + p["og:site_name"]) : null }</div>

View file

@ -32,7 +32,7 @@ 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, onWidgetLoad: React.PropTypes.func,
}, },
render: function() { render: function() {
@ -56,7 +56,7 @@ 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} />); onWidgetLoad={this.props.onWidgetLoad} />);
} }
} }
return ( return (