diff --git a/src/Entities.js b/src/Entities.js
index 5a666b1493..ac3c976797 100644
--- a/src/Entities.js
+++ b/src/Entities.js
@@ -108,6 +108,15 @@ class UserEntity extends Entity {
module.exports = {
+ newEntity: function(jsx, matchFn) {
+ var entity = new Entity();
+ entity.getJsx = function() {
+ return jsx;
+ };
+ entity.matches = matchFn;
+ return entity;
+ },
+
/**
* @param {RoomMember[]} members
* @return {Entity[]}
diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js
index 9172d43a24..7990b71bd7 100644
--- a/src/components/views/rooms/MemberList.js
+++ b/src/components/views/rooms/MemberList.js
@@ -311,6 +311,24 @@ module.exports = React.createClass({
},
onSearchQueryChanged: function(input) {
+ var EntityTile = sdk.getComponent("rooms.EntityTile");
+
+ var label;
+ if (input[0] === "@") {
+ label = input;
+ }
+ else {
+ label = "Email: " + input;
+ }
+
+ this._emailEntity = new Entities.newEntity(
+ ,
+ function(query) {
+ return true; // always show this
+ }
+ );
+
this.setState({
searchQuery: input
});
@@ -369,12 +387,19 @@ module.exports = React.createClass({
);
} else {
var SearchableEntityList = sdk.getComponent("rooms.SearchableEntityList");
-
+ var entities = Entities.fromUsers(this.userList || [], true, this.onInvite);
+
+ // Add an "Email: foo@bar.com" tile as the first tile
+ if (this._emailEntity) {
+ entities.unshift(this._emailEntity);
+ }
+
+
return (
+ entities={entities} />
);
}
},
diff --git a/src/components/views/rooms/SearchableEntityList.js b/src/components/views/rooms/SearchableEntityList.js
index efaebfd655..b2e20f23db 100644
--- a/src/components/views/rooms/SearchableEntityList.js
+++ b/src/components/views/rooms/SearchableEntityList.js
@@ -46,10 +46,17 @@ var SearchableEntityList = React.createClass({
getInitialState: function() {
return {
query: "",
- results: this.getSearchResults("")
+ results: this.getSearchResults("", this.props.entities)
};
},
+ componentWillReceiveProps: function(newProps) {
+ // recalculate the search results in case we got new entities
+ this.setState({
+ results: this.getSearchResults(this.state.query, newProps.entities)
+ });
+ },
+
componentWillUnmount: function() {
// pretend the query box was blanked out else filters could still be
// applied to other components which rely on onQueryChanged.
@@ -63,7 +70,7 @@ var SearchableEntityList = React.createClass({
setQuery: function(input) {
this.setState({
query: input,
- results: this.getSearchResults(input)
+ results: this.getSearchResults(input, this.props.entities)
});
},
@@ -71,9 +78,15 @@ var SearchableEntityList = React.createClass({
var q = ev.target.value;
this.setState({
query: q,
- results: this.getSearchResults(q)
+ results: this.getSearchResults(q, this.props.entities)
+ }, () => {
+ // invoke the callback AFTER we've flushed the new state. We need to
+ // do this because onQueryChanged can result in new props being passed
+ // to this component, which will then try to recalculate the search
+ // list. If we do this without flushing, we'll recalc with the last
+ // search term and not the current one!
+ this.props.onQueryChanged(q);
});
- this.props.onQueryChanged(q);
},
onQuerySubmit: function(ev) {
@@ -81,11 +94,11 @@ var SearchableEntityList = React.createClass({
this.props.onSubmit(this.state.query);
},
- getSearchResults: function(query) {
+ getSearchResults: function(query, entities) {
if (!query || query.length === 0) {
- return this.props.emptyQueryShowsAll ? this.props.entities : []
+ return this.props.emptyQueryShowsAll ? entities : []
}
- return this.props.entities.filter(function(e) {
+ return entities.filter(function(e) {
return e.matches(query);
});
},
@@ -95,7 +108,7 @@ var SearchableEntityList = React.createClass({
if (this.props.showInputBox) {
inputBox = (
-