element-web/src/components/views/elements/MemberEventListSummary.js

232 lines
7.4 KiB
JavaScript
Raw Normal View History

2016-11-09 19:03:35 +03:00
/*
Copyright 2016 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
2016-11-09 19:03:35 +03:00
const MemberAvatar = require('../avatars/MemberAvatar.js');
module.exports = React.createClass({
displayName: 'MemberEventListSummary',
propTypes: {
// An array of member events to summarise
2016-11-10 17:09:40 +03:00
events: React.PropTypes.array.isRequired,
2016-11-10 17:12:45 +03:00
// An array of EventTiles to render when expanded
children: React.PropTypes.array.isRequired,
2016-11-09 19:03:35 +03:00
// The maximum number of names to show in either the join or leave summaries
summaryLength: React.PropTypes.number,
// The maximum number of avatars to display in the summary
avatarsMaxLength: React.PropTypes.number,
// The minimum number of events needed to trigger summarisation
threshold: React.PropTypes.number,
},
getInitialState: function() {
return {
expanded: false,
};
},
getDefaultProps: function() {
return {
summaryLength: 3,
threshold: 3,
avatarsMaxLength: 5
};
},
2016-11-10 17:12:05 +03:00
_toggleSummary: function() {
2016-11-09 19:03:35 +03:00
this.setState({
expanded: !this.state.expanded,
});
},
2016-11-10 17:12:05 +03:00
_getEventSenderName: function(ev) {
2016-11-09 19:03:35 +03:00
if (!ev) {
return 'undefined';
}
return ev.sender.name || ev.event.content.displayname || ev.getSender();
},
2016-11-10 17:12:05 +03:00
_renderNameList: function(events) {
2016-11-09 19:03:35 +03:00
if (events.length === 0) {
return null;
}
let originalNumber = events.length;
events = events.slice(0, this.props.summaryLength);
let lastEvent = events.pop();
let names = events.map((ev) => {
2016-11-10 17:12:05 +03:00
return this._getEventSenderName(ev);
2016-11-09 19:03:35 +03:00
}).join(', ');
let lastName = this._getEventSenderName(lastEvent);
if (names.length === 0) {
// special-case for a single event
return lastName;
2016-11-09 19:03:35 +03:00
}
2016-11-10 17:33:41 +03:00
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;
}
2016-11-09 19:03:35 +03:00
},
2016-11-10 17:12:05 +03:00
_renderSummary: function(joinEvents, leaveEvents) {
let joiners = this._renderNameList(joinEvents);
let leavers = this._renderNameList(leaveEvents);
2016-11-10 17:33:41 +03:00
2016-11-09 19:03:35 +03:00
let joinSummary = null;
if (joiners) {
joinSummary = (
<span>
2016-11-10 17:33:41 +03:00
{joiners} joined the room
2016-11-09 19:03:35 +03:00
</span>
);
}
let leaveSummary = null;
2016-11-09 19:03:35 +03:00
if (leavers) {
leaveSummary = (
<span>
2016-11-10 17:33:41 +03:00
{leavers} left the room
2016-11-09 19:03:35 +03:00
</span>
);
}
// The joinEvents and leaveEvents are representative of the net movement
// per-user, and so it is possible that the total net movement is nil,
// whilst there are some events in the expanded list. If the total net
// movement is nil, then neither joinSummary nor leaveSummary will be
// truthy, so return null.
if (!joinSummary && !leaveSummary) {
return null;
}
2016-11-09 19:03:35 +03:00
return (
<span>
{joinSummary}{joinSummary && leaveSummary?'; ':''}
{leaveSummary}.&nbsp;
2016-11-09 19:03:35 +03:00
</span>
);
},
2016-11-10 17:12:05 +03:00
_renderAvatars: function(events) {
2016-11-09 19:03:35 +03:00
let avatars = events.slice(0, this.props.avatarsMaxLength).map((e) => {
return (
<MemberAvatar
key={e.getId()}
member={e.sender}
width={14}
height={14}
/>
);
});
return (
<span>
{avatars}
</span>
);
},
render: function() {
let summary = null;
// Reorder events so that joins come before leaves
let eventsToRender = this.props.events;
// Create an array of events that are not "cancelled-out" by another
// A join of sender S is cancelled out by a leave of sender S etc.
let filteredEvents = [];
let senders = new Set(eventsToRender.map((e) => e.getSender()));
senders.forEach(
(userId) => {
// Only push the last event if it isn't the same membership as the first
let userEvents = eventsToRender.filter((e) => {
return e.getSender() === userId;
});
let firstEvent = userEvents[0];
let lastEvent = userEvents[userEvents.length - 1];
if (firstEvent.getContent().membership !== lastEvent.getContent().membership) {
filteredEvents.push(lastEvent);
}
2016-11-09 19:03:35 +03:00
}
);
let joinAndLeft = (eventsToRender.length - filteredEvents.length) / 2;
if (joinAndLeft <= 0 || joinAndLeft % 1 !== 0) {
joinAndLeft = null;
}
let joinEvents = filteredEvents.filter((ev) => {
return ev.event.content.membership === 'join';
});
let leaveEvents = filteredEvents.filter((ev) => {
return ev.event.content.membership === 'leave';
});
let fewEvents = eventsToRender.length < this.props.threshold;
let expanded = this.state.expanded || fewEvents;
let expandedEvents = null;
if (expanded) {
expandedEvents = this.props.children;
2016-11-09 19:03:35 +03:00
}
2016-11-10 17:12:05 +03:00
let avatars = this._renderAvatars(joinEvents.concat(leaveEvents));
2016-11-09 19:03:35 +03:00
let toggleButton = null;
let summaryContainer = null;
if (!fewEvents) {
2016-11-10 17:12:05 +03:00
summary = this._renderSummary(joinEvents, leaveEvents);
2016-11-09 19:03:35 +03:00
toggleButton = (
<a className="mx_MemberEventListSummary_toggle" onClick={this._toggleSummary}>
{expanded ? 'collapse' : 'expand'}
</a>
2016-11-09 19:03:35 +03:00
);
let plural = (joinEvents.length + leaveEvents.length > 0) ? 'others' : 'users';
let noun = (joinAndLeft === 1 ? 'user' : plural);
2016-11-09 19:03:35 +03:00
summaryContainer = (
<div className="mx_EventTile_line">
<div className="mx_EventTile_info">
<span className="mx_MemberEventListSummary_avatars">
{avatars}
</span>
<span className="mx_TextualEvent mx_MemberEventListSummary_summary">
{summary}{joinAndLeft ? joinAndLeft + ' ' + noun + ' joined and left' : ''}
2016-11-09 19:03:35 +03:00
</span>&nbsp;
{toggleButton}
</div>
</div>
);
}
return (
<div className="mx_MemberEventListSummary">
{summaryContainer}
{expandedEvents}
</div>
);
},
});