Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Avoid rerendering EventTiles when not necessary #278

Merged
merged 1 commit into from
Apr 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 33 additions & 3 deletions src/components/structures/MessagePanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ var ReactDOM = require("react-dom");
var dis = require("../../dispatcher");
var sdk = require('../../index');

var MatrixClientPeg = require('../../MatrixClientPeg')

/* (almost) stateless UI component which builds the event tiles in the room timeline.
*/
module.exports = React.createClass({
Expand Down Expand Up @@ -150,7 +152,7 @@ module.exports = React.createClass({
this.refs.scrollPanel.scrollToBottom();
}
},

/**
* Page up/down.
*
Expand Down Expand Up @@ -335,13 +337,17 @@ module.exports = React.createClass({
// Local echos have a send "status".
var scrollToken = mxEv.status ? undefined : eventId;

var readReceipts = this._getReadReceiptsForEvent(mxEv);

ret.push(
<li key={eventId}
ref={this._collectEventNode.bind(this, eventId)}
data-scroll-token={scrollToken}>
<EventTile mxEvent={mxEv} continuation={continuation}
onWidgetLoad={this._onWidgetLoad}
last={last} isSelectedEvent={highlight} />
readReceipts={readReceipts}
eventSendStatus={mxEv.status}
last={last} isSelectedEvent={highlight}/>
</li>
);

Expand All @@ -359,6 +365,30 @@ module.exports = React.createClass({
!== 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) {
var hr;
if (visible) {
Expand Down Expand Up @@ -431,7 +461,7 @@ module.exports = React.createClass({

return (
<ScrollPanel ref="scrollPanel" className="mx_RoomView_messagePanel mx_fadable"
onScroll={ this.props.onScroll }
onScroll={ this.props.onScroll }
onResize={ this.onResize }
onFillRequest={ this.props.onFillRequest }
style={ style }
Expand Down
81 changes: 61 additions & 20 deletions src/components/views/rooms/EventTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ var Velociraptor = require('../../../Velociraptor');
require('../../../VelocityBounce');
var dispatcher = require("../../../dispatcher");

var ObjectUtils = require('../../../ObjectUtils');

var bounce = false;
try {
if (global.localStorage) {
Expand Down Expand Up @@ -107,12 +109,67 @@ module.exports = React.createClass({

/* callback called when dynamic content in events are loaded */
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() {
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() {
var actions = MatrixClientPeg.get().getPushActionsForEvent(this.props.mxEvent);
if (!actions || !actions.tweaks) { return false; }
Expand Down Expand Up @@ -153,20 +210,6 @@ module.exports = React.createClass({

getReadAvatars: function() {
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 left = 0;
Expand All @@ -176,11 +219,9 @@ module.exports = React.createClass({
easing: 'easeOut'
};

var receipts = this.props.readReceipts || [];
for (var i = 0; i < receipts.length; ++i) {
var member = room.getMember(receipts[i].userId);
if (!member) {
continue;
}
var member = receipts[i];

// Using react refs here would mean both getting Velociraptor to expose
// them and making them scoped to the whole RoomView. Not impossible, but
Expand Down Expand Up @@ -302,9 +343,9 @@ module.exports = React.createClass({
var classes = classNames({
mx_EventTile: true,
mx_EventTile_sending: ['sending', 'queued'].indexOf(
this.props.mxEvent.status
this.props.eventSendStatus
) !== -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_selected: this.props.isSelectedEvent,
mx_EventTile_continuation: this.props.continuation,
Expand Down