mirror of
https://github.com/element-hq/element-web
synced 2024-11-27 03:36:07 +03:00
Merge pull request #1402 from matrix-org/luke/groups-add-featured-rooms-and-users
GroupView: Add a User
This commit is contained in:
commit
60d444b841
3 changed files with 140 additions and 21 deletions
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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:"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue