diff --git a/src/components/views/elements/TruncatedList.js b/src/components/views/elements/TruncatedList.js
index e35e12e039..cc94b4ff9b 100644
--- a/src/components/views/elements/TruncatedList.js
+++ b/src/components/views/elements/TruncatedList.js
@@ -27,6 +27,16 @@ module.exports = React.createClass({
truncateAt: PropTypes.number,
// The className to apply to the wrapping div
className: PropTypes.string,
+ // A function that returns the children to be rendered into the element.
+ // Takes two integers which define the range of child indices to return.
+ // The start element is included, the end is not (as in `slice`).
+ // Returns an array.
+ // If omitted, the React child elements will be used. This parameter can be used
+ // to avoid creating unnecessary React elements.
+ getChildren: PropTypes.func,
+ // A function that should return the total number of child element available.
+ // Required if getChildren is supplied.
+ getChildCount: PropTypes.func,
// A function which will be invoked when an overflow element is required.
// This will be inserted after the children.
createOverflowElement: PropTypes.func,
@@ -43,33 +53,50 @@ module.exports = React.createClass({
};
},
+ _getChildren: function(min, max) {
+ if (this.props.getChildren && this.props.getChildCount) {
+ return this.props.getChildren(min, max);
+ } else {
+ // XXX: I'm not sure why anything would pass null into this, it seems
+ // like a bizzare case to handle, but I'm preserving the behaviour.
+ // (see commit 38d5c7d5c5d5a34dc16ef5d46278315f5c57f542)
+ return React.Children.toArray(this.props.children).filter((c) => {
+ return c != null;
+ }).slice(min, max);
+ }
+ },
+
+ _getChildCount: function() {
+ if (this.props.getChildren && this.props.getChildCount) {
+ return this.props.getChildCount();
+ } else {
+ return React.Children.toArray(this.props.children).filter((c) => {
+ return c != null;
+ }).length;
+ }
+ },
+
render: function() {
- let childsJsx = this.props.children;
- let overflowJsx;
- const childArray = React.Children.toArray(this.props.children).filter((c) => {
- return c != null;
- });
-
- const childCount = childArray.length;
+ let childNodes;
+ let overflowNode = null;
+ const totalChildren = this._getChildCount();
+ let upperBound = totalChildren;
if (this.props.truncateAt >= 0) {
- const overflowCount = childCount - this.props.truncateAt;
-
+ const overflowCount = totalChildren - this.props.truncateAt;
if (overflowCount > 1) {
- overflowJsx = this.props.createOverflowElement(
- overflowCount, childCount,
+ overflowNode = this.props.createOverflowElement(
+ overflowCount, totalChildren,
);
-
- // cut out the overflow elements
- childArray.splice(childCount - overflowCount, overflowCount);
- childsJsx = childArray; // use what is left
+ upperBound = this.props.truncateAt;
}
}
+ childNodes = this._getChildren(0, upperBound);
return (
- {childsJsx}
- {overflowJsx}
+ {childNodes}
+ {overflowNode}
);
},
diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js
index 33d05235bf..9c75e2d052 100644
--- a/src/components/views/rooms/MemberList.js
+++ b/src/components/views/rooms/MemberList.js
@@ -1,6 +1,7 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
+Copyright 2017 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,10 +15,11 @@ 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.
*/
-var React = require('react');
+
+import React from 'react';
import { _t } from '../../../languageHandler';
-var classNames = require('classnames');
-var Matrix = require("matrix-js-sdk");
+import classNames from 'classnames';
+import Matrix from 'matrix-js-sdk';
import Promise from 'bluebird';
var MatrixClientPeg = require("../../../MatrixClientPeg");
var Modal = require("../../../Modal");
@@ -27,13 +29,13 @@ var GeminiScrollbar = require('react-gemini-scrollbar');
var rate_limited_func = require('../../../ratelimitedfunc');
var CallHandler = require("../../../CallHandler");
-var INITIAL_LOAD_NUM_MEMBERS = 30;
+const INITIAL_LOAD_NUM_MEMBERS = 30;
module.exports = React.createClass({
displayName: 'MemberList',
getInitialState: function() {
- var state = {
+ const state = {
members: [],
// ideally we'd size this to the page height, but
// in practice I find that a little constraining
@@ -48,6 +50,8 @@ module.exports = React.createClass({
this.memberDict = this.getMemberDict();
state.members = this.roomMembers();
+ state.filteredJoinedMembers = this._filterMembers(state.members, 'join');
+ state.filteredInvitedMembers = this._filterMembers(state.members, 'invite');
return state;
},
@@ -146,10 +150,12 @@ module.exports = React.createClass({
// console.log("Updating memberlist");
this.memberDict = this.getMemberDict();
- var self = this;
- this.setState({
- members: self.roomMembers()
- });
+ const newState = {
+ members: this.roomMembers(),
+ };
+ newState.filteredJoinedMembers = this._filterMembers(newState.members, 'join');
+ newState.filteredInvitedMembers = this._filterMembers(newState.members, 'invite');
+ this.setState(newState);
}, 500),
getMemberDict: function() {
@@ -279,17 +285,17 @@ module.exports = React.createClass({
},
onSearchQueryChanged: function(ev) {
- this.setState({ searchQuery: ev.target.value });
+ const q = ev.target.value;
+ this.setState({
+ searchQuery: q,
+ filteredJoinedMembers: this._filterMembers(this.state.members, 'join', q),
+ filteredInvitedMembers: this._filterMembers(this.state.members, 'invite', q),
+ });
},
- makeMemberTiles: function(membership, query) {
- var MemberTile = sdk.getComponent("rooms.MemberTile");
- query = (query || "").toLowerCase();
-
- var self = this;
-
- var memberList = self.state.members.filter(function(userId) {
- var m = self.memberDict[userId];
+ _filterMembers: function(members, membership, query) {
+ return members.filter((userId) => {
+ const m = this.memberDict[userId];
if (query) {
const matchesName = m.name.toLowerCase().indexOf(query) !== -1;
@@ -301,14 +307,23 @@ module.exports = React.createClass({
}
return m.membership == membership;
- }).map(function(userId) {
- var m = self.memberDict[userId];
+ });
+ },
+
+ _makeMemberTiles: function(members, membership) {
+ const MemberTile = sdk.getComponent("rooms.MemberTile");
+
+ const memberList = members.map((userId) => {
+ const m = this.memberDict[userId];
return (
);
});
// XXX: surely this is not the right home for this logic.
+ // Double XXX: Now it's really, really not the right home for this logic:
+ // we shouldn't even be passing in the 'membership' param to this function.
+ // Ew, ew, and ew.
if (membership === "invite") {
// include 3pid invites (m.room.third_party_invite) state events.
// The HS may have already converted these into m.room.member invites so
@@ -341,9 +356,17 @@ module.exports = React.createClass({
return memberList;
},
+ _getChildrenJoined: function(min, max) {
+ return this._makeMemberTiles(this.state.filteredJoinedMembers.slice(min, max));
+ },
+
+ _getChildCountJoined: function() {
+ return this.state.filteredJoinedMembers.length;
+ },
+
render: function() {
- var invitedSection = null;
- var invitedMemberTiles = this.makeMemberTiles('invite', this.state.searchQuery);
+ let invitedSection = null;
+ const invitedMemberTiles = this._makeMemberTiles(this.state.filteredInvitedMembers, 'invite');
if (invitedMemberTiles.length > 0) {
invitedSection = (
@@ -355,7 +378,7 @@ module.exports = React.createClass({
);
}
- var inputBox = (
+ const inputBox = (