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

Truncate consecutive member events #544

Merged
merged 28 commits into from
Nov 11, 2016

Conversation

lukebarnard1
Copy link
Contributor

@lukebarnard1 lukebarnard1 commented Nov 8, 2016

This is needed for the IRC bridge to be able to do full membership list syncing (with quit/netsplit debouncing) without cluttering the message panel.

2016-11-08-162009_321x313_scrot

(See element-hq/element-web#349)

Also, for CSS changes, see element-hq/element-web#2565

This is needed for the IRC bridge to be able to do full membership list syncing without cluttering the message panel.
@matrixbot
Copy link
Member

Can one of the admins verify this patch?

@lukebarnard1
Copy link
Contributor Author

Note to self: test for element-hq/element-web#2020

Luke Barnard added 4 commits November 9, 2016 16:03
netsplits across midnight is not handled, and @richvdh suggested splitting the list in two
Now it's:
 1 user joined and left
 >1 others joined and left
@lukebarnard1
Copy link
Contributor Author

Things look a bit tidier now, with a summary followed by collapsed events:
2016-11-10-110009_438x247_scrot

A IRCAS bridged netsplit (/w incremental syncing turned on, QUIT debouncing off) where one user does not return following netsplit would look like this:
2016-11-10-110001_375x184_scrot

Copy link
Member

@richvdh richvdh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generally this looks like some great stuff! a few comments though :)

@@ -20,6 +20,7 @@ var dis = require("../../dispatcher");
var sdk = require('../../index');

var MatrixClientPeg = require('../../MatrixClientPeg')
var MemberEventListSummary = require('../views/elements/MemberEventListSummary.js');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

standard way to do this is with

const MemberEventListSummary = sdk.getComponent('views.elements.MemberEventListSummary');

in the relevant render function.

Copy link
Contributor Author

@lukebarnard1 lukebarnard1 Nov 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@richvdh how does one make a component exist when using getComponent?

@@ -286,6 +287,47 @@ module.exports = React.createClass({

var last = (i == lastShownEventIndex);

var isMembershipChange = (e) =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest hoisting this out of the for loop so that it doesn't need to be redefined for each event

}
summarisedEvents.push(collapsedMxEv);
}
let renderEvents = (pEvent, events) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's have a comment here to say what we expect i to be. Is it the last in the group, or the first that's not in the group?

<MemberEventListSummary
events={summarisedEvents}
previousEvent={prevEvent}
renderEvents={renderEvents}
Copy link
Member

@richvdh richvdh Nov 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

passing in a render callback feels pretty icky here.

An alternative might be to hoist the event filtering logic in MemberEventListSummary.render to here, then generate the tiles here, and then set them as children of the MemberEventListSummary:

const filteredEvents = filterEvents(summarisedEvents); /* as per MemberEventListSummary.render() */
const tiles = filteredEvents.map( /* as above*/ );
<MemberEventListSummary events={summarisedEvents}>
    {tiles}
</MemberEventListSummary>

and in MemberEventListSummary.render():

expandedEvents = this.props.children;

WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did have that thought. Yep, on reflection, WFM.

renderEvents={renderEvents}
/>
);
prevEvent = mxEv;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't you want the last event in the group, rather than the first?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, fair enough!


renderAvatars: function(events) {
let avatars = events.slice(0, this.props.avatarsMaxLength).map((e) => {
let onClickAvatar = () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be sorely tempted to push this down into MemberAvatar, maybe with a viewUserOnClick=true property, as a way to factor out the common code with MessagePanel.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, WFM.

render: function() {
let summary = null;

// Reorder events so that joins come before leaves
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as above - suggest moving this logic to timelinepanel.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much are we talking? I can't move filteredEvents on it's own because of the calculation for joinAndLeft

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, I think I misread before: I thought that the list being passed into renderEvents depended on the filtering calculations. Looks like it doesn't, so this is fine.

toggleButton = (
<a onClick={this.toggleSummary} href="javascript:;">{expanded?'collapse':'expand'}</a>
);
let noun = (joinAndLeft === 1 ? 'user' : 'others');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so... if 2 users join and leave, and nobody joins without leaving or vice versa... we'll just see "2 others joined and left"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, I'll update that to be either 'others' or 'users' as appropriate.

if (!fewEvents) {
summary = this.renderSummary(joinEvents, leaveEvents);
toggleButton = (
<a onClick={this.toggleSummary} href="javascript:;">{expanded?'collapse':'expand'}</a>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't think you need an href here.

@@ -28,17 +28,29 @@ module.exports = React.createClass({
createOverflowElement: React.PropTypes.func
},

getInitialState: function() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks unrelated... has it got lost?

I'm not quite sure what the intention is with it - it looks like what you are doing would be better handled by setting truncateAt = -1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops, didn't mean to commit this to the PR; I shall remove

@lukebarnard1
Copy link
Contributor Author

@richvdh how does one make a component exist when using getComponent?

}
summarisedEvents.push(collapsedMxEv);
}
// At this point, i = this.props.events.length OR i = the index of the first event
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you sure? line 303 suggests otherwise

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yes, good shout!

).reduce((a,b) => a.concat(b));
};

let eventTiles = renderEvents(prevEvent, summarisedEvents);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inline renderEvents?

resizeMethod: React.PropTypes.string
resizeMethod: React.PropTypes.string,
// Whether the onClick of the avatar should dispatch 'view_user'
viewUserOnClick: React.PropTypes.boolean
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: trailing comma would be nice

},

getDefaultProps: function() {
return {
width: 40,
height: 40,
resizeMethod: 'crop'
resizeMethod: 'crop',
viewUserOnClick: false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

@@ -65,9 +69,19 @@ module.exports = React.createClass({

var {member, ...otherProps} = this.props;

var onClick = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some uses of MemberAvatar set props.onClick to other things; you'll need to pass that down as the default, and probably exclude it from otherProps above.

render: function() {
let summary = null;

// Reorder events so that joins come before leaves
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, I think I misread before: I thought that the list being passed into renderEvents depended on the filtering calculations. Looks like it doesn't, so this is fine.


// Special case the last name. ' and ' might be included later
// So you have two cases:
if (originalNumber <= this.props.summaryLength) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a bit tortuous now. How about:

let lastName = this._getEventSenderName(lastEvent);
if (names.length === 0) {
    // special-case for a single event
    return lastName;
}

let remaining = originalNumber - this.props.summaryLength;
if (remaining > 0) {
    //  name1, name2, name3 and 100 others
    return names + ', ' + lastName + ', and ' + remaining + ' others';
} else {
    //  name1, name2 and name3
    return names + ' and ' + lastName;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WFM!

@richvdh richvdh assigned lukebarnard1 and unassigned richvdh Nov 10, 2016
@richvdh
Copy link
Member

richvdh commented Nov 10, 2016

matrixbot: ok to test

lukebarnard1 pushed a commit to element-hq/element-web that referenced this pull request Nov 10, 2016
Copy link
Member

@richvdh richvdh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm otherwise

// name1, name2, name3 [and 100 others]
names += ', ';
let lastName = this._getEventSenderName(lastEvent);
if (names.length === 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this makes lines 74-76 redundant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yeah!

@richvdh
Copy link
Member

richvdh commented Nov 11, 2016

lgtm!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants