From d5deb283bf17ecb113df05617e204fd7f204b3c9 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 14 Jan 2016 17:27:06 +0000 Subject: [PATCH 01/15] Add a SearchableEntityList and use it for invite box It all even works, though only for RoomMembers currently. --- src/component-index.js | 1 + src/components/views/rooms/MemberList.js | 17 +-- .../views/rooms/SearchableEntityList.js | 126 ++++++++++++++++++ 3 files changed, 136 insertions(+), 8 deletions(-) create mode 100644 src/components/views/rooms/SearchableEntityList.js diff --git a/src/component-index.js b/src/component-index.js index 7ae15ba12c..cbecbf7d2e 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -70,6 +70,7 @@ module.exports.components['views.rooms.RoomList'] = require('./components/views/ module.exports.components['views.rooms.RoomPreviewBar'] = require('./components/views/rooms/RoomPreviewBar'); module.exports.components['views.rooms.RoomSettings'] = require('./components/views/rooms/RoomSettings'); module.exports.components['views.rooms.RoomTile'] = require('./components/views/rooms/RoomTile'); +module.exports.components['views.rooms.SearchableEntityList'] = require('./components/views/rooms/SearchableEntityList'); module.exports.components['views.rooms.SearchResultTile'] = require('./components/views/rooms/SearchResultTile'); module.exports.components['views.rooms.TabCompleteBar'] = require('./components/views/rooms/TabCompleteBar'); module.exports.components['views.settings.ChangeAvatar'] = require('./components/views/settings/ChangeAvatar'); diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index eac5466e88..c768144cdc 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -267,11 +267,6 @@ module.exports = React.createClass({ return memberList; }, - onPopulateInvite: function(e) { - this.onInvite(this.refs.invite.value); - e.preventDefault(); - }, - inviteTile: function() { if (this.state.inviting) { var Loader = sdk.getComponent("elements.Spinner"); @@ -279,10 +274,16 @@ module.exports = React.createClass({ ); } else { + var SearchableEntityList = sdk.getComponent("rooms.SearchableEntityList"); + var room = MatrixClientPeg.get().getRoom(this.props.roomId); return ( -
- -
+ ); } }, diff --git a/src/components/views/rooms/SearchableEntityList.js b/src/components/views/rooms/SearchableEntityList.js new file mode 100644 index 0000000000..53ed93a81f --- /dev/null +++ b/src/components/views/rooms/SearchableEntityList.js @@ -0,0 +1,126 @@ +/* +Copyright 2015, 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'); +var classNames = require('classnames'); +var MatrixClientPeg = require("../../../MatrixClientPeg"); +var Modal = require("../../../Modal"); +var sdk = require('../../../index'); +var GeminiScrollbar = require('react-gemini-scrollbar'); + +// A list capable of displaying entities which conform to the SearchableEntity +// interface which is an object containing getJsx(): Jsx and matches(query: string): boolean +var SearchableEntityList = React.createClass({ + displayName: 'SearchableEntityList', + + propTypes: { + searchPlaceholderText: React.PropTypes.string, + emptyQueryShowsAll: React.PropTypes.bool, + onSubmit: React.PropTypes.func, // fn(inputText) + entities: React.PropTypes.array, + }, + + getDefaultProps: function() { + return { + searchPlaceholderText: "Search", + entities: [], + emptyQueryShowsAll: false, + onSubmit: function() {} + }; + }, + + getInitialState: function() { + return { + query: "", + results: this.getSearchResults("") + }; + }, + + onQueryChanged: function(ev) { + var q = ev.target.value; + this.setState({ + query: q, + results: this.getSearchResults(q) + }); + }, + + onQuerySubmit: function(ev) { + ev.preventDefault(); + this.props.onSubmit(this.state.query); + }, + + getSearchResults: function(query) { + if (!query || query.length === 0) { + return this.props.emptyQueryShowsAll ? this.props.entities : [] + } + return this.props.entities.filter(function(e) { + return e.matches(query); + }); + }, + + render: function() { + return ( +
+
+ +
+
+ {this.state.results.map(function(entity) { + return entity.getJsx(); + })} +
+
+ ); + } +}); + +SearchableEntityList.fromRoomMembers = function(members) { + var MemberTile = sdk.getComponent("rooms.MemberTile"); + return members.map(function(m) { + return { + matches: function(query) { + return m.name.toLowerCase().indexOf(query.toLowerCase()) === 0; + }, + + getJsx: function() { + return ( + + ); + } + }; + }); +}; + +SearchableEntityList.fromUsers = function(users) { + var UserTile = sdk.getComponent("rooms.UserTile"); + return users.map(function(u) { + return { + matches: function(query) { + return u.displayName.toLowerCase().indexOf(query.toLowerCase()) === 0; + }, + + getJsx: function() { + return ( + + ); + } + }; + }); +}; + + + module.exports = SearchableEntityList; From 83a458938efce10bfb86f9accec8c5b301a2f883 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 14 Jan 2016 17:41:04 +0000 Subject: [PATCH 02/15] Unbreak things --- src/components/views/rooms/SearchableEntityList.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/SearchableEntityList.js b/src/components/views/rooms/SearchableEntityList.js index 53ed93a81f..ec8b6540f0 100644 --- a/src/components/views/rooms/SearchableEntityList.js +++ b/src/components/views/rooms/SearchableEntityList.js @@ -105,7 +105,7 @@ SearchableEntityList.fromRoomMembers = function(members) { }); }; -SearchableEntityList.fromUsers = function(users) { +SearchableEntityList.fromUsers = function(users) { /* var UserTile = sdk.getComponent("rooms.UserTile"); return users.map(function(u) { return { @@ -119,7 +119,7 @@ SearchableEntityList.fromUsers = function(users) { ); } }; - }); + }); */ }; From 266aee2c6be9b946c2a5543050aa9916b637624d Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 15 Jan 2016 09:58:48 +0000 Subject: [PATCH 03/15] Factor out data model -> Entity logic to Entities.js --- src/Entities.js | 91 +++++++++++++++++++ src/components/views/rooms/MemberList.js | 3 +- .../views/rooms/SearchableEntityList.js | 37 -------- 3 files changed, 93 insertions(+), 38 deletions(-) create mode 100644 src/Entities.js diff --git a/src/Entities.js b/src/Entities.js new file mode 100644 index 0000000000..ee0d3e9d1b --- /dev/null +++ b/src/Entities.js @@ -0,0 +1,91 @@ +/* +Copyright 2015, 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'); +var sdk = require('./index'); + +/* + * Converts various data models to Entity objects. + * + * Entity objects provide an interface for UI components to use to display + * members in a data-agnostic way. This means they don't need to care if the + * underlying data model is a RoomMember, User or 3PID data structure, it just + * cares about rendering. + */ + +class Entity { + constructor(model) { + this.model = model; + } + + getJsx() { + return null; + } + + matches(queryString) { + return false; + } +} + +class MemberEntity extends Entity { + getJsx() { + var MemberTile = sdk.getComponent("rooms.MemberTile"); + return ( + + ); + } + + matches(queryString) { + return this.model.name.toLowerCase().indexOf(queryString.toLowerCase()) === 0; + } +} + +class UserEntity extends Entity { + + getJsx() { + var UserTile = sdk.getComponent("rooms.UserTile"); + return ( + + ); + } + + matches(queryString) { + return this.model.displayName.toLowerCase().indexOf(queryString.toLowerCase()) === 0; + } +} + + +module.exports = { + /** + * @param {RoomMember[]} members + * @return {Entity[]} + */ + fromRoomMembers: function(members) { + return members.map(function(m) { + return new MemberEntity(m); + }); + }, + + /** + * @param {User[]} users + * @return {Entity[]} + */ + fromUsers: function(users) { + return users.map(function(u) { + return new UserEntity(u); + }) + } +}; diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index c768144cdc..f5fc5b9f82 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -18,6 +18,7 @@ var classNames = require('classnames'); var Matrix = require("matrix-js-sdk"); var MatrixClientPeg = require("../../../MatrixClientPeg"); var Modal = require("../../../Modal"); +var Entities = require("../../../Entities"); var sdk = require('../../../index'); var GeminiScrollbar = require('react-gemini-scrollbar'); @@ -280,7 +281,7 @@ module.exports = React.createClass({ diff --git a/src/components/views/rooms/SearchableEntityList.js b/src/components/views/rooms/SearchableEntityList.js index ec8b6540f0..b8df4f6610 100644 --- a/src/components/views/rooms/SearchableEntityList.js +++ b/src/components/views/rooms/SearchableEntityList.js @@ -14,10 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ var React = require('react'); -var classNames = require('classnames'); var MatrixClientPeg = require("../../../MatrixClientPeg"); var Modal = require("../../../Modal"); -var sdk = require('../../../index'); var GeminiScrollbar = require('react-gemini-scrollbar'); // A list capable of displaying entities which conform to the SearchableEntity @@ -88,39 +86,4 @@ var SearchableEntityList = React.createClass({ } }); -SearchableEntityList.fromRoomMembers = function(members) { - var MemberTile = sdk.getComponent("rooms.MemberTile"); - return members.map(function(m) { - return { - matches: function(query) { - return m.name.toLowerCase().indexOf(query.toLowerCase()) === 0; - }, - - getJsx: function() { - return ( - - ); - } - }; - }); -}; - -SearchableEntityList.fromUsers = function(users) { /* - var UserTile = sdk.getComponent("rooms.UserTile"); - return users.map(function(u) { - return { - matches: function(query) { - return u.displayName.toLowerCase().indexOf(query.toLowerCase()) === 0; - }, - - getJsx: function() { - return ( - - ); - } - }; - }); */ -}; - - module.exports = SearchableEntityList; From aea833ec8766eb9866ba0b1db8af3130daf175d0 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 15 Jan 2016 10:31:31 +0000 Subject: [PATCH 04/15] Display User objects in search results --- src/Entities.js | 3 +- src/component-index.js | 1 + src/components/views/rooms/MemberList.js | 12 ++- src/components/views/rooms/UserTile.js | 127 +++++++++++++++++++++++ 4 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 src/components/views/rooms/UserTile.js diff --git a/src/Entities.js b/src/Entities.js index ee0d3e9d1b..712f7b4dde 100644 --- a/src/Entities.js +++ b/src/Entities.js @@ -63,7 +63,8 @@ class UserEntity extends Entity { } matches(queryString) { - return this.model.displayName.toLowerCase().indexOf(queryString.toLowerCase()) === 0; + var name = this.model.displayName || this.model.userId; + return name.toLowerCase().indexOf(queryString.toLowerCase()) === 0; } } diff --git a/src/component-index.js b/src/component-index.js index cbecbf7d2e..94f313a83c 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -73,6 +73,7 @@ module.exports.components['views.rooms.RoomTile'] = require('./components/views/ module.exports.components['views.rooms.SearchableEntityList'] = require('./components/views/rooms/SearchableEntityList'); module.exports.components['views.rooms.SearchResultTile'] = require('./components/views/rooms/SearchResultTile'); module.exports.components['views.rooms.TabCompleteBar'] = require('./components/views/rooms/TabCompleteBar'); +module.exports.components['views.rooms.UserTile'] = require('./components/views/rooms/UserTile'); module.exports.components['views.settings.ChangeAvatar'] = require('./components/views/settings/ChangeAvatar'); module.exports.components['views.settings.ChangeDisplayName'] = require('./components/views/settings/ChangeDisplayName'); module.exports.components['views.settings.ChangePassword'] = require('./components/views/settings/ChangePassword'); diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index f5fc5b9f82..625a35e2fa 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -275,14 +275,24 @@ module.exports = React.createClass({ ); } else { - var SearchableEntityList = sdk.getComponent("rooms.SearchableEntityList"); + // TODO: Cache this calculation var room = MatrixClientPeg.get().getRoom(this.props.roomId); + var allUsers = MatrixClientPeg.get().getUsers(); + // only add Users if they don't exist in the member list + allUsers = allUsers.filter(function(u) { + return room.getMember(u.userId) === null; + }); + + var SearchableEntityList = sdk.getComponent("rooms.SearchableEntityList"); + return ( ); diff --git a/src/components/views/rooms/UserTile.js b/src/components/views/rooms/UserTile.js new file mode 100644 index 0000000000..4a06dc9bc3 --- /dev/null +++ b/src/components/views/rooms/UserTile.js @@ -0,0 +1,127 @@ +/* +Copyright 2015, 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. +*/ + +'use strict'; + +var React = require('react'); + +var MatrixClientPeg = require('../../../MatrixClientPeg'); +var sdk = require('../../../index'); +var dis = require('../../../dispatcher'); +var Modal = require("../../../Modal"); + +module.exports = React.createClass({ + displayName: 'UserTile', + + propTypes: { + user: React.PropTypes.any.isRequired, // User + onInviteClick: React.PropTypes.func, //onInviteClick(User) + showInvite: React.PropTypes.bool, + onClick: React.PropTypes.func + }, + + getInitialState: function() { + return {}; + }, + + getDefaultProps: function() { + return { + onClick: function() {}, + onInviteClick: function() {}, + showInvite: false + }; + }, + + shouldComponentUpdate: function(nextProps, nextState) { + if (this.state.hover !== nextState.hover) return true; + return false; + }, + + mouseEnter: function(e) { + this.setState({ 'hover': true }); + }, + + mouseLeave: function(e) { + this.setState({ 'hover': false }); + }, + + render: function() { + var user = this.props.user; + var name = user.displayName || user.userId; + var isMyUser = MatrixClientPeg.get().credentials.userId == user.userId; + var active = -1; + var presenceClass = "mx_MemberTile_offline"; + + this.user_last_modified_time = user.getLastModifiedTime(); + + // FIXME: make presence data update whenever User.presence changes... + active = ( + (Date.now() - (user.lastPresenceTs - user.lastActiveAgo)) || -1 + ); + + if (user.presence === "online") { + presenceClass = "mx_MemberTile_online"; + } + else if (user.presence === "unavailable") { + presenceClass = "mx_MemberTile_unavailable"; + } + + + var mainClassName = "mx_MemberTile "; + mainClassName += presenceClass; + if (this.state.hover) { + mainClassName += " mx_MemberTile_hover"; + } + + var nameEl; + if (this.state.hover) { + var PresenceLabel = sdk.getComponent("rooms.PresenceLabel"); + nameEl = ( +
+ +
{ name }
+ +
+ ); + } + else { + nameEl = ( +
+ { name } +
+ ); + } + + var MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); + + if (this.props.showInvite) { + // TODO + } + + return ( +
+
+ +
+ { nameEl } +
+ ); + } +}); From d6c7631dcc55459e8cea6bc620dfcdd591f186e8 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 15 Jan 2016 17:31:32 +0000 Subject: [PATCH 05/15] Show User avatar URLs --- src/Avatar.js | 13 ++++++++++++- src/components/views/rooms/MemberTile.js | 2 +- src/components/views/rooms/UserTile.js | 7 ++++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Avatar.js b/src/Avatar.js index e97ed6b673..0ef6c8d07b 100644 --- a/src/Avatar.js +++ b/src/Avatar.js @@ -15,7 +15,7 @@ limitations under the License. */ 'use strict'; - +var ContentRepo = require("matrix-js-sdk").ContentRepo; var MatrixClientPeg = require('./MatrixClientPeg'); module.exports = { @@ -37,6 +37,17 @@ module.exports = { return url; }, + avatarUrlForUser: function(user, width, height, resizeMethod) { + var url = ContentRepo.getHttpUriForMxc( + MatrixClientPeg.get().getHomeserverUrl(), user.avatarUrl, + width, height, resizeMethod + ); + if (!url || url.length === 0) { + return null; + } + return url; + }, + defaultAvatarUrlForString: function(s) { var images = [ '76cfa6', '50e2c2', 'f4c371' ]; var total = 0; diff --git a/src/components/views/rooms/MemberTile.js b/src/components/views/rooms/MemberTile.js index 304d759ea7..cb0bce839f 100644 --- a/src/components/views/rooms/MemberTile.js +++ b/src/components/views/rooms/MemberTile.js @@ -156,7 +156,7 @@ module.exports = React.createClass({ var av; if (member) { av = ( - + ); } else { diff --git a/src/components/views/rooms/UserTile.js b/src/components/views/rooms/UserTile.js index 4a06dc9bc3..3fce07b327 100644 --- a/src/components/views/rooms/UserTile.js +++ b/src/components/views/rooms/UserTile.js @@ -18,6 +18,7 @@ limitations under the License. var React = require('react'); +var Avatar = require("../../../Avatar"); var MatrixClientPeg = require('../../../MatrixClientPeg'); var sdk = require('../../../index'); var dis = require('../../../dispatcher'); @@ -106,7 +107,7 @@ module.exports = React.createClass({ ); } - var MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); + var BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); if (this.props.showInvite) { // TODO @@ -117,8 +118,8 @@ module.exports = React.createClass({ onClick={ this.props.onClick } onMouseEnter={ this.mouseEnter } onMouseLeave={ this.mouseLeave }>
- +
{ nameEl } From 71009d81b6864a73b8a7119fa0541a70ee4a0e48 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 18 Jan 2016 15:19:49 +0000 Subject: [PATCH 06/15] Factor out common code between MemberTile and UserTile into EntityTile. Use EntityTile. --- src/component-index.js | 1 + src/components/views/rooms/EntityTile.js | 118 ++++++++++++++++++ src/components/views/rooms/MemberTile.js | 91 +++----------- .../views/rooms/SearchableEntityList.js | 8 +- src/components/views/rooms/UserTile.js | 88 ++----------- 5 files changed, 149 insertions(+), 157 deletions(-) create mode 100644 src/components/views/rooms/EntityTile.js diff --git a/src/component-index.js b/src/component-index.js index b783f36a6c..3f84ac6392 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -60,6 +60,7 @@ module.exports.components['views.messages.MVideoBody'] = require('./components/v module.exports.components['views.messages.TextualBody'] = require('./components/views/messages/TextualBody'); module.exports.components['views.messages.TextualEvent'] = require('./components/views/messages/TextualEvent'); module.exports.components['views.messages.UnknownBody'] = require('./components/views/messages/UnknownBody'); +module.exports.components['views.rooms.EntityTile'] = require('./components/views/rooms/EntityTile'); module.exports.components['views.rooms.EventTile'] = require('./components/views/rooms/EventTile'); module.exports.components['views.rooms.MemberInfo'] = require('./components/views/rooms/MemberInfo'); module.exports.components['views.rooms.MemberList'] = require('./components/views/rooms/MemberList'); diff --git a/src/components/views/rooms/EntityTile.js b/src/components/views/rooms/EntityTile.js new file mode 100644 index 0000000000..24577080e8 --- /dev/null +++ b/src/components/views/rooms/EntityTile.js @@ -0,0 +1,118 @@ +/* +Copyright 2015, 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. +*/ + +'use strict'; + +var React = require('react'); + +var MatrixClientPeg = require('../../../MatrixClientPeg'); +var sdk = require('../../../index'); + + +var PRESENCE_CLASS = { + "offline": "mx_MemberTile_offline", + "online": "mx_MemberTile_online", + "unavailable": "mx_MemberTile_unavailable" +}; + +module.exports = React.createClass({ + displayName: 'EntityTile', + + propTypes: { + name: React.PropTypes.string, + title: React.PropTypes.string, + avatarJsx: React.PropTypes.any, // + presenceState: React.PropTypes.string, + presenceActiveAgo: React.PropTypes.number, + showInviteButton: React.PropTypes.bool, + shouldComponentUpdate: React.PropTypes.func, + onClick: React.PropTypes.func + }, + + getDefaultProps: function() { + return { + shouldComponentUpdate: function(nextProps, nextState) { return false; }, + onClick: function() {}, + presenceState: "offline", + presenceActiveAgo: -1, + showInviteButton: false, + }; + }, + + getInitialState: function() { + return { + hover: false + }; + }, + + shouldComponentUpdate: function(nextProps, nextState) { + if (this.state.hover !== nextState.hover) return true; + return this.props.shouldComponentUpdate(nextProps, nextState); + }, + + mouseEnter: function(e) { + this.setState({ 'hover': true }); + }, + + mouseLeave: function(e) { + this.setState({ 'hover': false }); + }, + + render: function() { + var presenceClass = PRESENCE_CLASS[this.props.presenceState]; + var mainClassName = "mx_MemberTile "; + mainClassName += presenceClass; + if (this.state.hover) { + mainClassName += " mx_MemberTile_hover"; + } + + var nameEl; + if (this.state.hover) { + var PresenceLabel = sdk.getComponent("rooms.PresenceLabel"); + nameEl = ( +
+ +
{ this.props.name }
+ +
+ ); + } + else { + nameEl = ( +
+ { this.props.name } +
+ ); + } + + var MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); + var BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); + + var av = this.props.avatarJsx || ; + + return ( +
+
+ {av} +
+ { nameEl } +
+ ); + } +}); diff --git a/src/components/views/rooms/MemberTile.js b/src/components/views/rooms/MemberTile.js index cb0bce839f..71b941b833 100644 --- a/src/components/views/rooms/MemberTile.js +++ b/src/components/views/rooms/MemberTile.js @@ -28,8 +28,7 @@ module.exports = React.createClass({ propTypes: { member: React.PropTypes.any, // RoomMember - onFinished: React.PropTypes.func, - customDisplayName: React.PropTypes.string // for 3pid invites + customDisplayName: React.PropTypes.string, // for 3pid invites }, getInitialState: function() { @@ -37,13 +36,12 @@ module.exports = React.createClass({ }, shouldComponentUpdate: function(nextProps, nextState) { - if (this.state.hover !== nextState.hover) return true; if (!this.props.member) { return false; } // e.g. 3pid members if ( this.member_last_modified_time === undefined || this.member_last_modified_time < nextProps.member.getLastModifiedTime() ) { - return true + return true; } if ( nextProps.member.user && @@ -55,14 +53,6 @@ module.exports = React.createClass({ return false; }, - mouseEnter: function(e) { - this.setState({ 'hover': true }); - }, - - mouseLeave: function(e) { - this.setState({ 'hover': false }); - }, - onClick: function(e) { if (!this.props.member) { return; } // e.g. 3pid members @@ -91,13 +81,21 @@ module.exports = React.createClass({ }, render: function() { + var MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); + var BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); + var EntityTile = sdk.getComponent('rooms.EntityTile'); + var member = this.props.member; - var isMyUser = false; var name = this._getDisplayName(); var active = -1; - var presenceClass = "mx_MemberTile_offline"; + var presenceState = (member && member.user) ? member.user.presence : null; + var av; if (member) { + av = ( + + ); + if (member.user) { this.user_last_modified_time = member.user.getLastModifiedTime(); @@ -105,75 +103,20 @@ module.exports = React.createClass({ active = ( (Date.now() - (member.user.lastPresenceTs - member.user.lastActiveAgo)) || -1 ); - - if (member.user.presence === "online") { - presenceClass = "mx_MemberTile_online"; - } - else if (member.user.presence === "unavailable") { - presenceClass = "mx_MemberTile_unavailable"; - } } this.member_last_modified_time = member.getLastModifiedTime(); - isMyUser = MatrixClientPeg.get().credentials.userId == member.userId; - - // if (this.props.member && this.props.member.powerLevelNorm > 0) { - // var img = "img/p/p" + Math.floor(20 * this.props.member.powerLevelNorm / 100) + ".png"; - // power = ; - // } - } - - - var mainClassName = "mx_MemberTile "; - mainClassName += presenceClass; - if (this.state.hover) { - mainClassName += " mx_MemberTile_hover"; - } - - var nameEl; - if (this.state.hover) { - var presenceState = (member && member.user) ? member.user.presence : null; - var PresenceLabel = sdk.getComponent("rooms.PresenceLabel"); - nameEl = ( -
- -
{ name }
- -
- ); - } - else { - nameEl = ( -
- { name } -
- ); - } - - var MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); - var BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); - - var av; - if (member) { - av = ( - - ); } else { av = ( ); } - + return ( -
-
- {av} -
- { nameEl } -
+ ); } }); diff --git a/src/components/views/rooms/SearchableEntityList.js b/src/components/views/rooms/SearchableEntityList.js index b8df4f6610..50c0a1b726 100644 --- a/src/components/views/rooms/SearchableEntityList.js +++ b/src/components/views/rooms/SearchableEntityList.js @@ -28,6 +28,7 @@ var SearchableEntityList = React.createClass({ emptyQueryShowsAll: React.PropTypes.bool, onSubmit: React.PropTypes.func, // fn(inputText) entities: React.PropTypes.array, + onEntityClick: React.PropTypes.func }, getDefaultProps: function() { @@ -35,7 +36,8 @@ var SearchableEntityList = React.createClass({ searchPlaceholderText: "Search", entities: [], emptyQueryShowsAll: false, - onSubmit: function() {} + onSubmit: function() {}, + onEntityClick: function() {} }; }, @@ -77,8 +79,8 @@ var SearchableEntityList = React.createClass({ placeholder={this.props.searchPlaceholderText} />
- {this.state.results.map(function(entity) { - return entity.getJsx(); + {this.state.results.map((entity) => { + return entity.getJsx(this.props.onEntityClick); })}
diff --git a/src/components/views/rooms/UserTile.js b/src/components/views/rooms/UserTile.js index 3fce07b327..6597796764 100644 --- a/src/components/views/rooms/UserTile.js +++ b/src/components/views/rooms/UserTile.js @@ -28,101 +28,29 @@ module.exports = React.createClass({ displayName: 'UserTile', propTypes: { - user: React.PropTypes.any.isRequired, // User - onInviteClick: React.PropTypes.func, //onInviteClick(User) - showInvite: React.PropTypes.bool, - onClick: React.PropTypes.func - }, - - getInitialState: function() { - return {}; - }, - - getDefaultProps: function() { - return { - onClick: function() {}, - onInviteClick: function() {}, - showInvite: false - }; - }, - - shouldComponentUpdate: function(nextProps, nextState) { - if (this.state.hover !== nextState.hover) return true; - return false; - }, - - mouseEnter: function(e) { - this.setState({ 'hover': true }); - }, - - mouseLeave: function(e) { - this.setState({ 'hover': false }); + user: React.PropTypes.any.isRequired // User }, render: function() { + var EntityTile = sdk.getComponent("rooms.EntityTile"); var user = this.props.user; var name = user.displayName || user.userId; - var isMyUser = MatrixClientPeg.get().credentials.userId == user.userId; var active = -1; - var presenceClass = "mx_MemberTile_offline"; - - this.user_last_modified_time = user.getLastModifiedTime(); // FIXME: make presence data update whenever User.presence changes... active = ( (Date.now() - (user.lastPresenceTs - user.lastActiveAgo)) || -1 ); - if (user.presence === "online") { - presenceClass = "mx_MemberTile_online"; - } - else if (user.presence === "unavailable") { - presenceClass = "mx_MemberTile_unavailable"; - } - - - var mainClassName = "mx_MemberTile "; - mainClassName += presenceClass; - if (this.state.hover) { - mainClassName += " mx_MemberTile_hover"; - } - - var nameEl; - if (this.state.hover) { - var PresenceLabel = sdk.getComponent("rooms.PresenceLabel"); - nameEl = ( -
- -
{ name }
- -
- ); - } - else { - nameEl = ( -
- { name } -
- ); - } - var BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); - - if (this.props.showInvite) { - // TODO - } + var avatarJsx = ( + + ); return ( -
-
- -
- { nameEl } -
+ ); } }); From c758c0f84f630d3652c67f9c84860f318a89a854 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 18 Jan 2016 16:47:31 +0000 Subject: [PATCH 07/15] Add invite button JSX --- src/components/views/rooms/EntityTile.js | 31 +++++++++++++------ .../views/rooms/SearchableEntityList.js | 8 ++--- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/components/views/rooms/EntityTile.js b/src/components/views/rooms/EntityTile.js index 24577080e8..08471d0776 100644 --- a/src/components/views/rooms/EntityTile.js +++ b/src/components/views/rooms/EntityTile.js @@ -23,9 +23,9 @@ var sdk = require('../../../index'); var PRESENCE_CLASS = { - "offline": "mx_MemberTile_offline", - "online": "mx_MemberTile_online", - "unavailable": "mx_MemberTile_unavailable" + "offline": "mx_EntityTile_offline", + "online": "mx_EntityTile_online", + "unavailable": "mx_EntityTile_unavailable" }; module.exports = React.createClass({ @@ -73,19 +73,19 @@ module.exports = React.createClass({ render: function() { var presenceClass = PRESENCE_CLASS[this.props.presenceState]; - var mainClassName = "mx_MemberTile "; + var mainClassName = "mx_EntityTile "; mainClassName += presenceClass; if (this.state.hover) { - mainClassName += " mx_MemberTile_hover"; + mainClassName += " mx_EntityTile_hover"; } var nameEl; if (this.state.hover) { var PresenceLabel = sdk.getComponent("rooms.PresenceLabel"); nameEl = ( -
- -
{ this.props.name }
+
+ +
{ this.props.name }
@@ -93,12 +93,22 @@ module.exports = React.createClass({ } else { nameEl = ( -
+
{ this.props.name }
); } + var inviteButton; + if (this.props.showInviteButton) { + inviteButton = ( +
+ +
+ ); + } + + var MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); var BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); @@ -108,10 +118,11 @@ module.exports = React.createClass({
-
+
{av}
{ nameEl } + { inviteButton }
); } diff --git a/src/components/views/rooms/SearchableEntityList.js b/src/components/views/rooms/SearchableEntityList.js index 50c0a1b726..52f3307bcd 100644 --- a/src/components/views/rooms/SearchableEntityList.js +++ b/src/components/views/rooms/SearchableEntityList.js @@ -27,8 +27,7 @@ var SearchableEntityList = React.createClass({ searchPlaceholderText: React.PropTypes.string, emptyQueryShowsAll: React.PropTypes.bool, onSubmit: React.PropTypes.func, // fn(inputText) - entities: React.PropTypes.array, - onEntityClick: React.PropTypes.func + entities: React.PropTypes.array }, getDefaultProps: function() { @@ -36,8 +35,7 @@ var SearchableEntityList = React.createClass({ searchPlaceholderText: "Search", entities: [], emptyQueryShowsAll: false, - onSubmit: function() {}, - onEntityClick: function() {} + onSubmit: function() {} }; }, @@ -80,7 +78,7 @@ var SearchableEntityList = React.createClass({
{this.state.results.map((entity) => { - return entity.getJsx(this.props.onEntityClick); + return entity.getJsx(); })}
From 165adde0c80ed04f0b74c380af1ec39e1af0f7ac Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 18 Jan 2016 16:55:51 +0000 Subject: [PATCH 08/15] Use EntityTile for 3PID entities rather than munging MemberTile --- src/components/views/rooms/MemberList.js | 9 +++-- src/components/views/rooms/MemberTile.js | 46 +++++++----------------- 2 files changed, 19 insertions(+), 36 deletions(-) diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index 71f9b7b81d..b775860461 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -285,6 +285,8 @@ module.exports = React.createClass({ // we shouldn't add them if the 3pid invite state key (token) is in the // member invite (content.third_party_invite.signed.token) var room = MatrixClientPeg.get().getRoom(this.props.roomId); + var EntityTile = sdk.getComponent("rooms.EntityTile"); + var BaseAvatar = sdk.getComponent("avatars.BaseAvatar"); if (room) { room.currentState.getStateEvents("m.room.third_party_invite").forEach( function(e) { @@ -294,9 +296,12 @@ module.exports = React.createClass({ if (memberEvent) { return; } + var avatarJsx = ( + + ); memberList.push( - + ) }) } diff --git a/src/components/views/rooms/MemberTile.js b/src/components/views/rooms/MemberTile.js index 71b941b833..7a7c7fed75 100644 --- a/src/components/views/rooms/MemberTile.js +++ b/src/components/views/rooms/MemberTile.js @@ -27,8 +27,7 @@ module.exports = React.createClass({ displayName: 'MemberTile', propTypes: { - member: React.PropTypes.any, // RoomMember - customDisplayName: React.PropTypes.string, // for 3pid invites + member: React.PropTypes.any.isRequired, // RoomMember }, getInitialState: function() { @@ -36,7 +35,6 @@ module.exports = React.createClass({ }, shouldComponentUpdate: function(nextProps, nextState) { - if (!this.props.member) { return false; } // e.g. 3pid members if ( this.member_last_modified_time === undefined || this.member_last_modified_time < nextProps.member.getLastModifiedTime() @@ -54,8 +52,6 @@ module.exports = React.createClass({ }, onClick: function(e) { - if (!this.props.member) { return; } // e.g. 3pid members - dis.dispatch({ action: 'view_user', member: this.props.member, @@ -63,21 +59,11 @@ module.exports = React.createClass({ }, _getDisplayName: function() { - if (this.props.customDisplayName) { - return this.props.customDisplayName; - } return this.props.member.name; }, getPowerLabel: function() { - if (!this.props.member) { - return this._getDisplayName(); - } - var label = this.props.member.userId; - if (this.state.isTargetMod) { - label += " - Mod (" + this.props.member.powerLevelNorm + "%)"; - } - return label; + return this.props.member.userId; }, render: function() { @@ -88,29 +74,21 @@ module.exports = React.createClass({ var member = this.props.member; var name = this._getDisplayName(); var active = -1; - var presenceState = (member && member.user) ? member.user.presence : null; + var presenceState = member.user ? member.user.presence : null; - var av; - if (member) { - av = ( - - ); + var av = ( + + ); - if (member.user) { - this.user_last_modified_time = member.user.getLastModifiedTime(); + if (member.user) { + this.user_last_modified_time = member.user.getLastModifiedTime(); - // FIXME: make presence data update whenever User.presence changes... - active = ( - (Date.now() - (member.user.lastPresenceTs - member.user.lastActiveAgo)) || -1 - ); - } - this.member_last_modified_time = member.getLastModifiedTime(); - } - else { - av = ( - + // FIXME: make presence data update whenever User.presence changes... + active = ( + (Date.now() - (member.user.lastPresenceTs - member.user.lastActiveAgo)) || -1 ); } + this.member_last_modified_time = member.getLastModifiedTime(); return ( Date: Mon, 18 Jan 2016 17:12:35 +0000 Subject: [PATCH 09/15] Add ability to click-to-invite --- src/Entities.js | 21 ++++++++++++++++++--- src/components/views/rooms/MemberList.js | 9 ++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/Entities.js b/src/Entities.js index 712f7b4dde..47103bfb65 100644 --- a/src/Entities.js +++ b/src/Entities.js @@ -55,10 +55,23 @@ class MemberEntity extends Entity { class UserEntity extends Entity { + constructor(model, showInviteButton, inviteFn) { + super(model); + this.showInviteButton = Boolean(showInviteButton); + this.inviteFn = inviteFn; + } + + onClick() { + if (this.inviteFn) { + this.inviteFn(this.model.userId); + } + } + getJsx() { var UserTile = sdk.getComponent("rooms.UserTile"); return ( - + ); } @@ -82,11 +95,13 @@ module.exports = { /** * @param {User[]} users + * @param {boolean} showInviteButton + * @param {Function} inviteFn Called with the user ID. * @return {Entity[]} */ - fromUsers: function(users) { + fromUsers: function(users, showInviteButton, inviteFn) { return users.map(function(u) { - return new UserEntity(u); + return new UserEntity(u, showInviteButton, inviteFn); }) } }; diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index b775860461..ff76e64c56 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -320,11 +320,10 @@ module.exports = React.createClass({ // TODO: Cache this calculation var room = MatrixClientPeg.get().getRoom(this.props.roomId); var allUsers = MatrixClientPeg.get().getUsers(); - // only add Users if they don't exist in the member list + // only add Users if they are not joined allUsers = allUsers.filter(function(u) { - return room.getMember(u.userId) === null; + return !room.hasMembershipState(u.userId, "join"); }); - var SearchableEntityList = sdk.getComponent("rooms.SearchableEntityList"); return ( @@ -332,9 +331,9 @@ module.exports = React.createClass({ onSubmit={this.onInvite} entities={ Entities.fromRoomMembers( - room.currentState.getMembers() // ALLLLL OF THEM + room.getJoinedMembers() ).concat( - Entities.fromUsers(allUsers) + Entities.fromUsers(allUsers, true, this.onInvite) ) } /> ); From f1042bd1c4741ec7e05a424f8e35068fbb734fe3 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 18 Jan 2016 17:21:28 +0000 Subject: [PATCH 10/15] UI tweaks --- src/components/views/rooms/EntityTile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/EntityTile.js b/src/components/views/rooms/EntityTile.js index 08471d0776..268de25e14 100644 --- a/src/components/views/rooms/EntityTile.js +++ b/src/components/views/rooms/EntityTile.js @@ -103,7 +103,7 @@ module.exports = React.createClass({ if (this.props.showInviteButton) { inviteButton = (
- +
); } From a8643ff0398f6d43453a4108c62e3d1d975d1bae Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 18 Jan 2016 17:37:17 +0000 Subject: [PATCH 11/15] Move power level stuff to EntityTile for now --- src/components/views/rooms/EntityTile.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/components/views/rooms/EntityTile.js b/src/components/views/rooms/EntityTile.js index 268de25e14..a0135fb0c3 100644 --- a/src/components/views/rooms/EntityTile.js +++ b/src/components/views/rooms/EntityTile.js @@ -108,6 +108,15 @@ module.exports = React.createClass({ ); } + var power; + var powerLevel = this.props.powerLevel; + if (powerLevel >= 50 && powerLevel < 99) { + power = Mod; + } + if (powerLevel >= 99) { + power = Admin; + } + var MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); var BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); @@ -122,6 +131,7 @@ module.exports = React.createClass({ {av}
{ nameEl } + { power } { inviteButton }
); From d2708cf4d48f52c903a2a12b5958e0d2626bd3be Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 19 Jan 2016 14:51:26 +0000 Subject: [PATCH 12/15] Filter the membership list in addition to the search area for the input text --- src/components/views/rooms/MemberList.js | 25 +++++++++------- .../views/rooms/SearchableEntityList.js | 30 +++++++++++++++++-- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index ff76e64c56..275aec212e 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -264,13 +264,23 @@ module.exports = React.createClass({ return latB - latA; }, - makeMemberTiles: function(membership) { + onSearchQueryChanged: function(input) { + this.setState({ + searchQuery: input + }); + }, + + 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]; + if (query && m.name.toLowerCase().indexOf(query) !== 0) { + return false; + } return m.membership == membership; }).map(function(userId) { var m = self.memberDict[userId]; @@ -329,20 +339,15 @@ module.exports = React.createClass({ return ( + onQueryChanged={this.onSearchQueryChanged} + entities={Entities.fromUsers(allUsers, true, this.onInvite)} /> ); } }, render: function() { var invitedSection = null; - var invitedMemberTiles = this.makeMemberTiles('invite'); + var invitedMemberTiles = this.makeMemberTiles('invite', this.state.searchQuery); if (invitedMemberTiles.length > 0) { invitedSection = (
@@ -359,7 +364,7 @@ module.exports = React.createClass({ {this.inviteTile()}
- {this.makeMemberTiles('join')} + {this.makeMemberTiles('join', this.state.searchQuery)}
{invitedSection} diff --git a/src/components/views/rooms/SearchableEntityList.js b/src/components/views/rooms/SearchableEntityList.js index 52f3307bcd..8657293478 100644 --- a/src/components/views/rooms/SearchableEntityList.js +++ b/src/components/views/rooms/SearchableEntityList.js @@ -26,16 +26,20 @@ var SearchableEntityList = React.createClass({ propTypes: { searchPlaceholderText: React.PropTypes.string, emptyQueryShowsAll: React.PropTypes.bool, + showInputBox: React.PropTypes.bool, + onQueryChanged: React.PropTypes.func, // fn(inputText) onSubmit: React.PropTypes.func, // fn(inputText) entities: React.PropTypes.array }, getDefaultProps: function() { return { + showInputBox: true, searchPlaceholderText: "Search", entities: [], emptyQueryShowsAll: false, - onSubmit: function() {} + onSubmit: function() {}, + onQueryChanged: function(input) {} }; }, @@ -46,12 +50,24 @@ var SearchableEntityList = React.createClass({ }; }, + /** + * Public-facing method to set the input query text to the given input. + * @param {string} input + */ + setQuery: function(input) { + this.setState({ + query: input, + results: this.getSearchResults(input) + }); + }, + onQueryChanged: function(ev) { var q = ev.target.value; this.setState({ query: q, results: this.getSearchResults(q) }); + this.props.onQueryChanged(q); }, onQuerySubmit: function(ev) { @@ -69,13 +85,21 @@ var SearchableEntityList = React.createClass({ }, render: function() { - return ( -
+ var inputBox; + + if (this.props.showInputBox) { + inputBox = (
+ ); + } + + return ( +
+ {inputBox}
{this.state.results.map((entity) => { return entity.getJsx(); From e4e339323207317e004e085a2317df45c392d511 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 19 Jan 2016 17:09:36 +0000 Subject: [PATCH 13/15] Temporarily comment out address book until perf can be looked at given other PRs now depend on this PR --- src/components/views/rooms/MemberList.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index 275aec212e..bf96b51604 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -329,18 +329,18 @@ module.exports = React.createClass({ } else { // TODO: Cache this calculation var room = MatrixClientPeg.get().getRoom(this.props.roomId); - var allUsers = MatrixClientPeg.get().getUsers(); + /* var allUsers = MatrixClientPeg.get().getUsers(); // only add Users if they are not joined allUsers = allUsers.filter(function(u) { return !room.hasMembershipState(u.userId, "join"); - }); + }); */ var SearchableEntityList = sdk.getComponent("rooms.SearchableEntityList"); return ( + entities={[] /* Entities.fromUsers(allUsers, true, this.onInvite) */} /> ); } }, From 91c224aaf40026ab6b6e59459620edba71e8a831 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 20 Jan 2016 13:41:03 +0000 Subject: [PATCH 14/15] Review comments: Remove stuff which shouldn't have been there --- src/components/views/rooms/MemberList.js | 6 +----- src/components/views/rooms/MemberTile.js | 8 -------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index bf96b51604..4c84b00e70 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -296,7 +296,6 @@ module.exports = React.createClass({ // member invite (content.third_party_invite.signed.token) var room = MatrixClientPeg.get().getRoom(this.props.roomId); var EntityTile = sdk.getComponent("rooms.EntityTile"); - var BaseAvatar = sdk.getComponent("avatars.BaseAvatar"); if (room) { room.currentState.getStateEvents("m.room.third_party_invite").forEach( function(e) { @@ -306,12 +305,9 @@ module.exports = React.createClass({ if (memberEvent) { return; } - var avatarJsx = ( - - ); memberList.push( + name={e.getContent().display_name} /> ) }) } diff --git a/src/components/views/rooms/MemberTile.js b/src/components/views/rooms/MemberTile.js index 9136e848f1..d5c124ad21 100644 --- a/src/components/views/rooms/MemberTile.js +++ b/src/components/views/rooms/MemberTile.js @@ -79,14 +79,6 @@ module.exports = React.createClass({ var av = ( ); - var power; - var powerLevel = this.props.member.powerLevel; - if (powerLevel >= 50 && powerLevel < 99) { - power = Mod; - } - if (powerLevel >= 99) { - power = Admin; - } if (member.user) { this.user_last_modified_time = member.user.getLastModifiedTime(); From 391c653d24c56465e15002d320ccba65b455a6a4 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 20 Jan 2016 14:14:04 +0000 Subject: [PATCH 15/15] Lazy-load the user list to improve perf Still slow when typing due to adding 1000 tiles to the DOM, but it is at least a lot better than before (which would stutter on ANY change to the member list) --- src/components/views/rooms/EntityTile.js | 2 +- src/components/views/rooms/MemberList.js | 20 +++++++++++-------- .../views/rooms/SearchableEntityList.js | 6 ++++++ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/components/views/rooms/EntityTile.js b/src/components/views/rooms/EntityTile.js index a0135fb0c3..55629aa8b0 100644 --- a/src/components/views/rooms/EntityTile.js +++ b/src/components/views/rooms/EntityTile.js @@ -121,7 +121,7 @@ module.exports = React.createClass({ var MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); var BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); - var av = this.props.avatarJsx || ; + var av = this.props.avatarJsx || ; return (
); } else { - // TODO: Cache this calculation - var room = MatrixClientPeg.get().getRoom(this.props.roomId); - /* var allUsers = MatrixClientPeg.get().getUsers(); - // only add Users if they are not joined - allUsers = allUsers.filter(function(u) { - return !room.hasMembershipState(u.userId, "join"); - }); */ var SearchableEntityList = sdk.getComponent("rooms.SearchableEntityList"); return ( + entities={Entities.fromUsers(this.userList || [], true, this.onInvite)} /> ); } }, diff --git a/src/components/views/rooms/SearchableEntityList.js b/src/components/views/rooms/SearchableEntityList.js index 8657293478..b6232362ac 100644 --- a/src/components/views/rooms/SearchableEntityList.js +++ b/src/components/views/rooms/SearchableEntityList.js @@ -50,6 +50,12 @@ var SearchableEntityList = React.createClass({ }; }, + componentWillUnmount: function() { + // pretend the query box was blanked out else filters could still be + // applied to other components which rely on onQueryChanged. + this.props.onQueryChanged(""); + }, + /** * Public-facing method to set the input query text to the given input. * @param {string} input