Merge pull request #1402 from matrix-org/luke/groups-add-featured-rooms-and-users

GroupView: Add a User
This commit is contained in:
David Baker 2017-09-21 14:19:58 +01:00 committed by GitHub
commit 60d444b841
3 changed files with 140 additions and 21 deletions

View file

@ -17,6 +17,7 @@ limitations under the License.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Promise from 'bluebird';
import MatrixClientPeg from '../../MatrixClientPeg'; import MatrixClientPeg from '../../MatrixClientPeg';
import sdk from '../../index'; import sdk from '../../index';
import dis from '../../dispatcher'; import dis from '../../dispatcher';
@ -38,6 +39,9 @@ const RoomSummaryType = PropTypes.shape({
const UserSummaryType = PropTypes.shape({ const UserSummaryType = PropTypes.shape({
summaryInfo: PropTypes.shape({ summaryInfo: PropTypes.shape({
user_id: PropTypes.string.isRequired, user_id: PropTypes.string.isRequired,
role_id: PropTypes.string,
avatar_url: PropTypes.string,
displayname: PropTypes.string,
}).isRequired, }).isRequired,
}); });
@ -51,20 +55,32 @@ const CategoryRoomList = React.createClass({
name: PropTypes.string, name: PropTypes.string,
}).isRequired, }).isRequired,
}), }),
// Whether the list should be editable
editing: PropTypes.bool.isRequired,
}, },
render: function() { render: function() {
const roomNodes = this.props.rooms.map((r) => { const roomNodes = this.props.rooms.map((r) => {
return <FeaturedRoom key={r.room_id} summaryInfo={r} />; return <FeaturedRoom key={r.room_id} summaryInfo={r} />;
}); });
let catHeader = null; let catHeader = null;
if (this.props.category && this.props.category.profile) { if (this.props.category && this.props.category.profile) {
catHeader = <div className="mx_GroupView_featuredThings_category">{this.props.category.profile.name}</div>; catHeader = <div className="mx_GroupView_featuredThings_category">{this.props.category.profile.name}</div>;
} }
return <div> return <div className="mx_GroupView_featuredThings_container">
{catHeader} {catHeader}
{roomNodes} {roomNodes}
</div>; </div>;
// TODO: Modify UserPickerDialog to allow picking of rooms, and then use it here
// const TintableSvg = sdk.getComponent("elements.TintableSvg");
// <AccessibleButton className="mx_GroupView_featuredThings_addButton">
// <TintableSvg src="img/icons-create-room.svg" width="64" height="64"/>
// <div className="mx_GroupView_featuredThings_addButton_label">
// {_t('Add a Room')}
// </div>
// </AccessibleButton>
}, },
}); });
@ -122,9 +138,59 @@ const RoleUserList = React.createClass({
name: PropTypes.string, name: PropTypes.string,
}).isRequired, }).isRequired,
}), }),
groupId: PropTypes.string.isRequired,
// Whether the list should be editable
editing: PropTypes.bool.isRequired,
},
onAddUsersClicked: function(ev) {
ev.preventDefault();
const UserPickerDialog = sdk.getComponent("dialogs.UserPickerDialog");
Modal.createTrackedDialog('Add Users to Group Summary', '', UserPickerDialog, {
title: _t('Add users to the group summary'),
description: _t("Who would you like to add to this summary?"),
placeholder: _t("Name or matrix ID"),
button: _t("Add to summary"),
validAddressTypes: ['mx'],
groupId: this.props.groupId,
onFinished: (success, addrs) => {
if (!success) return;
const errorList = [];
Promise.all(addrs.map((addr) => {
return MatrixClientPeg.get()
.addUserToGroupSummary(this.props.groupId, addr.address)
.catch(() => { errorList.push(addr.address); })
.reflect();
})).then(() => {
if (errorList.length === 0) {
return;
}
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog(
'Failed to add the following users to the group summary',
'', ErrorDialog,
{
title: _t(
"Failed to add the following users to the summary of %(groupId)s:",
{groupId: this.props.groupId},
),
description: errorList.join(", "),
});
});
},
});
}, },
render: function() { render: function() {
const TintableSvg = sdk.getComponent("elements.TintableSvg");
const addButton = this.props.editing ?
(<AccessibleButton className="mx_GroupView_featuredThings_addButton" onClick={this.onAddUsersClicked}>
<TintableSvg src="img/icons-create-room.svg" width="64" height="64"/>
<div className="mx_GroupView_featuredThings_addButton_label">
{_t('Add a User')}
</div>
</AccessibleButton>) : null;
const userNodes = this.props.users.map((u) => { const userNodes = this.props.users.map((u) => {
return <FeaturedUser key={u.user_id} summaryInfo={u} />; return <FeaturedUser key={u.user_id} summaryInfo={u} />;
}); });
@ -132,9 +198,10 @@ const RoleUserList = React.createClass({
if (this.props.role && this.props.role.profile) { if (this.props.role && this.props.role.profile) {
roleHeader = <div className="mx_GroupView_featuredThings_category">{this.props.role.profile.name}</div>; roleHeader = <div className="mx_GroupView_featuredThings_category">{this.props.role.profile.name}</div>;
} }
return <div> return <div className="mx_GroupView_featuredThings_container">
{roleHeader} {roleHeader}
{userNodes} {userNodes}
{addButton}
</div>; </div>;
}, },
}); });
@ -158,13 +225,16 @@ const FeaturedUser = React.createClass({
}, },
render: function() { render: function() {
// Add avatar once we get profile info inline in the summary response const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
//const BaseAvatar = sdk.getComponent("avatars.BaseAvatar"); const name = this.props.summaryInfo.displayname || this.props.summaryInfo.user_id;
const permalink = 'https://matrix.to/#/' + this.props.summaryInfo.user_id; const permalink = 'https://matrix.to/#/' + this.props.summaryInfo.user_id;
const userNameNode = <a href={permalink} onClick={this.onClick} >{this.props.summaryInfo.user_id}</a>; const userNameNode = <a href={permalink} onClick={this.onClick}>{name}</a>;
const httpUrl = MatrixClientPeg.get()
.mxcUrlToHttp(this.props.summaryInfo.avatar_url, 64, 64);
return <AccessibleButton className="mx_GroupView_featuredThing" onClick={this.onClick}> return <AccessibleButton className="mx_GroupView_featuredThing" onClick={this.onClick}>
<BaseAvatar name={name} url={httpUrl} width={64} height={64} />
<div className="mx_GroupView_featuredThing_name">{userNameNode}</div> <div className="mx_GroupView_featuredThing_name">{userNameNode}</div>
</AccessibleButton>; </AccessibleButton>;
}, },
@ -369,8 +439,6 @@ export default React.createClass({
_getFeaturedRoomsNode() { _getFeaturedRoomsNode() {
const summary = this.state.summary; const summary = this.state.summary;
if (summary.rooms_section.rooms.length == 0) return null;
const defaultCategoryRooms = []; const defaultCategoryRooms = [];
const categoryRooms = {}; const categoryRooms = {};
summary.rooms_section.rooms.forEach((r) => { summary.rooms_section.rooms.forEach((r) => {
@ -386,13 +454,16 @@ export default React.createClass({
} }
}); });
let defaultCategoryNode = null; const defaultCategoryNode = <CategoryRoomList
if (defaultCategoryRooms.length > 0) { rooms={defaultCategoryRooms}
defaultCategoryNode = <CategoryRoomList rooms={defaultCategoryRooms} />; editing={this.state.editing}/>;
}
const categoryRoomNodes = Object.keys(categoryRooms).map((catId) => { const categoryRoomNodes = Object.keys(categoryRooms).map((catId) => {
const cat = summary.rooms_section.categories[catId]; const cat = summary.rooms_section.categories[catId];
return <CategoryRoomList key={catId} rooms={categoryRooms[catId]} category={cat} />; return <CategoryRoomList
key={catId}
rooms={categoryRooms[catId]}
category={cat}
editing={this.state.editing}/>;
}); });
return <div className="mx_GroupView_featuredThings"> return <div className="mx_GroupView_featuredThings">
@ -407,8 +478,6 @@ export default React.createClass({
_getFeaturedUsersNode() { _getFeaturedUsersNode() {
const summary = this.state.summary; const summary = this.state.summary;
if (summary.users_section.users.length == 0) return null;
const noRoleUsers = []; const noRoleUsers = [];
const roleUsers = {}; const roleUsers = {};
summary.users_section.users.forEach((u) => { summary.users_section.users.forEach((u) => {
@ -424,13 +493,18 @@ export default React.createClass({
} }
}); });
let noRoleNode = null; const noRoleNode = <RoleUserList
if (noRoleUsers.length > 0) { users={noRoleUsers}
noRoleNode = <RoleUserList users={noRoleUsers} />; groupId={this.props.groupId}
} editing={this.state.editing}/>;
const roleUserNodes = Object.keys(roleUsers).map((roleId) => { const roleUserNodes = Object.keys(roleUsers).map((roleId) => {
const role = summary.users_section.roles[roleId]; const role = summary.users_section.roles[roleId];
return <RoleUserList key={roleId} users={roleUsers[roleId]} role={role} />; return <RoleUserList
key={roleId}
users={roleUsers[roleId]}
role={role}
groupId={this.props.groupId}
editing={this.state.editing}/>;
}); });
return <div className="mx_GroupView_featuredThings"> return <div className="mx_GroupView_featuredThings">
@ -561,6 +635,8 @@ export default React.createClass({
onChange={this._onLongDescChange} onChange={this._onLongDescChange}
tabIndex="3" tabIndex="3"
/> />
{this._getFeaturedRoomsNode()}
{this._getFeaturedUsersNode()}
</div>; </div>;
} else { } else {
const groupAvatarUrl = summary.profile ? summary.profile.avatar_url : null; const groupAvatarUrl = summary.profile ? summary.profile.avatar_url : null;

View file

@ -40,6 +40,7 @@ module.exports = React.createClass({
focus: PropTypes.bool, focus: PropTypes.bool,
validAddressTypes: PropTypes.arrayOf(PropTypes.oneOf(addressTypes)), validAddressTypes: PropTypes.arrayOf(PropTypes.oneOf(addressTypes)),
onFinished: PropTypes.func.isRequired, onFinished: PropTypes.func.isRequired,
groupId: PropTypes.string,
}, },
getDefaultProps: function() { getDefaultProps: function() {
@ -140,7 +141,9 @@ module.exports = React.createClass({
// Only do search if there is something to search // Only do search if there is something to search
if (query.length > 0 && query != '@' && query.length >= 2) { if (query.length > 0 && query != '@' && query.length >= 2) {
this.queryChangedDebouncer = setTimeout(() => { this.queryChangedDebouncer = setTimeout(() => {
if (this.state.serverSupportsUserDirectory) { if (this.props.groupId) {
this._doNaiveGroupSearch(query);
} else if (this.state.serverSupportsUserDirectory) {
this._doUserDirectorySearch(query); this._doUserDirectorySearch(query);
} else { } else {
this._doLocalSearch(query); this._doLocalSearch(query);
@ -185,6 +188,40 @@ module.exports = React.createClass({
if (this._cancelThreepidLookup) this._cancelThreepidLookup(); if (this._cancelThreepidLookup) this._cancelThreepidLookup();
}, },
_doNaiveGroupSearch: function(query) {
const lowerCaseQuery = query.toLowerCase();
this.setState({
busy: true,
query,
searchError: null,
});
MatrixClientPeg.get().getGroupUsers(this.props.groupId).then((resp) => {
const results = [];
resp.chunk.forEach((u) => {
const userIdMatch = u.user_id.toLowerCase().includes(lowerCaseQuery);
const displayNameMatch = (u.displayname || '').toLowerCase().includes(lowerCaseQuery);
if (!(userIdMatch || displayNameMatch)) {
return;
}
results.push({
user_id: u.user_id,
avatar_url: u.avatar_url,
display_name: u.displayname,
});
});
this._processResults(results, query);
}).catch((err) => {
console.error('Error whilst searching group users: ', err);
this.setState({
searchError: err.errcode ? err.message : _t('Something went wrong!'),
});
}).done(() => {
this.setState({
busy: false,
});
});
},
_doUserDirectorySearch: function(query) { _doUserDirectorySearch: function(query) {
this.setState({ this.setState({
busy: true, busy: true,

View file

@ -886,5 +886,11 @@
"Leave %(groupName)s?": "Leave %(groupName)s?", "Leave %(groupName)s?": "Leave %(groupName)s?",
"%(widgetName)s widget modified by %(senderName)s": "%(widgetName)s widget modified by %(senderName)s", "%(widgetName)s widget modified by %(senderName)s": "%(widgetName)s widget modified by %(senderName)s",
"Robot check is currently unavailable on desktop - please use a <a>web browser</a>": "Robot check is currently unavailable on desktop - please use a <a>web browser</a>", "Robot check is currently unavailable on desktop - please use a <a>web browser</a>": "Robot check is currently unavailable on desktop - please use a <a>web browser</a>",
"Flair": "Flair" "Flair": "Flair",
"Add a Room": "Add a Room",
"Add a User": "Add a User",
"Add users to the group summary": "Add users to the group summary",
"Who would you like to add to this summary?": "Who would you like to add to this summary?",
"Add to summary": "Add to summary",
"Failed to add the following users to the summary of %(groupId)s:": "Failed to add the following users to the summary of %(groupId)s:"
} }