Merge pull request #108 from matrix-org/kegan/member-list-perf

Add "and X more" overflow tile to joined member list
This commit is contained in:
Kegsay 2016-01-21 16:15:12 +00:00
commit b45e5e6eb5
4 changed files with 101 additions and 19 deletions

View file

@ -23,6 +23,7 @@ limitations under the License.
module.exports.components = {}; module.exports.components = {};
module.exports.components['structures.CreateRoom'] = require('./components/structures/CreateRoom'); module.exports.components['structures.CreateRoom'] = require('./components/structures/CreateRoom');
module.exports.components['structures.login.ForgotPassword'] = require('./components/structures/login/ForgotPassword');
module.exports.components['structures.login.Login'] = require('./components/structures/login/Login'); module.exports.components['structures.login.Login'] = require('./components/structures/login/Login');
module.exports.components['structures.login.PostRegistration'] = require('./components/structures/login/PostRegistration'); module.exports.components['structures.login.PostRegistration'] = require('./components/structures/login/PostRegistration');
module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration'); module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration');
@ -31,10 +32,6 @@ module.exports.components['structures.RoomView'] = require('./components/structu
module.exports.components['structures.ScrollPanel'] = require('./components/structures/ScrollPanel'); module.exports.components['structures.ScrollPanel'] = require('./components/structures/ScrollPanel');
module.exports.components['structures.UploadBar'] = require('./components/structures/UploadBar'); module.exports.components['structures.UploadBar'] = require('./components/structures/UploadBar');
module.exports.components['structures.UserSettings'] = require('./components/structures/UserSettings'); module.exports.components['structures.UserSettings'] = require('./components/structures/UserSettings');
module.exports.components['structures.login.ForgotPassword'] = require('./components/structures/login/ForgotPassword');
module.exports.components['structures.login.Login'] = require('./components/structures/login/Login');
module.exports.components['structures.login.PostRegistration'] = require('./components/structures/login/PostRegistration');
module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration');
module.exports.components['views.avatars.BaseAvatar'] = require('./components/views/avatars/BaseAvatar'); module.exports.components['views.avatars.BaseAvatar'] = require('./components/views/avatars/BaseAvatar');
module.exports.components['views.avatars.MemberAvatar'] = require('./components/views/avatars/MemberAvatar'); module.exports.components['views.avatars.MemberAvatar'] = require('./components/views/avatars/MemberAvatar');
module.exports.components['views.avatars.RoomAvatar'] = require('./components/views/avatars/RoomAvatar'); module.exports.components['views.avatars.RoomAvatar'] = require('./components/views/avatars/RoomAvatar');
@ -49,6 +46,7 @@ module.exports.components['views.elements.EditableText'] = require('./components
module.exports.components['views.elements.PowerSelector'] = require('./components/views/elements/PowerSelector'); module.exports.components['views.elements.PowerSelector'] = require('./components/views/elements/PowerSelector');
module.exports.components['views.elements.ProgressBar'] = require('./components/views/elements/ProgressBar'); module.exports.components['views.elements.ProgressBar'] = require('./components/views/elements/ProgressBar');
module.exports.components['views.elements.TintableSvg'] = require('./components/views/elements/TintableSvg'); module.exports.components['views.elements.TintableSvg'] = require('./components/views/elements/TintableSvg');
module.exports.components['views.elements.TruncatedList'] = require('./components/views/elements/TruncatedList');
module.exports.components['views.elements.UserSelector'] = require('./components/views/elements/UserSelector'); module.exports.components['views.elements.UserSelector'] = require('./components/views/elements/UserSelector');
module.exports.components['views.login.CaptchaForm'] = require('./components/views/login/CaptchaForm'); module.exports.components['views.login.CaptchaForm'] = require('./components/views/login/CaptchaForm');
module.exports.components['views.login.CasLogin'] = require('./components/views/login/CasLogin'); module.exports.components['views.login.CasLogin'] = require('./components/views/login/CasLogin');

View file

@ -0,0 +1,68 @@
/*
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.
*/
var React = require('react');
module.exports = React.createClass({
displayName: 'TruncatedList',
propTypes: {
// The number of elements to show before truncating. If negative, no truncation is done.
truncateAt: React.PropTypes.number,
// The className to apply to the wrapping div
className: React.PropTypes.string,
// A function which will be invoked when an overflow element is required.
// This will be inserted after the children.
createOverflowElement: React.PropTypes.func
},
getDefaultProps: function() {
return {
truncateAt: 2,
createOverflowElement: function(overflowCount, totalCount) {
return (
<div>And {overflowCount} more...</div>
);
}
};
},
render: function() {
var childsJsx = this.props.children;
var overflowJsx;
var childCount = React.Children.count(this.props.children);
if (this.props.truncateAt >= 0) {
var overflowCount = childCount - this.props.truncateAt;
if (overflowCount > 0) {
overflowJsx = this.props.createOverflowElement(
overflowCount, childCount
);
var childArray = React.Children.toArray(this.props.children);
// cut out the overflow elements
childArray.splice(childCount - overflowCount, overflowCount);
childsJsx = childArray; // use what is left
}
}
return (
<div className={this.props.className}>
{childsJsx}
{overflowJsx}
</div>
);
}
});

View file

@ -39,16 +39,18 @@ module.exports = React.createClass({
presenceActiveAgo: React.PropTypes.number, presenceActiveAgo: React.PropTypes.number,
showInviteButton: React.PropTypes.bool, showInviteButton: React.PropTypes.bool,
shouldComponentUpdate: React.PropTypes.func, shouldComponentUpdate: React.PropTypes.func,
onClick: React.PropTypes.func onClick: React.PropTypes.func,
suppressOnHover: React.PropTypes.bool
}, },
getDefaultProps: function() { getDefaultProps: function() {
return { return {
shouldComponentUpdate: function(nextProps, nextState) { return false; }, shouldComponentUpdate: function(nextProps, nextState) { return true; },
onClick: function() {}, onClick: function() {},
presenceState: "offline", presenceState: "offline",
presenceActiveAgo: -1, presenceActiveAgo: -1,
showInviteButton: false, showInviteButton: false,
suppressOnHover: false
}; };
}, },
@ -75,12 +77,10 @@ module.exports = React.createClass({
var presenceClass = PRESENCE_CLASS[this.props.presenceState] || "mx_EntityTile_offline"; var presenceClass = PRESENCE_CLASS[this.props.presenceState] || "mx_EntityTile_offline";
var mainClassName = "mx_EntityTile "; var mainClassName = "mx_EntityTile ";
mainClassName += presenceClass; mainClassName += presenceClass;
if (this.state.hover) {
mainClassName += " mx_EntityTile_hover";
}
var nameEl; var nameEl;
if (this.state.hover) {
if (this.state.hover && !this.props.suppressOnHover) {
mainClassName += " mx_EntityTile_hover";
var PresenceLabel = sdk.getComponent("rooms.PresenceLabel"); var PresenceLabel = sdk.getComponent("rooms.PresenceLabel");
nameEl = ( nameEl = (
<div className="mx_EntityTile_details"> <div className="mx_EntityTile_details">

View file

@ -42,7 +42,8 @@ module.exports = React.createClass({
var members = this.roomMembers(INITIAL_LOAD_NUM_MEMBERS); var members = this.roomMembers(INITIAL_LOAD_NUM_MEMBERS);
return { return {
members: members members: members,
truncateAt: 10
}; };
}, },
@ -75,9 +76,6 @@ module.exports = React.createClass({
self._loadUserList(); self._loadUserList();
}, 50); }, 50);
setTimeout
// Attach a SINGLE listener for global presence changes then locate the // Attach a SINGLE listener for global presence changes then locate the
// member tile and re-render it. This is more efficient than every tile // member tile and re-render it. This is more efficient than every tile
// evar attaching their own listener. // evar attaching their own listener.
@ -260,7 +258,24 @@ module.exports = React.createClass({
return to_display; return to_display;
}, },
_createOverflowTile: function(overflowCount, totalCount) {
// For now we'll pretend this is any entity. It should probably be a separate tile.
var EntityTile = sdk.getComponent("rooms.EntityTile");
var BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
var text = "and " + overflowCount + " more";
return (
<EntityTile avatarJsx={
<BaseAvatar name="+" width={36} height={36} />
} name={text} presenceState="online" suppressOnHover={true}
onClick={this._showFullMemberList} />
);
},
_showFullMemberList: function() {
this.setState({
truncateAt: -1
});
},
memberSort: function(userIdA, userIdB) { memberSort: function(userIdA, userIdB) {
var userA = this.memberDict[userIdA].user; var userA = this.memberDict[userIdA].user;
@ -327,8 +342,7 @@ module.exports = React.createClass({
return; return;
} }
memberList.push( memberList.push(
<EntityTile key={e.getStateKey()} ref={e.getStateKey()} <EntityTile key={e.getStateKey()} name={e.getContent().display_name} />
name={e.getContent().display_name} />
) )
}) })
} }
@ -368,13 +382,15 @@ module.exports = React.createClass({
</div> </div>
); );
} }
var TruncatedList = sdk.getComponent("elements.TruncatedList");
return ( return (
<div className="mx_MemberList"> <div className="mx_MemberList">
{this.inviteTile()} {this.inviteTile()}
<GeminiScrollbar autoshow={true} className="mx_MemberList_joined mx_MemberList_outerWrapper"> <GeminiScrollbar autoshow={true} className="mx_MemberList_joined mx_MemberList_outerWrapper">
<div className="mx_MemberList_wrapper"> <TruncatedList className="mx_MemberList_wrapper" truncateAt={this.state.truncateAt}
createOverflowElement={this._createOverflowTile}>
{this.makeMemberTiles('join', this.state.searchQuery)} {this.makeMemberTiles('join', this.state.searchQuery)}
</div> </TruncatedList>
{invitedSection} {invitedSection}
</GeminiScrollbar> </GeminiScrollbar>
<div className="mx_MemberList_bottom"> <div className="mx_MemberList_bottom">