From 5f840a30319fb01ba8e79ccfd6b2e72a2fabf329 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 21 Sep 2017 16:53:10 +0100 Subject: [PATCH] Implement adding rooms to the group summary This includes making UserPickerDialog more generic such that it can also pick rooms from groups. --- src/GroupInvite.js | 4 +- src/RoomInvite.js | 8 +- src/components/structures/GroupView.js | 70 +++++++++++++---- ...PickerDialog.js => AddressPickerDialog.js} | 75 ++++++++++++++++--- src/i18n/strings/en_EN.json | 5 +- 5 files changed, 131 insertions(+), 31 deletions(-) rename src/components/views/dialogs/{UserPickerDialog.js => AddressPickerDialog.js} (85%) diff --git a/src/GroupInvite.js b/src/GroupInvite.js index c526ace188..e04e90d751 100644 --- a/src/GroupInvite.js +++ b/src/GroupInvite.js @@ -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"), diff --git a/src/RoomInvite.js b/src/RoomInvite.js index 9be3da53e4..af0ba3d1e7 100644 --- a/src/RoomInvite.js +++ b/src/RoomInvite.js @@ -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'), diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index 1b4b5cb809..04d04108e8 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -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 ? + ( + +
+ {_t('Add a Room')} +
+
) :
; + const roomNodes = this.props.rooms.map((r) => { return ; }); - let catHeader = null; + let catHeader =
; if (this.props.category && this.props.category.profile) { catHeader =
{this.props.category.profile.name}
; } return
{catHeader} {roomNodes} + {addButton}
; - // TODO: Modify UserPickerDialog to allow picking of rooms, and then use it here - // const TintableSvg = sdk.getComponent("elements.TintableSvg"); - // - // - //
- // {_t('Add a Room')} - //
- //
}, }); @@ -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({
{_t('Add a User')}
- ) : null; + ) :
; const userNodes = this.props.users.map((u) => { return ; }); - let roleHeader = null; + let roleHeader =
; if (this.props.role && this.props.role.profile) { roleHeader =
{this.props.role.profile.name}
; } @@ -456,6 +498,7 @@ export default React.createClass({ const defaultCategoryNode = ; 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}/>; }); diff --git a/src/components/views/dialogs/UserPickerDialog.js b/src/components/views/dialogs/AddressPickerDialog.js similarity index 85% rename from src/components/views/dialogs/UserPickerDialog.js rename to src/components/views/dialogs/AddressPickerDialog.js index 415a3d910e..741ff1fe8c 100644 --- a/src/components/views/dialogs/UserPickerDialog.js +++ b/src/components/views/dialogs/AddressPickerDialog.js @@ -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,12 +143,22 @@ 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.groupId) { - this._doNaiveGroupSearch(query); - } else if (this.state.serverSupportsUserDirectory) { - this._doUserDirectorySearch(query); + if (this.props.pickerType === 'user') { + if (this.props.groupId) { + this._doNaiveGroupSearch(query); + } else if (this.state.serverSupportsUserDirectory) { + this._doUserDirectorySearch(query); + } 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 { - this._doLocalSearch(query); + console.error('Unknown pickerType', this.props.pickerType); } }, QUERY_USER_DIRECTORY_DEBOUNCE_MS); } else { @@ -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, }); }); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d759547e66..088575273e 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -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" }