Implement adding rooms to the group summary

This includes making UserPickerDialog more generic such that it can also pick rooms from groups.
pull/21833/head
Luke Barnard 2017-09-21 16:53:10 +01:00
parent 5940b11fd2
commit 5f840a3031
5 changed files with 131 additions and 31 deletions

View File

@ -20,8 +20,8 @@ import MultiInviter from './utils/MultiInviter';
import { _t } from './languageHandler';
export function showGroupInviteDialog(groupId) {
const UserPickerDialog = sdk.getComponent("dialogs.UserPickerDialog");
Modal.createTrackedDialog('Group Invite', '', UserPickerDialog, {
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
Modal.createTrackedDialog('Group Invite', '', AddressPickerDialog, {
title: _t('Invite new group members'),
description: _t("Who would you like to add to this group?"),
placeholder: _t("Name or matrix ID"),

View File

@ -50,8 +50,8 @@ export function inviteMultipleToRoom(roomId, addrs) {
}
export function showStartChatInviteDialog() {
const UserPickerDialog = sdk.getComponent("dialogs.UserPickerDialog");
Modal.createTrackedDialog('Start a chat', '', UserPickerDialog, {
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
Modal.createTrackedDialog('Start a chat', '', AddressPickerDialog, {
title: _t('Start a chat'),
description: _t("Who would you like to communicate with?"),
placeholder: _t("Email, name or matrix ID"),
@ -61,8 +61,8 @@ export function showStartChatInviteDialog() {
}
export function showRoomInviteDialog(roomId) {
const UserPickerDialog = sdk.getComponent("dialogs.UserPickerDialog");
Modal.createTrackedDialog('Chat Invite', '', UserPickerDialog, {
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
Modal.createTrackedDialog('Chat Invite', '', AddressPickerDialog, {
title: _t('Invite new room members'),
description: _t('Who would you like to add to this room?'),
button: _t('Send Invites'),

View File

@ -55,32 +55,74 @@ const CategoryRoomList = React.createClass({
name: PropTypes.string,
}).isRequired,
}),
groupId: PropTypes.string.isRequired,
// Whether the list should be editable
editing: PropTypes.bool.isRequired,
},
onAddRoomsClicked: function(ev) {
ev.preventDefault();
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
Modal.createTrackedDialog('Add Rooms to Group Summary', '', AddressPickerDialog, {
title: _t('Add rooms to the group summary'),
description: _t("Which rooms would you like to add to this summary?"),
placeholder: _t("Room name or alias"),
button: _t("Add to summary"),
pickerType: 'room',
validAddressTypes: ['mx'],
groupId: this.props.groupId,
onFinished: (success, addrs) => {
if (!success) return;
const errorList = [];
Promise.all(addrs.map((addr) => {
return MatrixClientPeg.get()
.addRoomToGroupSummary(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 room to the group summary',
'', ErrorDialog,
{
title: _t(
"Failed to add the following rooms to the summary of %(groupId)s:",
{groupId: this.props.groupId},
),
description: errorList.join(", "),
});
});
},
});
},
render: function() {
const TintableSvg = sdk.getComponent("elements.TintableSvg");
const addButton = this.props.editing ?
(<AccessibleButton className="mx_GroupView_featuredThings_addButton" onClick={this.onAddRoomsClicked}>
<TintableSvg src="img/icons-create-room.svg" width="64" height="64"/>
<div className="mx_GroupView_featuredThings_addButton_label">
{_t('Add a Room')}
</div>
</AccessibleButton>) : <div />;
const roomNodes = this.props.rooms.map((r) => {
return <FeaturedRoom key={r.room_id} summaryInfo={r} />;
});
let catHeader = null;
let catHeader = <div />;
if (this.props.category && this.props.category.profile) {
catHeader = <div className="mx_GroupView_featuredThings_category">{this.props.category.profile.name}</div>;
}
return <div className="mx_GroupView_featuredThings_container">
{catHeader}
{roomNodes}
{addButton}
</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>
},
});
@ -146,8 +188,8 @@ const RoleUserList = React.createClass({
onAddUsersClicked: function(ev) {
ev.preventDefault();
const UserPickerDialog = sdk.getComponent("dialogs.UserPickerDialog");
Modal.createTrackedDialog('Add Users to Group Summary', '', UserPickerDialog, {
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
Modal.createTrackedDialog('Add Users to Group Summary', '', AddressPickerDialog, {
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"),
@ -190,11 +232,11 @@ const RoleUserList = React.createClass({
<div className="mx_GroupView_featuredThings_addButton_label">
{_t('Add a User')}
</div>
</AccessibleButton>) : null;
</AccessibleButton>) : <div />;
const userNodes = this.props.users.map((u) => {
return <FeaturedUser key={u.user_id} summaryInfo={u} />;
});
let roleHeader = null;
let roleHeader = <div />;
if (this.props.role && this.props.role.profile) {
roleHeader = <div className="mx_GroupView_featuredThings_category">{this.props.role.profile.name}</div>;
}
@ -456,6 +498,7 @@ export default React.createClass({
const defaultCategoryNode = <CategoryRoomList
rooms={defaultCategoryRooms}
groupId={this.props.groupId}
editing={this.state.editing}/>;
const categoryRoomNodes = Object.keys(categoryRooms).map((catId) => {
const cat = summary.rooms_section.categories[catId];
@ -463,6 +506,7 @@ export default React.createClass({
key={catId}
rooms={categoryRooms[catId]}
category={cat}
groupId={this.props.groupId}
editing={this.state.editing}/>;
});

View File

@ -28,7 +28,7 @@ const TRUNCATE_QUERY_LIST = 40;
const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200;
module.exports = React.createClass({
displayName: "UserPickerDialog",
displayName: "AddressPickerDialog",
propTypes: {
title: PropTypes.string.isRequired,
@ -41,6 +41,7 @@ module.exports = React.createClass({
validAddressTypes: PropTypes.arrayOf(PropTypes.oneOf(addressTypes)),
onFinished: PropTypes.func.isRequired,
groupId: PropTypes.string,
pickerType: PropTypes.oneOf(['user', 'room']),
},
getDefaultProps: function() {
@ -48,6 +49,7 @@ module.exports = React.createClass({
value: "",
focus: true,
validAddressTypes: addressTypes,
pickerType: 'user',
};
},
@ -141,6 +143,7 @@ module.exports = React.createClass({
// Only do search if there is something to search
if (query.length > 0 && query != '@' && query.length >= 2) {
this.queryChangedDebouncer = setTimeout(() => {
if (this.props.pickerType === 'user') {
if (this.props.groupId) {
this._doNaiveGroupSearch(query);
} else if (this.state.serverSupportsUserDirectory) {
@ -148,6 +151,15 @@ module.exports = React.createClass({
} else {
this._doLocalSearch(query);
}
} else if (this.props.pickerType === 'room') {
if (this.props.groupId) {
this._doNaiveGroupRoomSearch(query);
} else {
console.error('Room searching only implemented for groups');
}
} else {
console.error('Unknown pickerType', this.props.pickerType);
}
}, QUERY_USER_DIRECTORY_DEBOUNCE_MS);
} else {
this.setState({
@ -210,6 +222,36 @@ module.exports = React.createClass({
});
});
this._processResults(results, query);
}).catch((err) => {
console.error('Error whilst searching group rooms: ', err);
this.setState({
searchError: err.errcode ? err.message : _t('Something went wrong!'),
});
}).done(() => {
this.setState({
busy: false,
});
});
},
_doNaiveGroupRoomSearch: function(query) {
const lowerCaseQuery = query.toLowerCase();
MatrixClientPeg.get().getGroupRooms(this.props.groupId).then((resp) => {
const results = [];
resp.chunk.forEach((r) => {
const nameMatch = (r.name || '').toLowerCase().includes(lowerCaseQuery);
const topicMatch = (r.topic || '').toLowerCase().includes(lowerCaseQuery);
const aliasMatch = (r.canonical_alias || '').toLowerCase().includes(lowerCaseQuery);
if (!(nameMatch || topicMatch || aliasMatch)) {
return;
}
results.push({
room_id: r.room_id,
avatar_url: r.avatar_url,
name: r.name,
});
});
this._processResults(results, query);
}).catch((err) => {
console.error('Error whilst searching group users: ', err);
this.setState({
@ -282,17 +324,28 @@ module.exports = React.createClass({
_processResults: function(results, query) {
const queryList = [];
results.forEach((user) => {
if (user.user_id === MatrixClientPeg.get().credentials.userId) {
results.forEach((result) => {
if (result.room_id) {
queryList.push({
addressType: 'mx',
address: result.room_id,
displayName: result.name,
avatarMxc: result.avatar_url,
isKnown: true,
});
return;
}
if (result.user_id === MatrixClientPeg.get().credentials.userId) {
return;
}
// Return objects, structure of which is defined
// by UserAddressType
queryList.push({
addressType: 'mx',
address: user.user_id,
displayName: user.display_name,
avatarMxc: user.avatar_url,
address: result.user_id,
displayName: result.display_name,
avatarMxc: result.avatar_url,
isKnown: true,
});
});

View File

@ -892,5 +892,8 @@
"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:"
"Failed to add the following users to the summary of %(groupId)s:": "Failed to add the following users to the summary of %(groupId)s:",
"Add rooms to the group summary": "Add rooms to the group summary",
"Which rooms would you like to add to this summary?": "Which rooms would you like to add to this summary?",
"Room name or alias": "Room name or alias"
}