Merge pull request #3656 from matrix-org/dbkr/messagepanel_react_class
Convert MessagePanel to React class
This commit is contained in:
commit
ee40c7891b
1 changed files with 72 additions and 69 deletions
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
Copyright 2018 New Vector 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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -18,7 +19,6 @@ limitations under the License.
|
||||||
/* global Velocity */
|
/* global Velocity */
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
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.
|
/* (almost) stateless UI component which builds the event tiles in the room timeline.
|
||||||
*/
|
*/
|
||||||
module.exports = createReactClass({
|
export default class MessagePanel extends React.Component {
|
||||||
displayName: 'MessagePanel',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// true to give the component a 'display: none' style.
|
// true to give the component a 'display: none' style.
|
||||||
hidden: PropTypes.bool,
|
hidden: PropTypes.bool,
|
||||||
|
|
||||||
|
@ -109,9 +107,10 @@ module.exports = createReactClass({
|
||||||
|
|
||||||
// whether to show reactions for an event
|
// whether to show reactions for an event
|
||||||
showReactions: PropTypes.bool,
|
showReactions: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
componentWillMount: function() {
|
constructor() {
|
||||||
|
super();
|
||||||
// the event after which we put a visible unread marker on the last
|
// the event after which we put a visible unread marker on the last
|
||||||
// render cycle; null if readMarkerVisible was false or the RM was
|
// render cycle; null if readMarkerVisible was false or the RM was
|
||||||
// suppressed (eg because it was at the end of the timeline)
|
// suppressed (eg because it was at the end of the timeline)
|
||||||
|
@ -167,38 +166,42 @@ module.exports = createReactClass({
|
||||||
this._showHiddenEventsInTimeline =
|
this._showHiddenEventsInTimeline =
|
||||||
SettingsStore.getValue("showHiddenEventsInTimeline");
|
SettingsStore.getValue("showHiddenEventsInTimeline");
|
||||||
|
|
||||||
this._isMounted = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
|
||||||
this._isMounted = false;
|
this._isMounted = false;
|
||||||
},
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this._isMounted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this._isMounted = false;
|
||||||
|
}
|
||||||
|
|
||||||
/* get the DOM node representing the given event */
|
/* get the DOM node representing the given event */
|
||||||
getNodeForEventId: function(eventId) {
|
getNodeForEventId(eventId) {
|
||||||
if (!this.eventNodes) {
|
if (!this.eventNodes) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.eventNodes[eventId];
|
return this.eventNodes[eventId];
|
||||||
},
|
}
|
||||||
|
|
||||||
/* 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.
|
||||||
*/
|
*/
|
||||||
isAtBottom: function() {
|
isAtBottom() {
|
||||||
return this.refs.scrollPanel
|
return this.refs.scrollPanel
|
||||||
&& this.refs.scrollPanel.isAtBottom();
|
&& this.refs.scrollPanel.isAtBottom();
|
||||||
},
|
}
|
||||||
|
|
||||||
/* get the current scroll state. See ScrollPanel.getScrollState for
|
/* get the current scroll state. See ScrollPanel.getScrollState for
|
||||||
* details.
|
* details.
|
||||||
*
|
*
|
||||||
* returns null if we are not mounted.
|
* returns null if we are not mounted.
|
||||||
*/
|
*/
|
||||||
getScrollState: function() {
|
getScrollState() {
|
||||||
if (!this.refs.scrollPanel) { return null; }
|
if (!this.refs.scrollPanel) { return null; }
|
||||||
return this.refs.scrollPanel.getScrollState();
|
return this.refs.scrollPanel.getScrollState();
|
||||||
},
|
}
|
||||||
|
|
||||||
// returns one of:
|
// returns one of:
|
||||||
//
|
//
|
||||||
|
@ -206,7 +209,7 @@ module.exports = createReactClass({
|
||||||
// -1: read marker is above the window
|
// -1: read marker is above the window
|
||||||
// 0: read marker is within the window
|
// 0: read marker is within the window
|
||||||
// +1: read marker is below the window
|
// +1: read marker is below the window
|
||||||
getReadMarkerPosition: function() {
|
getReadMarkerPosition() {
|
||||||
const readMarker = this.refs.readMarkerNode;
|
const readMarker = this.refs.readMarkerNode;
|
||||||
const messageWrapper = this.refs.scrollPanel;
|
const messageWrapper = this.refs.scrollPanel;
|
||||||
|
|
||||||
|
@ -226,45 +229,45 @@ module.exports = createReactClass({
|
||||||
} else {
|
} else {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
/* jump to the top of the content.
|
/* jump to the top of the content.
|
||||||
*/
|
*/
|
||||||
scrollToTop: function() {
|
scrollToTop() {
|
||||||
if (this.refs.scrollPanel) {
|
if (this.refs.scrollPanel) {
|
||||||
this.refs.scrollPanel.scrollToTop();
|
this.refs.scrollPanel.scrollToTop();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
/* jump to the bottom of the content.
|
/* jump to the bottom of the content.
|
||||||
*/
|
*/
|
||||||
scrollToBottom: function() {
|
scrollToBottom() {
|
||||||
if (this.refs.scrollPanel) {
|
if (this.refs.scrollPanel) {
|
||||||
this.refs.scrollPanel.scrollToBottom();
|
this.refs.scrollPanel.scrollToBottom();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page up/down.
|
* Page up/down.
|
||||||
*
|
*
|
||||||
* @param {number} mult: -1 to page up, +1 to page down
|
* @param {number} mult: -1 to page up, +1 to page down
|
||||||
*/
|
*/
|
||||||
scrollRelative: function(mult) {
|
scrollRelative(mult) {
|
||||||
if (this.refs.scrollPanel) {
|
if (this.refs.scrollPanel) {
|
||||||
this.refs.scrollPanel.scrollRelative(mult);
|
this.refs.scrollPanel.scrollRelative(mult);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scroll up/down in response to a scroll key
|
* Scroll up/down in response to a scroll key
|
||||||
*
|
*
|
||||||
* @param {KeyboardEvent} ev: the keyboard event to handle
|
* @param {KeyboardEvent} ev: the keyboard event to handle
|
||||||
*/
|
*/
|
||||||
handleScrollKey: function(ev) {
|
handleScrollKey(ev) {
|
||||||
if (this.refs.scrollPanel) {
|
if (this.refs.scrollPanel) {
|
||||||
this.refs.scrollPanel.handleScrollKey(ev);
|
this.refs.scrollPanel.handleScrollKey(ev);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
/* jump to the given event id.
|
/* jump to the given event id.
|
||||||
*
|
*
|
||||||
|
@ -276,33 +279,33 @@ module.exports = createReactClass({
|
||||||
* node (specifically, the bottom of it) will be positioned. If omitted, it
|
* node (specifically, the bottom of it) will be positioned. If omitted, it
|
||||||
* defaults to 0.
|
* defaults to 0.
|
||||||
*/
|
*/
|
||||||
scrollToEvent: function(eventId, pixelOffset, offsetBase) {
|
scrollToEvent(eventId, pixelOffset, offsetBase) {
|
||||||
if (this.refs.scrollPanel) {
|
if (this.refs.scrollPanel) {
|
||||||
this.refs.scrollPanel.scrollToToken(eventId, pixelOffset, offsetBase);
|
this.refs.scrollPanel.scrollToToken(eventId, pixelOffset, offsetBase);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
scrollToEventIfNeeded: function(eventId) {
|
scrollToEventIfNeeded(eventId) {
|
||||||
const node = this.eventNodes[eventId];
|
const node = this.eventNodes[eventId];
|
||||||
if (node) {
|
if (node) {
|
||||||
node.scrollIntoView({block: "nearest", behavior: "instant"});
|
node.scrollIntoView({block: "nearest", behavior: "instant"});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
/* check the scroll state and send out pagination requests if necessary.
|
/* check the scroll state and send out pagination requests if necessary.
|
||||||
*/
|
*/
|
||||||
checkFillState: function() {
|
checkFillState() {
|
||||||
if (this.refs.scrollPanel) {
|
if (this.refs.scrollPanel) {
|
||||||
this.refs.scrollPanel.checkFillState();
|
this.refs.scrollPanel.checkFillState();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_isUnmounting: function() {
|
_isUnmounting() {
|
||||||
return !this._isMounted;
|
return !this._isMounted;
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: Implement granular (per-room) hide options
|
// TODO: Implement granular (per-room) hide options
|
||||||
_shouldShowEvent: function(mxEv) {
|
_shouldShowEvent(mxEv) {
|
||||||
if (mxEv.sender && MatrixClientPeg.get().isUserIgnored(mxEv.sender.userId)) {
|
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)
|
return false; // ignored = no show (only happens if the ignore happens after an event was received)
|
||||||
}
|
}
|
||||||
|
@ -320,9 +323,9 @@ module.exports = createReactClass({
|
||||||
if (this.props.highlightedEventId === mxEv.getId()) return true;
|
if (this.props.highlightedEventId === mxEv.getId()) return true;
|
||||||
|
|
||||||
return !shouldHideEvent(mxEv);
|
return !shouldHideEvent(mxEv);
|
||||||
},
|
}
|
||||||
|
|
||||||
_getEventTiles: function() {
|
_getEventTiles() {
|
||||||
const DateSeparator = sdk.getComponent('messages.DateSeparator');
|
const DateSeparator = sdk.getComponent('messages.DateSeparator');
|
||||||
const EventListSummary = sdk.getComponent('views.elements.EventListSummary');
|
const EventListSummary = sdk.getComponent('views.elements.EventListSummary');
|
||||||
const MemberEventListSummary = sdk.getComponent('views.elements.MemberEventListSummary');
|
const MemberEventListSummary = sdk.getComponent('views.elements.MemberEventListSummary');
|
||||||
|
@ -596,9 +599,9 @@ module.exports = createReactClass({
|
||||||
|
|
||||||
this.currentReadMarkerEventId = readMarkerVisible ? this.props.readMarkerEventId : null;
|
this.currentReadMarkerEventId = readMarkerVisible ? this.props.readMarkerEventId : null;
|
||||||
return ret;
|
return ret;
|
||||||
},
|
}
|
||||||
|
|
||||||
_getTilesForEvent: function(prevEvent, mxEv, last) {
|
_getTilesForEvent(prevEvent, mxEv, last) {
|
||||||
const EventTile = sdk.getComponent('rooms.EventTile');
|
const EventTile = sdk.getComponent('rooms.EventTile');
|
||||||
const DateSeparator = sdk.getComponent('messages.DateSeparator');
|
const DateSeparator = sdk.getComponent('messages.DateSeparator');
|
||||||
const ret = [];
|
const ret = [];
|
||||||
|
@ -691,20 +694,20 @@ module.exports = createReactClass({
|
||||||
);
|
);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
},
|
}
|
||||||
|
|
||||||
_wantsDateSeparator: function(prevEvent, nextEventDate) {
|
_wantsDateSeparator(prevEvent, nextEventDate) {
|
||||||
if (prevEvent == null) {
|
if (prevEvent == null) {
|
||||||
// first event in the panel: depends if we could back-paginate from
|
// first event in the panel: depends if we could back-paginate from
|
||||||
// here.
|
// here.
|
||||||
return !this.props.suppressFirstDateSeparator;
|
return !this.props.suppressFirstDateSeparator;
|
||||||
}
|
}
|
||||||
return wantsDateSeparator(prevEvent.getDate(), nextEventDate);
|
return wantsDateSeparator(prevEvent.getDate(), nextEventDate);
|
||||||
},
|
}
|
||||||
|
|
||||||
// Get a list of read receipts that should be shown next to this event
|
// Get a list of read receipts that should be shown next to this event
|
||||||
// Receipts are objects which have a 'userId', 'roomMember' and 'ts'.
|
// Receipts are objects which have a 'userId', 'roomMember' and 'ts'.
|
||||||
_getReadReceiptsForEvent: function(event) {
|
_getReadReceiptsForEvent(event) {
|
||||||
const myUserId = MatrixClientPeg.get().credentials.userId;
|
const myUserId = MatrixClientPeg.get().credentials.userId;
|
||||||
|
|
||||||
// get list of read receipts, sorted most recent first
|
// get list of read receipts, sorted most recent first
|
||||||
|
@ -728,12 +731,12 @@ module.exports = createReactClass({
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return receipts;
|
return receipts;
|
||||||
},
|
}
|
||||||
|
|
||||||
// Get an object that maps from event ID to a list of read receipts that
|
// 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,
|
// 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.
|
// they are folded into the receipts of the last shown event.
|
||||||
_getReadReceiptsByShownEvent: function() {
|
_getReadReceiptsByShownEvent() {
|
||||||
const receiptsByEvent = {};
|
const receiptsByEvent = {};
|
||||||
const receiptsByUserId = {};
|
const receiptsByUserId = {};
|
||||||
|
|
||||||
|
@ -786,9 +789,9 @@ module.exports = createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return receiptsByEvent;
|
return receiptsByEvent;
|
||||||
},
|
}
|
||||||
|
|
||||||
_getReadMarkerTile: function(visible) {
|
_getReadMarkerTile(visible) {
|
||||||
let hr;
|
let hr;
|
||||||
if (visible) {
|
if (visible) {
|
||||||
hr = <hr className="mx_RoomView_myReadMarker"
|
hr = <hr className="mx_RoomView_myReadMarker"
|
||||||
|
@ -802,9 +805,9 @@ module.exports = createReactClass({
|
||||||
{ hr }
|
{ hr }
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
_startAnimation: function(ghostNode) {
|
_startAnimation = (ghostNode) => {
|
||||||
if (this._readMarkerGhostNode) {
|
if (this._readMarkerGhostNode) {
|
||||||
Velocity.Utilities.removeData(this._readMarkerGhostNode);
|
Velocity.Utilities.removeData(this._readMarkerGhostNode);
|
||||||
}
|
}
|
||||||
|
@ -816,9 +819,9 @@ module.exports = createReactClass({
|
||||||
{duration: 400, easing: 'easeInSine',
|
{duration: 400, easing: 'easeInSine',
|
||||||
delay: 1000});
|
delay: 1000});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_getReadMarkerGhostTile: function() {
|
_getReadMarkerGhostTile() {
|
||||||
const hr = <hr className="mx_RoomView_myReadMarker"
|
const hr = <hr className="mx_RoomView_myReadMarker"
|
||||||
style={{opacity: 1, width: '99%'}}
|
style={{opacity: 1, width: '99%'}}
|
||||||
ref={this._startAnimation}
|
ref={this._startAnimation}
|
||||||
|
@ -833,31 +836,31 @@ module.exports = createReactClass({
|
||||||
{ hr }
|
{ hr }
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
_collectEventNode: function(eventId, node) {
|
_collectEventNode = (eventId, node) => {
|
||||||
this.eventNodes[eventId] = node;
|
this.eventNodes[eventId] = node;
|
||||||
},
|
}
|
||||||
|
|
||||||
// once dynamic content in the events load, make the scrollPanel check the
|
// once dynamic content in the events load, make the scrollPanel check the
|
||||||
// scroll offsets.
|
// scroll offsets.
|
||||||
_onHeightChanged: function() {
|
_onHeightChanged = () => {
|
||||||
const scrollPanel = this.refs.scrollPanel;
|
const scrollPanel = this.refs.scrollPanel;
|
||||||
if (scrollPanel) {
|
if (scrollPanel) {
|
||||||
scrollPanel.checkScroll();
|
scrollPanel.checkScroll();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_onTypingShown: function() {
|
_onTypingShown = () => {
|
||||||
const scrollPanel = this.refs.scrollPanel;
|
const scrollPanel = this.refs.scrollPanel;
|
||||||
// this will make the timeline grow, so checkScroll
|
// this will make the timeline grow, so checkScroll
|
||||||
scrollPanel.checkScroll();
|
scrollPanel.checkScroll();
|
||||||
if (scrollPanel && scrollPanel.getScrollState().stuckAtBottom) {
|
if (scrollPanel && scrollPanel.getScrollState().stuckAtBottom) {
|
||||||
scrollPanel.preventShrinking();
|
scrollPanel.preventShrinking();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_onTypingHidden: function() {
|
_onTypingHidden = () => {
|
||||||
const scrollPanel = this.refs.scrollPanel;
|
const scrollPanel = this.refs.scrollPanel;
|
||||||
if (scrollPanel) {
|
if (scrollPanel) {
|
||||||
// as hiding the typing notifications doesn't
|
// as hiding the typing notifications doesn't
|
||||||
|
@ -868,9 +871,9 @@ module.exports = createReactClass({
|
||||||
// reveal added padding to balance the notifs disappearing.
|
// reveal added padding to balance the notifs disappearing.
|
||||||
scrollPanel.checkScroll();
|
scrollPanel.checkScroll();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
updateTimelineMinHeight: function() {
|
updateTimelineMinHeight() {
|
||||||
const scrollPanel = this.refs.scrollPanel;
|
const scrollPanel = this.refs.scrollPanel;
|
||||||
|
|
||||||
if (scrollPanel) {
|
if (scrollPanel) {
|
||||||
|
@ -885,16 +888,16 @@ module.exports = createReactClass({
|
||||||
scrollPanel.preventShrinking();
|
scrollPanel.preventShrinking();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
onTimelineReset: function() {
|
onTimelineReset() {
|
||||||
const scrollPanel = this.refs.scrollPanel;
|
const scrollPanel = this.refs.scrollPanel;
|
||||||
if (scrollPanel) {
|
if (scrollPanel) {
|
||||||
scrollPanel.clearPreventShrinking();
|
scrollPanel.clearPreventShrinking();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const ScrollPanel = sdk.getComponent("structures.ScrollPanel");
|
const ScrollPanel = sdk.getComponent("structures.ScrollPanel");
|
||||||
const WhoIsTypingTile = sdk.getComponent("rooms.WhoIsTypingTile");
|
const WhoIsTypingTile = sdk.getComponent("rooms.WhoIsTypingTile");
|
||||||
const Spinner = sdk.getComponent("elements.Spinner");
|
const Spinner = sdk.getComponent("elements.Spinner");
|
||||||
|
@ -941,5 +944,5 @@ module.exports = createReactClass({
|
||||||
{ bottomSpinner }
|
{ bottomSpinner }
|
||||||
</ScrollPanel>
|
</ScrollPanel>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
Loading…
Reference in a new issue