diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 9034123e52..b1f88a6221 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -387,7 +387,7 @@ module.exports = React.createClass({ ret.push( { eventTiles } @@ -517,7 +517,7 @@ module.exports = React.createClass({ data-scroll-tokens={scrollToken}> { + const onHeightChanged = () => { const scrollPanel = this.refs.searchResultsPanel; if (scrollPanel) { scrollPanel.checkScroll(); @@ -1231,7 +1231,7 @@ module.exports = React.createClass({ searchHighlights={this.state.searchHighlights} resultLink={resultLink} permalinkCreator={this.state.permalinkCreator} - onWidgetLoad={onWidgetLoad} />); + onHeightChanged={onHeightChanged} />); } return ret; }, diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index 41ee602c68..ee4045c91e 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -79,26 +79,6 @@ if (DEBUG_SCROLL) { * offset as normal. */ - -function createTimelineResizeDetector(scrollNode, itemlist, callback) { - if (typeof ResizeObserver !== "undefined") { - const ro = new ResizeObserver(callback); - ro.observe(itemlist); - return ro; - } else if (typeof IntersectionObserver !== "undefined") { - const threshold = []; - for (let i = 0; i <= 1000; ++i) { - threshold.push(i / 1000); - } - const io = new IntersectionObserver( - callback, - {root: scrollNode, threshold}, - ); - io.observe(itemlist); - return io; - } -} - module.exports = React.createClass({ displayName: 'ScrollPanel', @@ -181,12 +161,6 @@ module.exports = React.createClass({ componentDidMount: function() { this.checkScroll(); - - this._timelineSizeObserver = createTimelineResizeDetector( - this._getScrollNode(), - this.refs.itemlist, - () => { this._restoreSavedScrollState(); }, - ); }, componentDidUpdate: function() { @@ -204,10 +178,6 @@ module.exports = React.createClass({ // // (We could use isMounted(), but facebook have deprecated that.) this.unmounted = true; - if (this._timelineSizeObserver) { - this._timelineSizeObserver.disconnect(); - this._timelineSizeObserver = null; - } }, onScroll: function(ev) { @@ -601,16 +571,17 @@ module.exports = React.createClass({ } const scrollNode = this._getScrollNode(); - const scrollBottom = scrollNode.scrollTop + scrollNode.clientHeight; - + const scrollTop = scrollNode.scrollTop; + const viewportBottom = scrollTop + scrollNode.clientHeight; const nodeBottom = node.offsetTop + node.clientHeight; - const scrollDelta = nodeBottom + pixelOffset - scrollBottom; + const intendedViewportBottom = nodeBottom + pixelOffset; + const scrollDelta = intendedViewportBottom - viewportBottom; debuglog("ScrollPanel: scrolling to token '" + scrollToken + "'+" + pixelOffset + " (delta: "+scrollDelta+")"); - if (scrollDelta != 0) { - this._setScrollTop(scrollNode.scrollTop + scrollDelta); + if (scrollDelta !== 0) { + this._setScrollTop(scrollTop + scrollDelta); } }, @@ -622,7 +593,7 @@ module.exports = React.createClass({ } const scrollNode = this._getScrollNode(); - const scrollBottom = scrollNode.scrollTop + scrollNode.clientHeight; + const viewportBottom = scrollNode.scrollTop + scrollNode.clientHeight; const itemlist = this.refs.itemlist; const messages = itemlist.children; @@ -636,7 +607,7 @@ module.exports = React.createClass({ node = messages[i]; // break at the first message (coming from the bottom) // that has it's offsetTop above the bottom of the viewport. - if (node.offsetTop < scrollBottom) { + if (node.offsetTop < viewportBottom) { // Use this node as the scrollToken break; } @@ -652,7 +623,7 @@ module.exports = React.createClass({ this.scrollState = { stuckAtBottom: false, trackedScrollToken: node.dataset.scrollTokens.split(',')[0], - pixelOffset: scrollBottom - nodeBottom, + pixelOffset: viewportBottom - nodeBottom, }; }, diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index 19b28d37a3..f3cd6e144d 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -31,7 +31,7 @@ export default class ReplyThread extends React.Component { // the latest event in this chain of replies parentEv: PropTypes.instanceOf(MatrixEvent), // called when the ReplyThread contents has changed, including EventTiles thereof - onWidgetLoad: PropTypes.func.isRequired, + onHeightChanged: PropTypes.func.isRequired, permalinkCreator: PropTypes.instanceOf(RoomPermalinkCreator).isRequired, }; @@ -160,11 +160,11 @@ export default class ReplyThread extends React.Component { }; } - static makeThread(parentEv, onWidgetLoad, permalinkCreator, ref) { + static makeThread(parentEv, onHeightChanged, permalinkCreator, ref) { if (!ReplyThread.getParentEventId(parentEv)) { return
; } - return ; } @@ -175,7 +175,7 @@ export default class ReplyThread extends React.Component { } componentDidUpdate() { - this.props.onWidgetLoad(); + this.props.onHeightChanged(); } componentWillUnmount() { @@ -295,7 +295,7 @@ export default class ReplyThread extends React.Component { { dateSep } ; diff --git a/src/components/views/messages/MFileBody.js b/src/components/views/messages/MFileBody.js index 781d340252..7960db0384 100644 --- a/src/components/views/messages/MFileBody.js +++ b/src/components/views/messages/MFileBody.js @@ -203,6 +203,17 @@ module.exports = React.createClass({ }; }, + propTypes: { + /* the MatrixEvent to show */ + mxEvent: PropTypes.object.isRequired, + /* already decrypted blob */ + decryptedBlob: PropTypes.object, + /* called when the download link iframe is shown */ + onHeightChanged: PropTypes.func, + /* the shape of the tile, used */ + tileShape: PropTypes.string, + }, + contextTypes: { appConfig: PropTypes.object, }, @@ -248,6 +259,12 @@ module.exports = React.createClass({ this.tint(); }, + componentDidUpdate: function(prevProps, prevState) { + if (this.props.onHeightChanged && !prevState.decryptedBlob && this.state.decryptedBlob) { + this.props.onHeightChanged(); + } + }, + componentWillUnmount: function() { // Remove this from the list of mounted components delete mounts[this.id]; diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js index dd1165cf58..882d315d72 100644 --- a/src/components/views/messages/MImageBody.js +++ b/src/components/views/messages/MImageBody.js @@ -34,7 +34,7 @@ export default class MImageBody extends React.Component { mxEvent: PropTypes.object.isRequired, /* called when the image has loaded */ - onWidgetLoad: PropTypes.func.isRequired, + onHeightChanged: PropTypes.func.isRequired, /* the maximum image height to use */ maxImageHeight: PropTypes.number, @@ -144,7 +144,7 @@ export default class MImageBody extends React.Component { } onImageLoad() { - this.props.onWidgetLoad(); + this.props.onHeightChanged(); let loadedImageDimensions; diff --git a/src/components/views/messages/MVideoBody.js b/src/components/views/messages/MVideoBody.js index f1199263e7..e864b983d3 100644 --- a/src/components/views/messages/MVideoBody.js +++ b/src/components/views/messages/MVideoBody.js @@ -33,7 +33,7 @@ module.exports = React.createClass({ mxEvent: PropTypes.object.isRequired, /* called when the video has loaded */ - onWidgetLoad: PropTypes.func.isRequired, + onHeightChanged: PropTypes.func.isRequired, }, getInitialState: function() { @@ -108,7 +108,7 @@ module.exports = React.createClass({ decryptedThumbnailUrl: thumbnailUrl, decryptedBlob: decryptedBlob, }); - this.props.onWidgetLoad(); + this.props.onHeightChanged(); }); }).catch((err) => { console.warn("Unable to decrypt attachment: ", err); diff --git a/src/components/views/messages/MessageEvent.js b/src/components/views/messages/MessageEvent.js index fd51e6074b..010b1d8751 100644 --- a/src/components/views/messages/MessageEvent.js +++ b/src/components/views/messages/MessageEvent.js @@ -37,7 +37,7 @@ module.exports = React.createClass({ showUrlPreview: PropTypes.bool, /* callback called when dynamic content in events are loaded */ - onWidgetLoad: PropTypes.func, + onHeightChanged: PropTypes.func, /* the shape of the tile, used */ tileShape: PropTypes.string, @@ -89,6 +89,6 @@ module.exports = React.createClass({ showUrlPreview={this.props.showUrlPreview} tileShape={this.props.tileShape} maxImageHeight={this.props.maxImageHeight} - onWidgetLoad={this.props.onWidgetLoad} />; + onHeightChanged={this.props.onHeightChanged} />; }, }); diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index ec8c18cd7e..1b6c9418a9 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -52,7 +52,7 @@ module.exports = React.createClass({ showUrlPreview: PropTypes.bool, /* callback for when our widget has loaded */ - onWidgetLoad: PropTypes.func, + onHeightChanged: PropTypes.func, /* the shape of the tile, used */ tileShape: PropTypes.string, @@ -451,7 +451,7 @@ module.exports = React.createClass({ link={link} mxEvent={this.props.mxEvent} onCancelClick={this.onCancelClick} - onWidgetLoad={this.props.onWidgetLoad} />; + onHeightChanged={this.props.onHeightChanged} />; }); } diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 804789f306..d7c9f1e443 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -129,7 +129,7 @@ module.exports = withMatrixClient(React.createClass({ isSelectedEvent: PropTypes.bool, /* callback called when dynamic content in events are loaded */ - onWidgetLoad: PropTypes.func, + onHeightChanged: PropTypes.func, /* a list of read-receipts we should show. Each object has a 'roomMember' and 'ts'. */ readReceipts: PropTypes.arrayOf(React.PropTypes.object), @@ -165,8 +165,8 @@ module.exports = withMatrixClient(React.createClass({ getDefaultProps: function() { return { - // no-op function because onWidgetLoad is optional yet some sub-components assume its existence - onWidgetLoad: function() {}, + // no-op function because onHeightChanged is optional yet some sub-components assume its existence + onHeightChanged: function() {}, }; }, @@ -223,7 +223,7 @@ module.exports = withMatrixClient(React.createClass({ */ _onDecrypted: function() { // we need to re-verify the sending device. - // (we call onWidgetLoad in _verifyEvent to handle the case where decryption + // (we call onHeightChanged in _verifyEvent to handle the case where decryption // has caused a change in size of the event tile) this._verifyEvent(this.props.mxEvent); this.forceUpdate(); @@ -245,7 +245,7 @@ module.exports = withMatrixClient(React.createClass({ verified: verified, }, () => { // Decryption may have caused a change in size - this.props.onWidgetLoad(); + this.props.onHeightChanged(); }); }, @@ -667,7 +667,7 @@ module.exports = withMatrixClient(React.createClass({ highlights={this.props.highlights} highlightLink={this.props.highlightLink} showUrlPreview={this.props.showUrlPreview} - onWidgetLoad={this.props.onWidgetLoad} /> + onHeightChanged={this.props.onHeightChanged} />
); @@ -682,7 +682,7 @@ module.exports = withMatrixClient(React.createClass({ highlightLink={this.props.highlightLink} showUrlPreview={this.props.showUrlPreview} tileShape={this.props.tileShape} - onWidgetLoad={this.props.onWidgetLoad} /> + onHeightChanged={this.props.onHeightChanged} /> @@ -732,7 +732,7 @@ module.exports = withMatrixClient(React.createClass({ default: { const thread = ReplyThread.makeThread( this.props.mxEvent, - this.props.onWidgetLoad, + this.props.onHeightChanged, this.props.permalinkCreator, 'replyThread', ); @@ -753,7 +753,7 @@ module.exports = withMatrixClient(React.createClass({ highlights={this.props.highlights} highlightLink={this.props.highlightLink} showUrlPreview={this.props.showUrlPreview} - onWidgetLoad={this.props.onWidgetLoad} /> + onHeightChanged={this.props.onHeightChanged} /> { keyRequestInfo } { editButton } diff --git a/src/components/views/rooms/LinkPreviewWidget.js b/src/components/views/rooms/LinkPreviewWidget.js index 78796a8f14..5f32a6a613 100644 --- a/src/components/views/rooms/LinkPreviewWidget.js +++ b/src/components/views/rooms/LinkPreviewWidget.js @@ -32,7 +32,7 @@ module.exports = React.createClass({ link: PropTypes.string.isRequired, // the URL being previewed mxEvent: PropTypes.object.isRequired, // the Event associated with the preview onCancelClick: PropTypes.func, // called when the preview's cancel ('hide') button is clicked - onWidgetLoad: PropTypes.func, // called when the preview's contents has loaded + onHeightChanged: PropTypes.func, // called when the preview's contents has loaded }, getInitialState: function() { @@ -49,7 +49,7 @@ module.exports = React.createClass({ } this.setState( { preview: res }, - this.props.onWidgetLoad, + this.props.onHeightChanged, ); }, (error)=>{ console.error("Failed to get preview for " + this.props.link + " " + error); diff --git a/src/components/views/rooms/PinnedEventTile.js b/src/components/views/rooms/PinnedEventTile.js index 3253340a76..8b2b217e19 100644 --- a/src/components/views/rooms/PinnedEventTile.js +++ b/src/components/views/rooms/PinnedEventTile.js @@ -92,7 +92,7 @@ module.exports = React.createClass({
{}} // we need to give this, apparently + onHeightChanged={() => {}} // we need to give this, apparently />
diff --git a/src/components/views/rooms/SearchResultTile.js b/src/components/views/rooms/SearchResultTile.js index eff3fdc89d..e396ea7011 100644 --- a/src/components/views/rooms/SearchResultTile.js +++ b/src/components/views/rooms/SearchResultTile.js @@ -33,7 +33,7 @@ module.exports = React.createClass({ // href for the highlights in this result resultLink: PropTypes.string, - onWidgetLoad: PropTypes.func, + onHeightChanged: PropTypes.func, }, render: function() { @@ -58,7 +58,7 @@ module.exports = React.createClass({ ret.push(); + onHeightChanged={this.props.onHeightChanged} />); } } return (