mirror of https://github.com/vector-im/riot-web
Merge pull request #278 from matrix-org/rav/optimise_eventtile_update
Avoid rerendering EventTiles when not necessarypull/21833/head
commit
b0eba8aea8
|
@ -19,6 +19,8 @@ var ReactDOM = require("react-dom");
|
||||||
var dis = require("../../dispatcher");
|
var dis = require("../../dispatcher");
|
||||||
var sdk = require('../../index');
|
var sdk = require('../../index');
|
||||||
|
|
||||||
|
var MatrixClientPeg = require('../../MatrixClientPeg')
|
||||||
|
|
||||||
/* (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 = React.createClass({
|
module.exports = React.createClass({
|
||||||
|
@ -150,7 +152,7 @@ module.exports = React.createClass({
|
||||||
this.refs.scrollPanel.scrollToBottom();
|
this.refs.scrollPanel.scrollToBottom();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page up/down.
|
* Page up/down.
|
||||||
*
|
*
|
||||||
|
@ -335,13 +337,17 @@ module.exports = React.createClass({
|
||||||
// Local echos have a send "status".
|
// Local echos have a send "status".
|
||||||
var scrollToken = mxEv.status ? undefined : eventId;
|
var scrollToken = mxEv.status ? undefined : eventId;
|
||||||
|
|
||||||
|
var readReceipts = this._getReadReceiptsForEvent(mxEv);
|
||||||
|
|
||||||
ret.push(
|
ret.push(
|
||||||
<li key={eventId}
|
<li key={eventId}
|
||||||
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}
|
onWidgetLoad={this._onWidgetLoad}
|
||||||
last={last} isSelectedEvent={highlight} />
|
readReceipts={readReceipts}
|
||||||
|
eventSendStatus={mxEv.status}
|
||||||
|
last={last} isSelectedEvent={highlight}/>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -359,6 +365,30 @@ module.exports = React.createClass({
|
||||||
!== new Date(nextEventTs).toDateString());
|
!== new Date(nextEventTs).toDateString());
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// get a list of the userids whose read receipts should
|
||||||
|
// be shown next to this event
|
||||||
|
_getReadReceiptsForEvent: function(event) {
|
||||||
|
var myUserId = MatrixClientPeg.get().credentials.userId;
|
||||||
|
|
||||||
|
// get list of read receipts, sorted most recent first
|
||||||
|
var room = MatrixClientPeg.get().getRoom(event.getRoomId());
|
||||||
|
if (!room) {
|
||||||
|
// huh.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return room.getReceiptsForEvent(event).filter(function(r) {
|
||||||
|
return r.type === "m.read" && r.userId != myUserId;
|
||||||
|
}).sort(function(r1, r2) {
|
||||||
|
return r2.data.ts - r1.data.ts;
|
||||||
|
}).map(function(r) {
|
||||||
|
return room.getMember(r.userId);
|
||||||
|
}).filter(function(m) {
|
||||||
|
// check that the user is a known room member
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
_getReadMarkerTile: function(visible) {
|
_getReadMarkerTile: function(visible) {
|
||||||
var hr;
|
var hr;
|
||||||
if (visible) {
|
if (visible) {
|
||||||
|
@ -431,7 +461,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollPanel ref="scrollPanel" className="mx_RoomView_messagePanel mx_fadable"
|
<ScrollPanel ref="scrollPanel" className="mx_RoomView_messagePanel mx_fadable"
|
||||||
onScroll={ this.props.onScroll }
|
onScroll={ this.props.onScroll }
|
||||||
onResize={ this.onResize }
|
onResize={ this.onResize }
|
||||||
onFillRequest={ this.props.onFillRequest }
|
onFillRequest={ this.props.onFillRequest }
|
||||||
style={ style }
|
style={ style }
|
||||||
|
|
|
@ -29,6 +29,8 @@ var Velociraptor = require('../../../Velociraptor');
|
||||||
require('../../../VelocityBounce');
|
require('../../../VelocityBounce');
|
||||||
var dispatcher = require("../../../dispatcher");
|
var dispatcher = require("../../../dispatcher");
|
||||||
|
|
||||||
|
var ObjectUtils = require('../../../ObjectUtils');
|
||||||
|
|
||||||
var bounce = false;
|
var bounce = false;
|
||||||
try {
|
try {
|
||||||
if (global.localStorage) {
|
if (global.localStorage) {
|
||||||
|
@ -107,12 +109,67 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
/* callback called when dynamic content in events are loaded */
|
/* callback called when dynamic content in events are loaded */
|
||||||
onWidgetLoad: React.PropTypes.func,
|
onWidgetLoad: React.PropTypes.func,
|
||||||
|
|
||||||
|
/* a list of Room Members whose read-receipts we should show */
|
||||||
|
readReceipts: React.PropTypes.arrayOf(React.PropTypes.object),
|
||||||
|
|
||||||
|
/* the status of this event - ie, mxEvent.status. Denormalised to here so
|
||||||
|
* that we can tell when it changes. */
|
||||||
|
eventSendStatus: React.PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return {menu: false, allReadAvatars: false};
|
return {menu: false, allReadAvatars: false};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
shouldComponentUpdate: function (nextProps, nextState) {
|
||||||
|
if (!ObjectUtils.shallowEqual(this.state, nextState)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._propsEqual(this.props, nextProps)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_propsEqual: function(objA, objB) {
|
||||||
|
var keysA = Object.keys(objA);
|
||||||
|
var keysB = Object.keys(objB);
|
||||||
|
|
||||||
|
if (keysA.length !== keysB.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < keysA.length; i++) {
|
||||||
|
var key = keysA[i];
|
||||||
|
|
||||||
|
if (!objB.hasOwnProperty(key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// need to deep-compare readReceipts
|
||||||
|
if (key == 'readReceipts') {
|
||||||
|
var rA = objA[key];
|
||||||
|
var rB = objB[key];
|
||||||
|
if (rA.length !== rB.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (var j = 0; j < rA.length; j++) {
|
||||||
|
if (rA[j].userId !== rB[j].userId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (objA[key] !== objB[key]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
shouldHighlight: function() {
|
shouldHighlight: function() {
|
||||||
var actions = MatrixClientPeg.get().getPushActionsForEvent(this.props.mxEvent);
|
var actions = MatrixClientPeg.get().getPushActionsForEvent(this.props.mxEvent);
|
||||||
if (!actions || !actions.tweaks) { return false; }
|
if (!actions || !actions.tweaks) { return false; }
|
||||||
|
@ -153,20 +210,6 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
getReadAvatars: function() {
|
getReadAvatars: function() {
|
||||||
var avatars = [];
|
var avatars = [];
|
||||||
|
|
||||||
var room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
|
|
||||||
|
|
||||||
if (!room) return [];
|
|
||||||
|
|
||||||
var myUserId = MatrixClientPeg.get().credentials.userId;
|
|
||||||
|
|
||||||
// get list of read receipts, sorted most recent first
|
|
||||||
var receipts = room.getReceiptsForEvent(this.props.mxEvent).filter(function(r) {
|
|
||||||
return r.type === "m.read" && r.userId != myUserId;
|
|
||||||
}).sort(function(r1, r2) {
|
|
||||||
return r2.data.ts - r1.data.ts;
|
|
||||||
});
|
|
||||||
|
|
||||||
var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
||||||
|
|
||||||
var left = 0;
|
var left = 0;
|
||||||
|
@ -176,11 +219,9 @@ module.exports = React.createClass({
|
||||||
easing: 'easeOut'
|
easing: 'easeOut'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var receipts = this.props.readReceipts || [];
|
||||||
for (var i = 0; i < receipts.length; ++i) {
|
for (var i = 0; i < receipts.length; ++i) {
|
||||||
var member = room.getMember(receipts[i].userId);
|
var member = receipts[i];
|
||||||
if (!member) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Using react refs here would mean both getting Velociraptor to expose
|
// Using react refs here would mean both getting Velociraptor to expose
|
||||||
// them and making them scoped to the whole RoomView. Not impossible, but
|
// them and making them scoped to the whole RoomView. Not impossible, but
|
||||||
|
@ -302,9 +343,9 @@ module.exports = React.createClass({
|
||||||
var classes = classNames({
|
var classes = classNames({
|
||||||
mx_EventTile: true,
|
mx_EventTile: true,
|
||||||
mx_EventTile_sending: ['sending', 'queued'].indexOf(
|
mx_EventTile_sending: ['sending', 'queued'].indexOf(
|
||||||
this.props.mxEvent.status
|
this.props.eventSendStatus
|
||||||
) !== -1,
|
) !== -1,
|
||||||
mx_EventTile_notSent: this.props.mxEvent.status == 'not_sent',
|
mx_EventTile_notSent: this.props.eventSendStatus == 'not_sent',
|
||||||
mx_EventTile_highlight: this.shouldHighlight(),
|
mx_EventTile_highlight: this.shouldHighlight(),
|
||||||
mx_EventTile_selected: this.props.isSelectedEvent,
|
mx_EventTile_selected: this.props.isSelectedEvent,
|
||||||
mx_EventTile_continuation: this.props.continuation,
|
mx_EventTile_continuation: this.props.continuation,
|
||||||
|
|
Loading…
Reference in New Issue