From 6d4abeef4515bab769d04359a2ded0ca70219d57 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 22 Nov 2019 12:07:25 +0000 Subject: [PATCH 1/4] Convert MessagePanel to React class I was about to add the getDerivedStateFromProps function to change how read markers worked, but doing that in an old style class means the statics object, so let;s just convert the thing. --- src/components/structures/MessagePanel.js | 132 +++++++++++----------- 1 file changed, 65 insertions(+), 67 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index cf2a5b1738..3781dd0ce7 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -1,6 +1,7 @@ /* Copyright 2016 OpenMarket Ltd Copyright 2018 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,7 +19,6 @@ limitations under the License. /* global Velocity */ import React from 'react'; -import createReactClass from 'create-react-class'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; import classNames from 'classnames'; @@ -37,10 +37,8 @@ const isMembershipChange = (e) => e.getType() === 'm.room.member' || e.getType() /* (almost) stateless UI component which builds the event tiles in the room timeline. */ -module.exports = createReactClass({ - displayName: 'MessagePanel', - - propTypes: { +export default class MessagePanel extends React.Component { + propTypes = { // true to give the component a 'display: none' style. hidden: PropTypes.bool, @@ -109,9 +107,9 @@ module.exports = createReactClass({ // whether to show reactions for an event showReactions: PropTypes.bool, - }, + }; - componentWillMount: function() { + componentDidMount() { // the event after which we put a visible unread marker on the last // render cycle; null if readMarkerVisible was false or the RM was // suppressed (eg because it was at the end of the timeline) @@ -168,37 +166,37 @@ module.exports = createReactClass({ SettingsStore.getValue("showHiddenEventsInTimeline"); this._isMounted = true; - }, + } - componentWillUnmount: function() { + componentWillUnmount() { this._isMounted = false; - }, + } /* get the DOM node representing the given event */ - getNodeForEventId: function(eventId) { + getNodeForEventId(eventId) { if (!this.eventNodes) { return undefined; } return this.eventNodes[eventId]; - }, + } /* return true if the content is fully scrolled down right now; else false. */ - isAtBottom: function() { + isAtBottom() { return this.refs.scrollPanel && this.refs.scrollPanel.isAtBottom(); - }, + } /* get the current scroll state. See ScrollPanel.getScrollState for * details. * * returns null if we are not mounted. */ - getScrollState: function() { + getScrollState() { if (!this.refs.scrollPanel) { return null; } return this.refs.scrollPanel.getScrollState(); - }, + } // returns one of: // @@ -206,7 +204,7 @@ module.exports = createReactClass({ // -1: read marker is above the window // 0: read marker is within the window // +1: read marker is below the window - getReadMarkerPosition: function() { + getReadMarkerPosition() { const readMarker = this.refs.readMarkerNode; const messageWrapper = this.refs.scrollPanel; @@ -226,45 +224,45 @@ module.exports = createReactClass({ } else { return 1; } - }, + } /* jump to the top of the content. */ - scrollToTop: function() { + scrollToTop() { if (this.refs.scrollPanel) { this.refs.scrollPanel.scrollToTop(); } - }, + } /* jump to the bottom of the content. */ - scrollToBottom: function() { + scrollToBottom() { if (this.refs.scrollPanel) { this.refs.scrollPanel.scrollToBottom(); } - }, + } /** * Page up/down. * * @param {number} mult: -1 to page up, +1 to page down */ - scrollRelative: function(mult) { + scrollRelative(mult) { if (this.refs.scrollPanel) { this.refs.scrollPanel.scrollRelative(mult); } - }, + } /** * Scroll up/down in response to a scroll key * * @param {KeyboardEvent} ev: the keyboard event to handle */ - handleScrollKey: function(ev) { + handleScrollKey(ev) { if (this.refs.scrollPanel) { this.refs.scrollPanel.handleScrollKey(ev); } - }, + } /* jump to the given event id. * @@ -276,33 +274,33 @@ module.exports = createReactClass({ * node (specifically, the bottom of it) will be positioned. If omitted, it * defaults to 0. */ - scrollToEvent: function(eventId, pixelOffset, offsetBase) { + scrollToEvent(eventId, pixelOffset, offsetBase) { if (this.refs.scrollPanel) { this.refs.scrollPanel.scrollToToken(eventId, pixelOffset, offsetBase); } - }, + } - scrollToEventIfNeeded: function(eventId) { + scrollToEventIfNeeded(eventId) { const node = this.eventNodes[eventId]; if (node) { node.scrollIntoView({block: "nearest", behavior: "instant"}); } - }, + } /* check the scroll state and send out pagination requests if necessary. */ - checkFillState: function() { + checkFillState() { if (this.refs.scrollPanel) { this.refs.scrollPanel.checkFillState(); } - }, + } - _isUnmounting: function() { + _isUnmounting() { return !this._isMounted; - }, + } // TODO: Implement granular (per-room) hide options - _shouldShowEvent: function(mxEv) { + _shouldShowEvent(mxEv) { if (mxEv.sender && MatrixClientPeg.get().isUserIgnored(mxEv.sender.userId)) { return false; // ignored = no show (only happens if the ignore happens after an event was received) } @@ -320,9 +318,9 @@ module.exports = createReactClass({ if (this.props.highlightedEventId === mxEv.getId()) return true; return !shouldHideEvent(mxEv); - }, + } - _getEventTiles: function() { + _getEventTiles() { const DateSeparator = sdk.getComponent('messages.DateSeparator'); const EventListSummary = sdk.getComponent('views.elements.EventListSummary'); const MemberEventListSummary = sdk.getComponent('views.elements.MemberEventListSummary'); @@ -596,9 +594,9 @@ module.exports = createReactClass({ this.currentReadMarkerEventId = readMarkerVisible ? this.props.readMarkerEventId : null; return ret; - }, + } - _getTilesForEvent: function(prevEvent, mxEv, last) { + _getTilesForEvent(prevEvent, mxEv, last) { const EventTile = sdk.getComponent('rooms.EventTile'); const DateSeparator = sdk.getComponent('messages.DateSeparator'); const ret = []; @@ -691,20 +689,20 @@ module.exports = createReactClass({ ); return ret; - }, + } - _wantsDateSeparator: function(prevEvent, nextEventDate) { + _wantsDateSeparator(prevEvent, nextEventDate) { if (prevEvent == null) { // first event in the panel: depends if we could back-paginate from // here. return !this.props.suppressFirstDateSeparator; } return wantsDateSeparator(prevEvent.getDate(), nextEventDate); - }, + } // Get a list of read receipts that should be shown next to this event // Receipts are objects which have a 'userId', 'roomMember' and 'ts'. - _getReadReceiptsForEvent: function(event) { + _getReadReceiptsForEvent(event) { const myUserId = MatrixClientPeg.get().credentials.userId; // get list of read receipts, sorted most recent first @@ -728,12 +726,12 @@ module.exports = createReactClass({ }); }); return receipts; - }, + } // Get an object that maps from event ID to a list of read receipts that // should be shown next to that event. If a hidden event has read receipts, // they are folded into the receipts of the last shown event. - _getReadReceiptsByShownEvent: function() { + _getReadReceiptsByShownEvent() { const receiptsByEvent = {}; const receiptsByUserId = {}; @@ -786,9 +784,9 @@ module.exports = createReactClass({ } return receiptsByEvent; - }, + } - _getReadMarkerTile: function(visible) { + _getReadMarkerTile(visible) { let hr; if (visible) { hr =
); - }, + } - _startAnimation: function(ghostNode) { + _startAnimation = (ghostNode) => { if (this._readMarkerGhostNode) { Velocity.Utilities.removeData(this._readMarkerGhostNode); } @@ -816,9 +814,9 @@ module.exports = createReactClass({ {duration: 400, easing: 'easeInSine', delay: 1000}); } - }, + }; - _getReadMarkerGhostTile: function() { + _getReadMarkerGhostTile() { const hr =
); - }, + } - _collectEventNode: function(eventId, node) { + _collectEventNode = (eventId, node) => { this.eventNodes[eventId] = node; - }, + } // once dynamic content in the events load, make the scrollPanel check the // scroll offsets. - _onHeightChanged: function() { + _onHeightChanged = () => { const scrollPanel = this.refs.scrollPanel; if (scrollPanel) { scrollPanel.checkScroll(); } - }, + }; - _onTypingShown: function() { + _onTypingShown = () => { const scrollPanel = this.refs.scrollPanel; // this will make the timeline grow, so checkScroll scrollPanel.checkScroll(); if (scrollPanel && scrollPanel.getScrollState().stuckAtBottom) { scrollPanel.preventShrinking(); } - }, + }; - _onTypingHidden: function() { + _onTypingHidden = () => { const scrollPanel = this.refs.scrollPanel; if (scrollPanel) { // as hiding the typing notifications doesn't @@ -868,9 +866,9 @@ module.exports = createReactClass({ // reveal added padding to balance the notifs disappearing. scrollPanel.checkScroll(); } - }, + }; - updateTimelineMinHeight: function() { + updateTimelineMinHeight() { const scrollPanel = this.refs.scrollPanel; if (scrollPanel) { @@ -885,16 +883,16 @@ module.exports = createReactClass({ scrollPanel.preventShrinking(); } } - }, + } - onTimelineReset: function() { + onTimelineReset() { const scrollPanel = this.refs.scrollPanel; if (scrollPanel) { scrollPanel.clearPreventShrinking(); } - }, + } - render: function() { + render() { const ScrollPanel = sdk.getComponent("structures.ScrollPanel"); const WhoIsTypingTile = sdk.getComponent("rooms.WhoIsTypingTile"); const Spinner = sdk.getComponent("elements.Spinner"); @@ -941,5 +939,5 @@ module.exports = createReactClass({ { bottomSpinner } ); - }, -}); + } +} From 3f5a8faf376b33499258f071b1eefe69e8c2c5ee Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 22 Nov 2019 13:01:56 +0000 Subject: [PATCH 2/4] PropTypes should be static --- res/css/structures/_RoomView.scss | 1 + src/components/structures/MessagePanel.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 50d412ad58..8e47fe7509 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -221,6 +221,7 @@ hr.mx_RoomView_myReadMarker { position: relative; top: -1px; z-index: 1; + transition: width 1s easeInSine; } .mx_RoomView_callStatusBar .mx_UploadBar_uploadProgressInner { diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 3781dd0ce7..bd5630ab12 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -38,7 +38,7 @@ const isMembershipChange = (e) => e.getType() === 'm.room.member' || e.getType() /* (almost) stateless UI component which builds the event tiles in the room timeline. */ export default class MessagePanel extends React.Component { - propTypes = { + static propTypes = { // true to give the component a 'display: none' style. hidden: PropTypes.bool, From 0dbb639aea1ef008bcdede36899112f3b7bca1fa Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 22 Nov 2019 13:06:35 +0000 Subject: [PATCH 3/4] Accidentally committed --- res/css/structures/_RoomView.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 8e47fe7509..50d412ad58 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -221,7 +221,6 @@ hr.mx_RoomView_myReadMarker { position: relative; top: -1px; z-index: 1; - transition: width 1s easeInSine; } .mx_RoomView_callStatusBar .mx_UploadBar_uploadProgressInner { From 25ba4c5f7124d40fd5d83cddba30b1479f64dc0c Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 22 Nov 2019 13:11:36 +0000 Subject: [PATCH 4/4] Fix read markers init code needs to be a constructor or its run too late --- src/components/structures/MessagePanel.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index bd5630ab12..22be35db60 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -109,7 +109,8 @@ export default class MessagePanel extends React.Component { showReactions: PropTypes.bool, }; - componentDidMount() { + constructor() { + super(); // the event after which we put a visible unread marker on the last // render cycle; null if readMarkerVisible was false or the RM was // suppressed (eg because it was at the end of the timeline) @@ -165,6 +166,10 @@ export default class MessagePanel extends React.Component { this._showHiddenEventsInTimeline = SettingsStore.getValue("showHiddenEventsInTimeline"); + this._isMounted = false; + } + + componentDidMount() { this._isMounted = true; }