From 234a88c0988f40b801e288332ff75c130da57d7d Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 4 Aug 2017 15:00:34 +0100 Subject: [PATCH] WIP group member list/tiles --- .../views/dialogs/ConfirmUserActionDialog.js | 27 ++- .../views/groups/GroupMemberInfo.js | 158 ++++++++++++++++++ .../views/groups/GroupMemberList.js | 11 +- .../views/groups/GroupMemberTile.js | 16 +- src/i18n/strings/en_EN.json | 3 +- 5 files changed, 198 insertions(+), 17 deletions(-) create mode 100644 src/components/views/groups/GroupMemberInfo.js diff --git a/src/components/views/dialogs/ConfirmUserActionDialog.js b/src/components/views/dialogs/ConfirmUserActionDialog.js index b10df3ccef..c4e4f18ee0 100644 --- a/src/components/views/dialogs/ConfirmUserActionDialog.js +++ b/src/components/views/dialogs/ConfirmUserActionDialog.js @@ -18,6 +18,7 @@ import React from 'react'; import sdk from '../../../index'; import { _t } from '../../../languageHandler'; import classnames from 'classnames'; +import { GroupMemberType } from '../../../groups'; /* * A dialog for confirming an operation on another user. @@ -30,7 +31,10 @@ import classnames from 'classnames'; export default React.createClass({ displayName: 'ConfirmUserActionDialog', propTypes: { - member: React.PropTypes.object.isRequired, // matrix-js-sdk member object + // matrix-js-sdk (room) member object. Supply either this or 'groupMember' + member: React.PropTypes.object, + // group member object. Supply either this or 'member' + groupMember: GroupMemberType, action: React.PropTypes.string.isRequired, // eg. 'Ban' // Whether to display a text field for a reason @@ -69,6 +73,7 @@ export default React.createClass({ render: function() { const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const MemberAvatar = sdk.getComponent("views.avatars.MemberAvatar"); + const BaseAvatar = sdk.getComponent("views.avatars.BaseAvatar"); const title = _t("%(actionVerb)s this person?", { actionVerb: this.props.action}); const confirmButtonClass = classnames({ @@ -91,6 +96,20 @@ export default React.createClass({ ); } + let avatar; + let name; + let userId; + if (this.props.member) { + avatar = ; + name = this.props.member.name; + userId = this.props.member.userId; + } else { + // we don't get this info from the API yet + avatar = + name = this.props.groupMember.userId; + userId = this.props.groupMember.userId; + } + return (
- + {avatar}
-
{this.props.member.name}
-
{this.props.member.userId}
+
{name}
+
{userId}
{reasonBox}
diff --git a/src/components/views/groups/GroupMemberInfo.js b/src/components/views/groups/GroupMemberInfo.js new file mode 100644 index 0000000000..70aa7d4504 --- /dev/null +++ b/src/components/views/groups/GroupMemberInfo.js @@ -0,0 +1,158 @@ +/* +Copyright 2017 Vector Creations Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import PropTypes from 'prop-types'; +import React from 'react'; +import classNames from 'classnames'; +import dis from '../../../dispatcher'; +import Modal from '../../../Modal'; +import sdk from '../../../index'; +import { _t } from '../../../languageHandler'; +import createRoom from '../../../createRoom'; +import DMRoomMap from '../../../utils/DMRoomMap'; +import Unread from '../../../Unread'; +import { GroupMemberType } from '../../../groups'; +import { findReadReceiptFromUserId } from '../../../utils/Receipt'; +import withMatrixClient from '../../../wrappers/withMatrixClient'; +import AccessibleButton from '../elements/AccessibleButton'; +import GeminiScrollbar from 'react-gemini-scrollbar'; + + +module.exports = withMatrixClient(React.createClass({ + displayName: 'GroupMemberInfo', + + propTypes: { + matrixClient: PropTypes.object.isRequired, + groupId: PropTypes.string, + member: GroupMemberType, + }, + + componentWillMount: function() { + this._fetchMembers(); + }, + + _fetchMembers: function() { + this.setState({fetching: true}); + this.props.matrixClient.getGroupUsers(this.props.groupId).then((result) => { + this.setState({ + members: result.chunk, + fetching: false, + }); + }).catch((e) => { + this.setState({fetching: false}); + console.error("Failed to get group member list: " + e); + }); + }, + + _onKick: function() { + const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog"); + Modal.createDialog(ConfirmUserActionDialog, { + groupMember: this.props.member, + action: _t('Remove from group'), + danger: true, + onFinished: (proceed) => { + }, + }); + }, + + onCancel: function(e) { + dis.dispatch({ + action: "view_user", + member: null + }); + }, + + onRoomTileClick(roomId) { + dis.dispatch({ + action: 'view_room', + room_id: roomId, + }); + }, + + render: function() { + if (this.state.fetching) { + const Loader = sdk.getComponent("elements.Spinner"); + return ; + } + if (!this.state.members) return null; + + let targetIsInGroup = false; + for (const m of this.state.members) { + if (m.user_id == this.props.member.userId) { + targetIsInGroup = true; + } + } + + let kickButton, adminButton; + + if (targetIsInGroup) { + kickButton = ( + + {_t('Remove from group')} + + ); + + // No make/revoke admin API yet + /*const opLabel = this.state.isTargetMod ? _t("Revoke Moderator") : _t("Make Moderator"); + giveModButton = + {giveOpLabel} + ;*/ + } + + let adminTools; + if (kickButton || adminButton) { + adminTools = +
+

{_t("Admin tools")}

+ +
+ {kickButton} + {adminButton} +
+
; + } + + const BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); + const avatar = ( + + ); + + const memberName = this.props.member.userId; + + const EmojiText = sdk.getComponent('elements.EmojiText'); + return ( +
+ + +
+ {avatar} +
+ + {memberName} + +
+
+ { this.props.member.userId } +
+
+ + { adminTools } +
+
+ ); + } +})); diff --git a/src/components/views/groups/GroupMemberList.js b/src/components/views/groups/GroupMemberList.js index 48d49dc04b..0f74ff5eed 100644 --- a/src/components/views/groups/GroupMemberList.js +++ b/src/components/views/groups/GroupMemberList.js @@ -17,6 +17,7 @@ import React from 'react'; import { _t } from '../../../languageHandler'; import Promise from 'bluebird'; import sdk from '../../../index'; +import { groupMemberFromApiObject } from '../../../groups'; import GeminiScrollbar from 'react-gemini-scrollbar'; import PropTypes from 'prop-types'; import withMatrixClient from '../../../wrappers/withMatrixClient'; @@ -47,7 +48,9 @@ export default withMatrixClient(React.createClass({ this.setState({fetching: true}); this.props.matrixClient.getGroupUsers(this.props.groupId).then((result) => { this.setState({ - members: result.chunk, + members: result.chunk.map((apiMember) => { + return groupMemberFromApiObject(apiMember); + }), fetching: false, }); }).catch((e) => { @@ -86,7 +89,7 @@ export default withMatrixClient(React.createClass({ const memberList = this.state.members.filter((m) => { if (query) { //const matchesName = m.name.toLowerCase().indexOf(query) !== -1; - const matchesId = m.user_id.toLowerCase().indexOf(query) !== -1; + const matchesId = m.userId.toLowerCase().indexOf(query) !== -1; if (/*!matchesName &&*/ !matchesId) { return false; @@ -94,9 +97,9 @@ export default withMatrixClient(React.createClass({ } return true; - }).map(function(m) { + }).map((m) => { return ( - + ); }); diff --git a/src/components/views/groups/GroupMemberTile.js b/src/components/views/groups/GroupMemberTile.js index 1ef59fb1a6..d129fb9440 100644 --- a/src/components/views/groups/GroupMemberTile.js +++ b/src/components/views/groups/GroupMemberTile.js @@ -19,6 +19,7 @@ import PropTypes from 'prop-types'; import sdk from '../../../index'; import dis from '../../../dispatcher'; import { _t } from '../../../languageHandler'; +import { GroupMemberType } from '../../../groups'; import withMatrixClient from '../../../wrappers/withMatrixClient'; import Matrix from "matrix-js-sdk"; @@ -27,9 +28,8 @@ export default withMatrixClient(React.createClass({ propTypes: { matrixClient: PropTypes.object, - member: PropTypes.shape({ - user_id: PropTypes.string.isRequired, - }).isRequired, + groupId: PropTypes.string.isRequired, + member: GroupMemberType.isRequired, }, getInitialState: function() { @@ -37,10 +37,10 @@ export default withMatrixClient(React.createClass({ }, onClick: function(e) { - const member = new Matrix.RoomMember(null, this.props.member.user_id); dis.dispatch({ - action: 'view_user', - member: member, + action: 'view_group_user', + member: this.props.member, + groupId: this.props.groupId, }); }, @@ -52,10 +52,10 @@ export default withMatrixClient(React.createClass({ const BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); const EntityTile = sdk.getComponent('rooms.EntityTile'); - const name = this.props.member.user_id; + const name = this.props.member.userId; const av = ( - + ); return ( diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a12d277a92..82e10bf3d1 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -963,5 +963,6 @@ "Failed to upload image": "Failed to upload image", "Failed to update group": "Failed to update group", "Description": "Description", - "Filter group members": "Filter group members" + "Filter group members": "Filter group members", + "Remove from group": "Remove from group" }