2017-06-05 15:51:50 +00:00
|
|
|
/*
|
|
|
|
Copyright 2017 Vector Creations Ltd.
|
2017-08-21 18:18:32 +00:00
|
|
|
Copyright 2017 New Vector Ltd.
|
2017-06-05 15:51:50 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2017-06-27 08:58:29 +00:00
|
|
|
import React from 'react';
|
2017-06-29 16:51:38 +00:00
|
|
|
import PropTypes from 'prop-types';
|
2017-09-21 11:34:16 +00:00
|
|
|
import Promise from 'bluebird';
|
2017-06-05 15:51:50 +00:00
|
|
|
import MatrixClientPeg from '../../MatrixClientPeg';
|
|
|
|
import sdk from '../../index';
|
2017-07-06 18:13:14 +00:00
|
|
|
import dis from '../../dispatcher';
|
2017-06-23 16:02:54 +00:00
|
|
|
import { sanitizedHtmlNode } from '../../HtmlUtils';
|
2017-06-26 16:47:17 +00:00
|
|
|
import { _t } from '../../languageHandler';
|
2017-07-06 18:13:14 +00:00
|
|
|
import AccessibleButton from '../views/elements/AccessibleButton';
|
2017-07-21 13:03:10 +00:00
|
|
|
import Modal from '../../Modal';
|
2017-07-21 13:13:57 +00:00
|
|
|
import classnames from 'classnames';
|
2017-06-05 15:51:50 +00:00
|
|
|
|
2017-10-04 15:56:35 +00:00
|
|
|
import GroupStoreCache from '../../stores/GroupStoreCache';
|
|
|
|
import GroupStore from '../../stores/GroupStore';
|
2017-10-13 15:46:33 +00:00
|
|
|
import { showGroupAddRoomDialog } from '../../GroupAddressPicker';
|
|
|
|
|
2017-09-22 17:52:06 +00:00
|
|
|
|
2017-07-11 13:28:44 +00:00
|
|
|
const RoomSummaryType = PropTypes.shape({
|
|
|
|
room_id: PropTypes.string.isRequired,
|
|
|
|
profile: PropTypes.shape({
|
|
|
|
name: PropTypes.string,
|
|
|
|
avatar_url: PropTypes.string,
|
|
|
|
canonical_alias: PropTypes.string,
|
2017-07-11 14:16:58 +00:00
|
|
|
}).isRequired,
|
2017-07-11 13:28:44 +00:00
|
|
|
});
|
2017-06-05 15:51:50 +00:00
|
|
|
|
2017-07-11 13:28:44 +00:00
|
|
|
const UserSummaryType = PropTypes.shape({
|
|
|
|
summaryInfo: PropTypes.shape({
|
|
|
|
user_id: PropTypes.string.isRequired,
|
2017-09-20 15:54:12 +00:00
|
|
|
role_id: PropTypes.string,
|
|
|
|
avatar_url: PropTypes.string,
|
|
|
|
displayname: PropTypes.string,
|
2017-07-11 13:28:44 +00:00
|
|
|
}).isRequired,
|
|
|
|
});
|
2017-07-06 18:13:14 +00:00
|
|
|
|
2017-07-11 13:28:44 +00:00
|
|
|
const CategoryRoomList = React.createClass({
|
|
|
|
displayName: 'CategoryRoomList',
|
2017-07-06 18:13:14 +00:00
|
|
|
|
|
|
|
props: {
|
2017-07-11 14:16:58 +00:00
|
|
|
rooms: PropTypes.arrayOf(RoomSummaryType).isRequired,
|
2017-07-11 13:28:44 +00:00
|
|
|
category: PropTypes.shape({
|
2017-07-11 12:41:00 +00:00
|
|
|
profile: PropTypes.shape({
|
|
|
|
name: PropTypes.string,
|
|
|
|
}).isRequired,
|
2017-07-11 13:28:44 +00:00
|
|
|
}),
|
2017-09-21 15:53:10 +00:00
|
|
|
groupId: PropTypes.string.isRequired,
|
2017-09-21 11:44:17 +00:00
|
|
|
|
|
|
|
// Whether the list should be editable
|
|
|
|
editing: PropTypes.bool.isRequired,
|
2017-07-11 13:28:44 +00:00
|
|
|
},
|
|
|
|
|
2017-10-13 15:46:33 +00:00
|
|
|
onAddRoomsToSummaryClicked: function(ev) {
|
2017-09-21 15:53:10 +00:00
|
|
|
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',
|
2017-09-27 14:30:58 +00:00
|
|
|
validAddressTypes: ['mx-room-id'],
|
2017-09-21 15:53:10 +00:00
|
|
|
groupId: this.props.groupId,
|
|
|
|
onFinished: (success, addrs) => {
|
|
|
|
if (!success) return;
|
|
|
|
const errorList = [];
|
|
|
|
Promise.all(addrs.map((addr) => {
|
2017-10-04 15:56:35 +00:00
|
|
|
return this.context.groupStore
|
2017-09-22 17:52:06 +00:00
|
|
|
.addRoomToGroupSummary(addr.address)
|
2017-09-21 15:53:10 +00:00
|
|
|
.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(", "),
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2017-07-11 13:28:44 +00:00
|
|
|
render: function() {
|
2017-09-21 15:53:10 +00:00
|
|
|
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
|
|
|
const addButton = this.props.editing ?
|
2017-10-13 15:46:33 +00:00
|
|
|
(<AccessibleButton className="mx_GroupView_featuredThings_addButton" onClick={this.onAddRoomsToSummaryClicked}>
|
2017-09-28 10:21:06 +00:00
|
|
|
<TintableSvg src="img/icons-create-room.svg" width="64" height="64" />
|
2017-09-21 15:53:10 +00:00
|
|
|
<div className="mx_GroupView_featuredThings_addButton_label">
|
2017-09-28 10:21:06 +00:00
|
|
|
{ _t('Add a Room') }
|
2017-09-21 15:53:10 +00:00
|
|
|
</div>
|
|
|
|
</AccessibleButton>) : <div />;
|
|
|
|
|
2017-07-11 13:28:44 +00:00
|
|
|
const roomNodes = this.props.rooms.map((r) => {
|
2017-09-22 14:55:42 +00:00
|
|
|
return <FeaturedRoom
|
|
|
|
key={r.room_id}
|
|
|
|
groupId={this.props.groupId}
|
|
|
|
editing={this.props.editing}
|
2017-09-28 10:21:06 +00:00
|
|
|
summaryInfo={r} />;
|
2017-07-11 13:28:44 +00:00
|
|
|
});
|
2017-09-20 13:41:29 +00:00
|
|
|
|
2017-09-21 15:53:10 +00:00
|
|
|
let catHeader = <div />;
|
2017-07-11 13:28:44 +00:00
|
|
|
if (this.props.category && this.props.category.profile) {
|
2017-09-28 10:21:06 +00:00
|
|
|
catHeader = <div className="mx_GroupView_featuredThings_category">
|
|
|
|
{ this.props.category.profile.name }
|
|
|
|
</div>;
|
2017-07-11 13:28:44 +00:00
|
|
|
}
|
2017-09-20 13:41:29 +00:00
|
|
|
return <div className="mx_GroupView_featuredThings_container">
|
2017-09-28 10:21:06 +00:00
|
|
|
{ catHeader }
|
|
|
|
{ roomNodes }
|
|
|
|
{ addButton }
|
2017-07-11 13:28:44 +00:00
|
|
|
</div>;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const FeaturedRoom = React.createClass({
|
|
|
|
displayName: 'FeaturedRoom',
|
|
|
|
|
|
|
|
props: {
|
|
|
|
summaryInfo: RoomSummaryType.isRequired,
|
2017-09-22 14:55:42 +00:00
|
|
|
editing: PropTypes.bool.isRequired,
|
|
|
|
groupId: PropTypes.string.isRequired,
|
2017-07-06 18:13:14 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
onClick: function(e) {
|
|
|
|
e.preventDefault();
|
2017-07-10 18:32:02 +00:00
|
|
|
e.stopPropagation();
|
2017-07-06 18:13:14 +00:00
|
|
|
|
|
|
|
dis.dispatch({
|
|
|
|
action: 'view_room',
|
|
|
|
room_alias: this.props.summaryInfo.profile.canonical_alias,
|
|
|
|
room_id: this.props.summaryInfo.room_id,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2017-09-22 14:55:42 +00:00
|
|
|
onDeleteClicked: function(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
2017-10-04 15:56:35 +00:00
|
|
|
this.context.groupStore.removeRoomFromGroupSummary(
|
2017-09-22 14:55:42 +00:00
|
|
|
this.props.summaryInfo.room_id,
|
|
|
|
).catch((err) => {
|
|
|
|
console.error('Error whilst removing room from group summary', err);
|
|
|
|
const roomName = this.props.summaryInfo.name ||
|
|
|
|
this.props.summaryInfo.canonical_alias ||
|
|
|
|
this.props.summaryInfo.room_id;
|
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
|
|
Modal.createTrackedDialog(
|
|
|
|
'Failed to remove room from group summary',
|
|
|
|
'', ErrorDialog,
|
|
|
|
{
|
|
|
|
title: _t(
|
|
|
|
"Failed to remove the room from the summary of %(groupId)s",
|
|
|
|
{groupId: this.props.groupId},
|
|
|
|
),
|
|
|
|
description: _t("The room '%(roomName)s' could not be removed from the summary.", {roomName}),
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2017-07-06 18:13:14 +00:00
|
|
|
render: function() {
|
|
|
|
const RoomAvatar = sdk.getComponent("avatars.RoomAvatar");
|
|
|
|
|
2017-09-27 15:18:15 +00:00
|
|
|
const roomName = this.props.summaryInfo.profile.name ||
|
|
|
|
this.props.summaryInfo.profile.canonical_alias ||
|
|
|
|
_t("Unnamed Room");
|
|
|
|
|
2017-07-06 18:13:14 +00:00
|
|
|
const oobData = {
|
|
|
|
roomId: this.props.summaryInfo.room_id,
|
|
|
|
avatarUrl: this.props.summaryInfo.profile.avatar_url,
|
2017-09-27 15:18:15 +00:00
|
|
|
name: roomName,
|
2017-07-06 18:13:14 +00:00
|
|
|
};
|
2017-09-27 15:18:15 +00:00
|
|
|
|
2017-07-06 18:13:14 +00:00
|
|
|
let permalink = null;
|
|
|
|
if (this.props.summaryInfo.profile && this.props.summaryInfo.profile.canonical_alias) {
|
|
|
|
permalink = 'https://matrix.to/#/' + this.props.summaryInfo.profile.canonical_alias;
|
|
|
|
}
|
2017-09-27 15:18:15 +00:00
|
|
|
|
2017-07-06 18:13:14 +00:00
|
|
|
let roomNameNode = null;
|
|
|
|
if (permalink) {
|
2017-09-28 10:21:06 +00:00
|
|
|
roomNameNode = <a href={permalink} onClick={this.onClick} >{ roomName }</a>;
|
2017-07-06 18:13:14 +00:00
|
|
|
} else {
|
2017-09-28 10:21:06 +00:00
|
|
|
roomNameNode = <span>{ roomName }</span>;
|
2017-07-06 18:13:14 +00:00
|
|
|
}
|
|
|
|
|
2017-09-22 14:55:42 +00:00
|
|
|
const deleteButton = this.props.editing ?
|
|
|
|
<img
|
|
|
|
className="mx_GroupView_featuredThing_deleteButton"
|
|
|
|
src="img/cancel-small.svg"
|
|
|
|
width="14"
|
|
|
|
height="14"
|
|
|
|
alt="Delete"
|
2017-09-28 10:21:06 +00:00
|
|
|
onClick={this.onDeleteClicked} />
|
2017-09-22 14:55:42 +00:00
|
|
|
: <div />;
|
|
|
|
|
2017-07-10 18:32:02 +00:00
|
|
|
return <AccessibleButton className="mx_GroupView_featuredThing" onClick={this.onClick}>
|
2017-07-06 18:13:14 +00:00
|
|
|
<RoomAvatar oobData={oobData} width={64} height={64} />
|
2017-09-28 10:21:06 +00:00
|
|
|
<div className="mx_GroupView_featuredThing_name">{ roomNameNode }</div>
|
|
|
|
{ deleteButton }
|
2017-07-10 18:32:02 +00:00
|
|
|
</AccessibleButton>;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2017-07-11 13:28:44 +00:00
|
|
|
const RoleUserList = React.createClass({
|
|
|
|
displayName: 'RoleUserList',
|
|
|
|
|
|
|
|
props: {
|
|
|
|
users: PropTypes.arrayOf(UserSummaryType).isRequired,
|
|
|
|
role: PropTypes.shape({
|
|
|
|
profile: PropTypes.shape({
|
|
|
|
name: PropTypes.string,
|
|
|
|
}).isRequired,
|
|
|
|
}),
|
2017-09-20 14:29:31 +00:00
|
|
|
groupId: PropTypes.string.isRequired,
|
2017-09-21 11:44:17 +00:00
|
|
|
|
|
|
|
// Whether the list should be editable
|
|
|
|
editing: PropTypes.bool.isRequired,
|
2017-09-20 14:29:31 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
onAddUsersClicked: function(ev) {
|
|
|
|
ev.preventDefault();
|
2017-09-21 15:53:10 +00:00
|
|
|
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
|
|
|
|
Modal.createTrackedDialog('Add Users to Group Summary', '', AddressPickerDialog, {
|
2017-09-20 14:29:31 +00:00
|
|
|
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"),
|
2017-09-27 14:30:58 +00:00
|
|
|
validAddressTypes: ['mx-user-id'],
|
2017-09-20 14:29:31 +00:00
|
|
|
groupId: this.props.groupId,
|
2017-09-27 10:04:41 +00:00
|
|
|
shouldOmitSelf: false,
|
2017-09-20 14:29:31 +00:00
|
|
|
onFinished: (success, addrs) => {
|
|
|
|
if (!success) return;
|
2017-09-21 11:34:16 +00:00
|
|
|
const errorList = [];
|
|
|
|
Promise.all(addrs.map((addr) => {
|
2017-10-04 15:56:35 +00:00
|
|
|
return this.context.groupStore
|
2017-09-22 17:52:06 +00:00
|
|
|
.addUserToGroupSummary(addr.address)
|
2017-09-21 11:34:16 +00:00
|
|
|
.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(", "),
|
|
|
|
});
|
2017-09-20 15:32:02 +00:00
|
|
|
});
|
2017-09-20 14:29:31 +00:00
|
|
|
},
|
|
|
|
});
|
2017-07-11 13:28:44 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
render: function() {
|
2017-09-20 13:41:29 +00:00
|
|
|
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
2017-09-21 11:44:17 +00:00
|
|
|
const addButton = this.props.editing ?
|
|
|
|
(<AccessibleButton className="mx_GroupView_featuredThings_addButton" onClick={this.onAddUsersClicked}>
|
2017-09-28 10:21:06 +00:00
|
|
|
<TintableSvg src="img/icons-create-room.svg" width="64" height="64" />
|
2017-09-21 11:44:17 +00:00
|
|
|
<div className="mx_GroupView_featuredThings_addButton_label">
|
2017-09-28 10:21:06 +00:00
|
|
|
{ _t('Add a User') }
|
2017-09-21 11:44:17 +00:00
|
|
|
</div>
|
2017-09-21 15:53:10 +00:00
|
|
|
</AccessibleButton>) : <div />;
|
2017-07-11 13:28:44 +00:00
|
|
|
const userNodes = this.props.users.map((u) => {
|
2017-09-22 14:55:42 +00:00
|
|
|
return <FeaturedUser
|
|
|
|
key={u.user_id}
|
|
|
|
summaryInfo={u}
|
|
|
|
editing={this.props.editing}
|
2017-09-28 10:21:06 +00:00
|
|
|
groupId={this.props.groupId} />;
|
2017-07-11 13:28:44 +00:00
|
|
|
});
|
2017-09-21 15:53:10 +00:00
|
|
|
let roleHeader = <div />;
|
2017-07-11 13:28:44 +00:00
|
|
|
if (this.props.role && this.props.role.profile) {
|
2017-09-28 10:21:06 +00:00
|
|
|
roleHeader = <div className="mx_GroupView_featuredThings_category">{ this.props.role.profile.name }</div>;
|
2017-07-11 13:28:44 +00:00
|
|
|
}
|
2017-09-20 13:41:29 +00:00
|
|
|
return <div className="mx_GroupView_featuredThings_container">
|
2017-09-28 10:21:06 +00:00
|
|
|
{ roleHeader }
|
|
|
|
{ userNodes }
|
|
|
|
{ addButton }
|
2017-07-11 13:28:44 +00:00
|
|
|
</div>;
|
|
|
|
},
|
|
|
|
});
|
2017-07-10 18:32:02 +00:00
|
|
|
|
|
|
|
const FeaturedUser = React.createClass({
|
|
|
|
displayName: 'FeaturedUser',
|
|
|
|
|
|
|
|
props: {
|
2017-07-11 13:28:44 +00:00
|
|
|
summaryInfo: UserSummaryType.isRequired,
|
2017-09-22 14:55:42 +00:00
|
|
|
editing: PropTypes.bool.isRequired,
|
|
|
|
groupId: PropTypes.string.isRequired,
|
2017-07-10 18:32:02 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
onClick: function(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
|
|
|
|
dis.dispatch({
|
|
|
|
action: 'view_start_chat_or_reuse',
|
|
|
|
user_id: this.props.summaryInfo.user_id,
|
|
|
|
go_home_on_cancel: false,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2017-09-22 14:55:42 +00:00
|
|
|
onDeleteClicked: function(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
2017-10-04 15:56:35 +00:00
|
|
|
this.context.groupStore.removeUserFromGroupSummary(
|
2017-09-22 14:55:42 +00:00
|
|
|
this.props.summaryInfo.user_id,
|
|
|
|
).catch((err) => {
|
|
|
|
console.error('Error whilst removing user from group summary', err);
|
|
|
|
const displayName = this.props.summaryInfo.displayname || this.props.summaryInfo.user_id;
|
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
|
|
Modal.createTrackedDialog(
|
|
|
|
'Failed to remove user from group summary',
|
|
|
|
'', ErrorDialog,
|
|
|
|
{
|
|
|
|
title: _t(
|
|
|
|
"Failed to remove a user from the summary of %(groupId)s",
|
|
|
|
{groupId: this.props.groupId},
|
|
|
|
),
|
|
|
|
description: _t("The user '%(displayName)s' could not be removed from the summary.", {displayName}),
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2017-07-10 18:32:02 +00:00
|
|
|
render: function() {
|
2017-09-20 15:54:12 +00:00
|
|
|
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
2017-09-21 09:34:11 +00:00
|
|
|
const name = this.props.summaryInfo.displayname || this.props.summaryInfo.user_id;
|
2017-07-10 18:32:02 +00:00
|
|
|
|
|
|
|
const permalink = 'https://matrix.to/#/' + this.props.summaryInfo.user_id;
|
2017-09-28 10:21:06 +00:00
|
|
|
const userNameNode = <a href={permalink} onClick={this.onClick}>{ name }</a>;
|
2017-09-20 15:54:12 +00:00
|
|
|
const httpUrl = MatrixClientPeg.get()
|
|
|
|
.mxcUrlToHttp(this.props.summaryInfo.avatar_url, 64, 64);
|
2017-07-10 18:32:02 +00:00
|
|
|
|
2017-09-22 14:55:42 +00:00
|
|
|
const deleteButton = this.props.editing ?
|
|
|
|
<img
|
|
|
|
className="mx_GroupView_featuredThing_deleteButton"
|
|
|
|
src="img/cancel-small.svg"
|
|
|
|
width="14"
|
|
|
|
height="14"
|
|
|
|
alt="Delete"
|
2017-09-28 10:21:06 +00:00
|
|
|
onClick={this.onDeleteClicked} />
|
2017-09-22 14:55:42 +00:00
|
|
|
: <div />;
|
|
|
|
|
2017-07-10 18:32:02 +00:00
|
|
|
return <AccessibleButton className="mx_GroupView_featuredThing" onClick={this.onClick}>
|
2017-09-20 15:54:12 +00:00
|
|
|
<BaseAvatar name={name} url={httpUrl} width={64} height={64} />
|
2017-09-28 10:21:06 +00:00
|
|
|
<div className="mx_GroupView_featuredThing_name">{ userNameNode }</div>
|
|
|
|
{ deleteButton }
|
2017-07-06 18:13:14 +00:00
|
|
|
</AccessibleButton>;
|
2017-07-07 12:46:05 +00:00
|
|
|
},
|
2017-07-06 18:13:14 +00:00
|
|
|
});
|
|
|
|
|
2017-10-04 15:56:35 +00:00
|
|
|
const GroupContext = {
|
|
|
|
groupStore: React.PropTypes.instanceOf(GroupStore).isRequired,
|
2017-09-22 17:52:06 +00:00
|
|
|
};
|
|
|
|
|
2017-10-04 15:56:35 +00:00
|
|
|
CategoryRoomList.contextTypes = GroupContext;
|
|
|
|
FeaturedRoom.contextTypes = GroupContext;
|
|
|
|
RoleUserList.contextTypes = GroupContext;
|
|
|
|
FeaturedUser.contextTypes = GroupContext;
|
2017-09-22 17:52:06 +00:00
|
|
|
|
2017-06-29 16:51:38 +00:00
|
|
|
export default React.createClass({
|
2017-06-05 15:51:50 +00:00
|
|
|
displayName: 'GroupView',
|
|
|
|
|
|
|
|
propTypes: {
|
2017-06-29 16:51:38 +00:00
|
|
|
groupId: PropTypes.string.isRequired,
|
2017-06-05 15:51:50 +00:00
|
|
|
},
|
|
|
|
|
2017-09-22 17:52:06 +00:00
|
|
|
childContextTypes: {
|
2017-10-04 15:56:35 +00:00
|
|
|
groupStore: React.PropTypes.instanceOf(GroupStore),
|
2017-09-22 17:52:06 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
getChildContext: function() {
|
|
|
|
return {
|
2017-10-04 15:56:35 +00:00
|
|
|
groupStore: this._groupStore,
|
2017-09-22 17:52:06 +00:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2017-06-05 15:51:50 +00:00
|
|
|
getInitialState: function() {
|
|
|
|
return {
|
|
|
|
summary: null,
|
2017-06-26 16:47:17 +00:00
|
|
|
error: null,
|
2017-07-06 18:13:14 +00:00
|
|
|
editing: false,
|
2017-07-21 10:12:15 +00:00
|
|
|
saving: false,
|
2017-07-21 13:03:10 +00:00
|
|
|
uploadingAvatar: false,
|
2017-08-21 18:18:32 +00:00
|
|
|
membershipBusy: false,
|
2017-09-22 18:27:02 +00:00
|
|
|
publicityBusy: false,
|
2017-06-05 15:51:50 +00:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
componentWillMount: function() {
|
2017-07-21 10:12:15 +00:00
|
|
|
this._changeAvatarComponent = null;
|
2017-10-04 15:56:35 +00:00
|
|
|
this._initGroupStore(this.props.groupId);
|
2017-08-21 18:18:32 +00:00
|
|
|
|
|
|
|
MatrixClientPeg.get().on("Group.myMembership", this._onGroupMyMembership);
|
|
|
|
},
|
|
|
|
|
|
|
|
componentWillUnmount: function() {
|
|
|
|
MatrixClientPeg.get().removeListener("Group.myMembership", this._onGroupMyMembership);
|
2017-10-04 15:56:35 +00:00
|
|
|
this._groupStore.removeAllListeners();
|
2017-06-05 15:51:50 +00:00
|
|
|
},
|
|
|
|
|
2017-06-27 12:13:00 +00:00
|
|
|
componentWillReceiveProps: function(newProps) {
|
|
|
|
if (this.props.groupId != newProps.groupId) {
|
2017-06-05 15:51:50 +00:00
|
|
|
this.setState({
|
|
|
|
summary: null,
|
2017-06-27 09:28:46 +00:00
|
|
|
error: null,
|
2017-07-07 09:08:29 +00:00
|
|
|
}, () => {
|
2017-10-04 15:56:35 +00:00
|
|
|
this._initGroupStore(newProps.groupId);
|
2017-06-27 12:13:00 +00:00
|
|
|
});
|
2017-06-05 15:51:50 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2017-08-21 18:18:32 +00:00
|
|
|
_onGroupMyMembership: function(group) {
|
|
|
|
if (group.groupId !== this.props.groupId) return;
|
|
|
|
|
|
|
|
this.setState({membershipBusy: false});
|
|
|
|
},
|
|
|
|
|
2017-10-04 15:56:35 +00:00
|
|
|
_initGroupStore: function(groupId) {
|
|
|
|
this._groupStore = GroupStoreCache.getGroupStore(MatrixClientPeg.get(), groupId);
|
|
|
|
this._groupStore.on('update', () => {
|
2017-06-23 16:02:54 +00:00
|
|
|
this.setState({
|
2017-10-04 15:56:35 +00:00
|
|
|
summary: this._groupStore.getSummary(),
|
2017-06-27 09:28:46 +00:00
|
|
|
error: null,
|
2017-06-05 15:51:50 +00:00
|
|
|
});
|
2017-09-22 17:52:06 +00:00
|
|
|
});
|
2017-10-04 15:56:35 +00:00
|
|
|
this._groupStore.on('error', (err) => {
|
2017-10-13 15:46:33 +00:00
|
|
|
console.error(err);
|
2017-06-23 16:02:54 +00:00
|
|
|
this.setState({
|
2017-06-05 15:51:50 +00:00
|
|
|
summary: null,
|
2017-06-26 16:47:17 +00:00
|
|
|
error: err,
|
2017-06-05 15:51:50 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2017-08-15 12:12:39 +00:00
|
|
|
_onShowRhsClick: function(ev) {
|
|
|
|
dis.dispatch({ action: 'show_right_panel' });
|
|
|
|
},
|
|
|
|
|
2017-07-17 16:17:18 +00:00
|
|
|
_onEditClick: function() {
|
2017-07-13 17:41:51 +00:00
|
|
|
this.setState({
|
|
|
|
editing: true,
|
|
|
|
profileForm: Object.assign({}, this.state.summary.profile),
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
_onCancelClick: function() {
|
|
|
|
this.setState({
|
|
|
|
editing: false,
|
|
|
|
profileForm: null,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2017-07-17 13:40:38 +00:00
|
|
|
_onNameChange: function(e) {
|
|
|
|
const newProfileForm = Object.assign(this.state.profileForm, { name: e.target.value });
|
|
|
|
this.setState({
|
|
|
|
profileForm: newProfileForm,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
_onShortDescChange: function(e) {
|
|
|
|
const newProfileForm = Object.assign(this.state.profileForm, { short_description: e.target.value });
|
|
|
|
this.setState({
|
|
|
|
profileForm: newProfileForm,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
_onLongDescChange: function(e) {
|
|
|
|
const newProfileForm = Object.assign(this.state.profileForm, { long_description: e.target.value });
|
|
|
|
this.setState({
|
|
|
|
profileForm: newProfileForm,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2017-07-21 13:03:10 +00:00
|
|
|
_onAvatarSelected: function(ev) {
|
|
|
|
const file = ev.target.files[0];
|
|
|
|
if (!file) return;
|
2017-07-21 10:12:15 +00:00
|
|
|
|
2017-07-21 13:03:10 +00:00
|
|
|
this.setState({uploadingAvatar: true});
|
|
|
|
MatrixClientPeg.get().uploadContent(file).then((url) => {
|
|
|
|
const newProfileForm = Object.assign(this.state.profileForm, { avatar_url: url });
|
|
|
|
this.setState({
|
|
|
|
uploadingAvatar: false,
|
|
|
|
profileForm: newProfileForm,
|
|
|
|
});
|
|
|
|
}).catch((e) => {
|
|
|
|
this.setState({uploadingAvatar: false});
|
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
|
|
console.error("Failed to upload avatar image", e);
|
2017-08-10 14:17:52 +00:00
|
|
|
Modal.createTrackedDialog('Failed to upload image', '', ErrorDialog, {
|
2017-07-21 13:03:10 +00:00
|
|
|
title: _t('Error'),
|
|
|
|
description: _t('Failed to upload image'),
|
|
|
|
});
|
|
|
|
}).done();
|
2017-07-21 10:12:15 +00:00
|
|
|
},
|
|
|
|
|
2017-07-13 17:41:51 +00:00
|
|
|
_onSaveClick: function() {
|
2017-07-21 10:12:15 +00:00
|
|
|
this.setState({saving: true});
|
|
|
|
MatrixClientPeg.get().setGroupProfile(this.props.groupId, this.state.profileForm).then((result) => {
|
|
|
|
this.setState({
|
|
|
|
saving: false,
|
|
|
|
editing: false,
|
2017-07-21 13:18:28 +00:00
|
|
|
summary: null,
|
2017-07-21 10:12:15 +00:00
|
|
|
});
|
2017-10-04 15:56:35 +00:00
|
|
|
this._initGroupStore(this.props.groupId);
|
2017-07-21 10:12:15 +00:00
|
|
|
}).catch((e) => {
|
|
|
|
this.setState({
|
|
|
|
saving: false,
|
|
|
|
});
|
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
|
|
console.error("Failed to save group profile", e);
|
2017-08-10 14:17:52 +00:00
|
|
|
Modal.createTrackedDialog('Failed to update group', '', ErrorDialog, {
|
2017-07-21 10:12:15 +00:00
|
|
|
title: _t('Error'),
|
|
|
|
description: _t('Failed to update group'),
|
|
|
|
});
|
|
|
|
}).done();
|
2017-07-06 18:13:14 +00:00
|
|
|
},
|
|
|
|
|
2017-08-21 18:18:32 +00:00
|
|
|
_onAcceptInviteClick: function() {
|
|
|
|
this.setState({membershipBusy: true});
|
|
|
|
MatrixClientPeg.get().acceptGroupInvite(this.props.groupId).then(() => {
|
|
|
|
// don't reset membershipBusy here: wait for the membership change to come down the sync
|
|
|
|
}).catch((e) => {
|
|
|
|
this.setState({membershipBusy: false});
|
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
|
|
Modal.createTrackedDialog('Error accepting invite', '', ErrorDialog, {
|
|
|
|
title: _t("Error"),
|
|
|
|
description: _t("Unable to accept invite"),
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
_onRejectInviteClick: function() {
|
|
|
|
this.setState({membershipBusy: true});
|
|
|
|
MatrixClientPeg.get().leaveGroup(this.props.groupId).then(() => {
|
|
|
|
// don't reset membershipBusy here: wait for the membership change to come down the sync
|
|
|
|
}).catch((e) => {
|
|
|
|
this.setState({membershipBusy: false});
|
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
|
|
Modal.createTrackedDialog('Error rejecting invite', '', ErrorDialog, {
|
|
|
|
title: _t("Error"),
|
|
|
|
description: _t("Unable to reject invite"),
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
_onLeaveClick: function() {
|
|
|
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
|
|
|
Modal.createTrackedDialog('Leave Group', '', QuestionDialog, {
|
|
|
|
title: _t("Leave Group"),
|
|
|
|
description: _t("Leave %(groupName)s?", {groupName: this.props.groupId}),
|
|
|
|
button: _t("Leave"),
|
|
|
|
danger: true,
|
|
|
|
onFinished: (confirmed) => {
|
|
|
|
if (!confirmed) return;
|
|
|
|
|
|
|
|
this.setState({membershipBusy: true});
|
|
|
|
MatrixClientPeg.get().leaveGroup(this.props.groupId).then(() => {
|
|
|
|
// don't reset membershipBusy here: wait for the membership change to come down the sync
|
|
|
|
}).catch((e) => {
|
|
|
|
this.setState({membershipBusy: false});
|
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
|
|
Modal.createTrackedDialog('Error leaving room', '', ErrorDialog, {
|
|
|
|
title: _t("Error"),
|
|
|
|
description: _t("Unable to leave room"),
|
|
|
|
});
|
|
|
|
});
|
2017-08-21 18:34:07 +00:00
|
|
|
},
|
2017-08-21 18:18:32 +00:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2017-09-22 18:27:02 +00:00
|
|
|
_onPubliciseOffClick: function() {
|
|
|
|
this._setPublicity(false);
|
|
|
|
},
|
|
|
|
|
|
|
|
_onPubliciseOnClick: function() {
|
|
|
|
this._setPublicity(true);
|
|
|
|
},
|
|
|
|
|
2017-10-13 15:46:33 +00:00
|
|
|
_onAddRoomsClick: function() {
|
|
|
|
showGroupAddRoomDialog(this.props.groupId);
|
|
|
|
},
|
|
|
|
|
2017-09-22 18:27:02 +00:00
|
|
|
_setPublicity: function(publicity) {
|
|
|
|
this.setState({
|
|
|
|
publicityBusy: true,
|
|
|
|
});
|
2017-10-04 15:56:35 +00:00
|
|
|
this._groupStore.setGroupPublicity(publicity).then(() => {
|
2017-09-22 18:27:02 +00:00
|
|
|
this.setState({
|
|
|
|
publicityBusy: false,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2017-10-13 15:46:33 +00:00
|
|
|
_getRoomsNode: function() {
|
|
|
|
const RoomDetailList = sdk.getComponent('rooms.RoomDetailList');
|
|
|
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
|
|
|
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
|
|
|
const addButton = this.state.editing ?
|
|
|
|
(<AccessibleButton onClick={this._onAddRoomsClick} >
|
|
|
|
<div className="mx_GroupView_rooms_header_addButton" >
|
|
|
|
<TintableSvg src="img/icons-room-add.svg" width="24" height="24" />
|
|
|
|
</div>
|
|
|
|
<div className="mx_GroupView_rooms_header_addButton_label">
|
|
|
|
{ _t('Add rooms to this group') }
|
|
|
|
</div>
|
|
|
|
</AccessibleButton>) : <div />;
|
|
|
|
return <div className="mx_GroupView_rooms">
|
|
|
|
<div className="mx_GroupView_rooms_header">
|
|
|
|
<h3>Rooms</h3>
|
|
|
|
{ addButton }
|
|
|
|
</div>
|
|
|
|
<RoomDetailList rooms={this._groupStore.getGroupRooms()} />
|
|
|
|
</div>;
|
|
|
|
},
|
|
|
|
|
2017-09-22 18:27:02 +00:00
|
|
|
_getFeaturedRoomsNode: function() {
|
2017-07-10 18:32:02 +00:00
|
|
|
const summary = this.state.summary;
|
|
|
|
|
|
|
|
const defaultCategoryRooms = [];
|
|
|
|
const categoryRooms = {};
|
|
|
|
summary.rooms_section.rooms.forEach((r) => {
|
|
|
|
if (r.category_id === null) {
|
|
|
|
defaultCategoryRooms.push(r);
|
|
|
|
} else {
|
|
|
|
let list = categoryRooms[r.category_id];
|
|
|
|
if (list === undefined) {
|
|
|
|
list = [];
|
|
|
|
categoryRooms[r.category_id] = list;
|
|
|
|
}
|
|
|
|
list.push(r);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2017-09-21 11:44:17 +00:00
|
|
|
const defaultCategoryNode = <CategoryRoomList
|
|
|
|
rooms={defaultCategoryRooms}
|
2017-09-21 15:53:10 +00:00
|
|
|
groupId={this.props.groupId}
|
2017-09-28 10:21:06 +00:00
|
|
|
editing={this.state.editing} />;
|
2017-07-10 18:32:02 +00:00
|
|
|
const categoryRoomNodes = Object.keys(categoryRooms).map((catId) => {
|
|
|
|
const cat = summary.rooms_section.categories[catId];
|
2017-09-21 11:44:17 +00:00
|
|
|
return <CategoryRoomList
|
|
|
|
key={catId}
|
|
|
|
rooms={categoryRooms[catId]}
|
|
|
|
category={cat}
|
2017-09-21 15:53:10 +00:00
|
|
|
groupId={this.props.groupId}
|
2017-09-28 10:21:06 +00:00
|
|
|
editing={this.state.editing} />;
|
2017-07-10 18:32:02 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
return <div className="mx_GroupView_featuredThings">
|
|
|
|
<div className="mx_GroupView_featuredThings_header">
|
2017-09-28 10:21:06 +00:00
|
|
|
{ _t('Featured Rooms:') }
|
2017-07-10 18:32:02 +00:00
|
|
|
</div>
|
2017-09-28 10:21:06 +00:00
|
|
|
{ defaultCategoryNode }
|
|
|
|
{ categoryRoomNodes }
|
2017-07-10 18:32:02 +00:00
|
|
|
</div>;
|
|
|
|
},
|
|
|
|
|
2017-09-22 18:27:02 +00:00
|
|
|
_getFeaturedUsersNode: function() {
|
2017-07-10 18:32:02 +00:00
|
|
|
const summary = this.state.summary;
|
|
|
|
|
|
|
|
const noRoleUsers = [];
|
|
|
|
const roleUsers = {};
|
|
|
|
summary.users_section.users.forEach((u) => {
|
|
|
|
if (u.role_id === null) {
|
|
|
|
noRoleUsers.push(u);
|
|
|
|
} else {
|
|
|
|
let list = roleUsers[u.role_id];
|
|
|
|
if (list === undefined) {
|
|
|
|
list = [];
|
|
|
|
roleUsers[u.role_id] = list;
|
|
|
|
}
|
|
|
|
list.push(u);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2017-09-21 11:44:17 +00:00
|
|
|
const noRoleNode = <RoleUserList
|
|
|
|
users={noRoleUsers}
|
|
|
|
groupId={this.props.groupId}
|
2017-09-28 10:21:06 +00:00
|
|
|
editing={this.state.editing} />;
|
2017-07-10 18:32:02 +00:00
|
|
|
const roleUserNodes = Object.keys(roleUsers).map((roleId) => {
|
|
|
|
const role = summary.users_section.roles[roleId];
|
2017-09-21 11:44:17 +00:00
|
|
|
return <RoleUserList
|
|
|
|
key={roleId}
|
|
|
|
users={roleUsers[roleId]}
|
|
|
|
role={role}
|
|
|
|
groupId={this.props.groupId}
|
2017-09-28 10:21:06 +00:00
|
|
|
editing={this.state.editing} />;
|
2017-07-10 18:32:02 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
return <div className="mx_GroupView_featuredThings">
|
|
|
|
<div className="mx_GroupView_featuredThings_header">
|
2017-09-28 10:21:06 +00:00
|
|
|
{ _t('Featured Users:') }
|
2017-07-10 18:32:02 +00:00
|
|
|
</div>
|
2017-09-28 10:21:06 +00:00
|
|
|
{ noRoleNode }
|
|
|
|
{ roleUserNodes }
|
2017-07-10 18:32:02 +00:00
|
|
|
</div>;
|
|
|
|
},
|
|
|
|
|
2017-08-21 18:18:32 +00:00
|
|
|
_getMembershipSection: function() {
|
2017-09-22 18:27:02 +00:00
|
|
|
const Spinner = sdk.getComponent("elements.Spinner");
|
|
|
|
|
2017-08-21 18:18:32 +00:00
|
|
|
const group = MatrixClientPeg.get().getGroup(this.props.groupId);
|
|
|
|
if (!group) return null;
|
|
|
|
|
|
|
|
if (group.myMembership === 'invite') {
|
|
|
|
if (this.state.membershipBusy) {
|
2017-09-21 14:03:30 +00:00
|
|
|
return <div className="mx_GroupView_membershipSection">
|
2017-08-21 18:18:32 +00:00
|
|
|
<Spinner />
|
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
|
2017-09-21 14:03:30 +00:00
|
|
|
return <div className="mx_GroupView_membershipSection mx_GroupView_membershipSection_invited">
|
|
|
|
<div className="mx_GroupView_membershipSection_description">
|
2017-09-28 10:21:06 +00:00
|
|
|
{ _t("%(inviter)s has invited you to join this group", {inviter: group.inviter.userId}) }
|
2017-09-21 14:03:30 +00:00
|
|
|
</div>
|
2017-08-21 18:18:32 +00:00
|
|
|
<div className="mx_GroupView_membership_buttonContainer">
|
|
|
|
<AccessibleButton className="mx_GroupView_textButton mx_RoomHeader_textButton"
|
|
|
|
onClick={this._onAcceptInviteClick}
|
|
|
|
>
|
2017-09-28 10:21:06 +00:00
|
|
|
{ _t("Accept") }
|
2017-08-21 18:18:32 +00:00
|
|
|
</AccessibleButton>
|
|
|
|
<AccessibleButton className="mx_GroupView_textButton mx_RoomHeader_textButton"
|
|
|
|
onClick={this._onRejectInviteClick}
|
|
|
|
>
|
2017-09-28 10:21:06 +00:00
|
|
|
{ _t("Decline") }
|
2017-08-21 18:18:32 +00:00
|
|
|
</AccessibleButton>
|
|
|
|
</div>
|
|
|
|
</div>;
|
|
|
|
} else if (group.myMembership === 'join') {
|
2017-09-21 15:55:56 +00:00
|
|
|
let youAreAMemberText = _t("You are a member of this group");
|
|
|
|
if (this.state.summary.user && this.state.summary.user.is_privileged) {
|
|
|
|
youAreAMemberText = _t("You are an administrator of this group");
|
|
|
|
}
|
2017-09-22 18:27:02 +00:00
|
|
|
|
|
|
|
let publicisedButton;
|
|
|
|
if (this.state.publicityBusy) {
|
|
|
|
publicisedButton = <Spinner />;
|
|
|
|
}
|
|
|
|
|
|
|
|
let publicisedSection;
|
2017-09-26 13:46:57 +00:00
|
|
|
if (this.state.summary.user && this.state.summary.user.is_publicised) {
|
2017-09-22 18:27:02 +00:00
|
|
|
if (!this.state.publicityBusy) {
|
|
|
|
publicisedButton = <AccessibleButton className="mx_GroupView_textButton mx_RoomHeader_textButton"
|
|
|
|
onClick={this._onPubliciseOffClick}
|
|
|
|
>
|
2017-09-28 10:21:06 +00:00
|
|
|
{ _t("Unpublish") }
|
2017-09-22 18:27:02 +00:00
|
|
|
</AccessibleButton>;
|
|
|
|
}
|
|
|
|
publicisedSection = <div className="mx_GroupView_membershipSubSection">
|
2017-09-28 10:21:06 +00:00
|
|
|
{ _t("This group is published on your profile") }
|
2017-09-22 18:27:02 +00:00
|
|
|
<div className="mx_GroupView_membership_buttonContainer">
|
2017-09-28 10:21:06 +00:00
|
|
|
{ publicisedButton }
|
2017-09-22 18:27:02 +00:00
|
|
|
</div>
|
|
|
|
</div>;
|
|
|
|
} else {
|
|
|
|
if (!this.state.publicityBusy) {
|
|
|
|
publicisedButton = <AccessibleButton className="mx_GroupView_textButton mx_RoomHeader_textButton"
|
|
|
|
onClick={this._onPubliciseOnClick}
|
2017-08-21 18:18:32 +00:00
|
|
|
>
|
2017-09-28 10:21:06 +00:00
|
|
|
{ _t("Publish") }
|
2017-09-25 12:17:07 +00:00
|
|
|
</AccessibleButton>;
|
2017-09-22 18:27:02 +00:00
|
|
|
}
|
|
|
|
publicisedSection = <div className="mx_GroupView_membershipSubSection">
|
2017-09-28 10:21:06 +00:00
|
|
|
{ _t("This group is not published on your profile") }
|
2017-09-22 18:27:02 +00:00
|
|
|
<div className="mx_GroupView_membership_buttonContainer">
|
2017-09-28 10:21:06 +00:00
|
|
|
{ publicisedButton }
|
2017-09-22 18:27:02 +00:00
|
|
|
</div>
|
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
|
|
|
|
return <div className="mx_GroupView_membershipSection mx_GroupView_membershipSection_joined">
|
|
|
|
<div className="mx_GroupView_membershipSubSection">
|
|
|
|
<div className="mx_GroupView_membershipSection_description">
|
2017-09-28 10:21:06 +00:00
|
|
|
{ youAreAMemberText }
|
2017-09-22 18:27:02 +00:00
|
|
|
</div>
|
|
|
|
<div className="mx_GroupView_membership_buttonContainer">
|
|
|
|
<AccessibleButton className="mx_GroupView_textButton mx_RoomHeader_textButton"
|
|
|
|
onClick={this._onLeaveClick}
|
|
|
|
>
|
2017-09-28 10:21:06 +00:00
|
|
|
{ _t("Leave") }
|
2017-09-22 18:27:02 +00:00
|
|
|
</AccessibleButton>
|
|
|
|
</div>
|
2017-08-21 18:18:32 +00:00
|
|
|
</div>
|
2017-09-28 10:21:06 +00:00
|
|
|
{ publicisedSection }
|
2017-08-21 18:18:32 +00:00
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
2017-06-05 15:51:50 +00:00
|
|
|
render: function() {
|
2017-06-29 16:51:38 +00:00
|
|
|
const GroupAvatar = sdk.getComponent("avatars.GroupAvatar");
|
2017-06-23 16:02:54 +00:00
|
|
|
const Loader = sdk.getComponent("elements.Spinner");
|
2017-07-21 10:12:15 +00:00
|
|
|
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
2017-10-13 15:46:33 +00:00
|
|
|
const ScrollPanel = sdk.getComponent("structures.ScrollPanel");
|
2017-06-05 15:51:50 +00:00
|
|
|
|
2017-07-21 10:12:15 +00:00
|
|
|
if (this.state.summary === null && this.state.error === null || this.state.saving) {
|
2017-06-23 16:02:54 +00:00
|
|
|
return <Loader />;
|
2017-07-13 17:41:51 +00:00
|
|
|
} else if (this.state.summary) {
|
|
|
|
const summary = this.state.summary;
|
2017-07-06 18:13:14 +00:00
|
|
|
|
2017-07-13 17:41:51 +00:00
|
|
|
let avatarNode;
|
2017-06-29 16:17:43 +00:00
|
|
|
let nameNode;
|
2017-07-13 17:41:51 +00:00
|
|
|
let shortDescNode;
|
2017-10-13 15:46:33 +00:00
|
|
|
let bodyNodes = [];
|
2017-08-21 18:34:07 +00:00
|
|
|
const rightButtons = [];
|
2017-07-21 13:30:09 +00:00
|
|
|
const headerClasses = {
|
2017-07-21 13:13:57 +00:00
|
|
|
mx_GroupView_header: true,
|
|
|
|
};
|
2017-07-13 17:41:51 +00:00
|
|
|
if (this.state.editing) {
|
2017-07-21 13:03:10 +00:00
|
|
|
let avatarImage;
|
|
|
|
if (this.state.uploadingAvatar) {
|
|
|
|
avatarImage = <Loader />;
|
|
|
|
} else {
|
|
|
|
const GroupAvatar = sdk.getComponent('avatars.GroupAvatar');
|
|
|
|
avatarImage = <GroupAvatar groupId={this.props.groupId}
|
|
|
|
groupAvatarUrl={this.state.profileForm.avatar_url}
|
|
|
|
width={48} height={48} resizeMethod='crop'
|
|
|
|
/>;
|
|
|
|
}
|
|
|
|
|
2017-07-13 17:41:51 +00:00
|
|
|
avatarNode = (
|
|
|
|
<div className="mx_GroupView_avatarPicker">
|
2017-07-21 10:12:15 +00:00
|
|
|
<label htmlFor="avatarInput" className="mx_GroupView_avatarPicker_label">
|
2017-09-28 10:21:06 +00:00
|
|
|
{ avatarImage }
|
2017-07-21 10:12:15 +00:00
|
|
|
</label>
|
2017-07-13 17:41:51 +00:00
|
|
|
<div className="mx_GroupView_avatarPicker_edit">
|
2017-07-21 10:12:15 +00:00
|
|
|
<label htmlFor="avatarInput" className="mx_GroupView_avatarPicker_label">
|
2017-07-13 17:41:51 +00:00
|
|
|
<img src="img/camera.svg"
|
2017-09-28 10:21:06 +00:00
|
|
|
alt={_t("Upload avatar")} title={_t("Upload avatar")}
|
2017-07-13 17:41:51 +00:00
|
|
|
width="17" height="15" />
|
|
|
|
</label>
|
2017-09-28 10:21:06 +00:00
|
|
|
<input id="avatarInput" className="mx_GroupView_uploadInput" type="file" onChange={this._onAvatarSelected} />
|
2017-07-13 17:41:51 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
nameNode = <input type="text"
|
|
|
|
value={this.state.profileForm.name}
|
|
|
|
onChange={this._onNameChange}
|
|
|
|
placeholder={_t('Group Name')}
|
2017-07-17 13:40:38 +00:00
|
|
|
tabIndex="1"
|
2017-07-17 17:13:20 +00:00
|
|
|
/>;
|
2017-07-13 17:41:51 +00:00
|
|
|
shortDescNode = <input type="text"
|
|
|
|
value={this.state.profileForm.short_description}
|
|
|
|
onChange={this._onShortDescChange}
|
|
|
|
placeholder={_t('Description')}
|
2017-07-17 13:40:38 +00:00
|
|
|
tabIndex="2"
|
2017-07-17 17:13:20 +00:00
|
|
|
/>;
|
2017-08-15 12:12:39 +00:00
|
|
|
rightButtons.push(
|
2017-08-21 18:18:32 +00:00
|
|
|
<AccessibleButton className="mx_GroupView_textButton mx_RoomHeader_textButton"
|
2017-08-15 12:12:39 +00:00
|
|
|
onClick={this._onSaveClick} key="_saveButton"
|
|
|
|
>
|
2017-09-28 10:21:06 +00:00
|
|
|
{ _t('Save') }
|
2017-08-21 18:34:07 +00:00
|
|
|
</AccessibleButton>,
|
2017-08-15 12:12:39 +00:00
|
|
|
);
|
|
|
|
rightButtons.push(
|
2017-09-27 09:36:44 +00:00
|
|
|
<AccessibleButton className="mx_RoomHeader_cancelButton" onClick={this._onCancelClick} key="_cancelButton">
|
|
|
|
<img src="img/cancel.svg" className="mx_filterFlipColor"
|
2017-09-28 10:21:06 +00:00
|
|
|
width="18" height="18" alt={_t("Cancel")} />
|
2017-08-21 18:34:07 +00:00
|
|
|
</AccessibleButton>,
|
2017-08-15 12:12:39 +00:00
|
|
|
);
|
2017-10-13 15:46:33 +00:00
|
|
|
bodyNodes = [
|
|
|
|
<textarea className="mx_GroupView_editLongDesc"
|
|
|
|
value={this.state.profileForm.long_description}
|
2017-07-14 16:22:17 +00:00
|
|
|
onChange={this._onLongDescChange}
|
2017-07-17 13:40:38 +00:00
|
|
|
tabIndex="3"
|
2017-10-13 15:46:33 +00:00
|
|
|
key="editLongDesc"
|
|
|
|
/>,
|
|
|
|
this._getRoomsNode(),
|
|
|
|
];
|
2017-06-29 16:17:43 +00:00
|
|
|
} else {
|
2017-07-13 17:41:51 +00:00
|
|
|
const groupAvatarUrl = summary.profile ? summary.profile.avatar_url : null;
|
|
|
|
avatarNode = <GroupAvatar
|
|
|
|
groupId={this.props.groupId}
|
|
|
|
groupAvatarUrl={groupAvatarUrl}
|
|
|
|
width={48} height={48}
|
|
|
|
/>;
|
|
|
|
if (summary.profile && summary.profile.name) {
|
|
|
|
nameNode = <div>
|
2017-09-28 10:21:06 +00:00
|
|
|
<span>{ summary.profile.name }</span>
|
2017-07-13 17:41:51 +00:00
|
|
|
<span className="mx_GroupView_header_groupid">
|
2017-09-28 10:21:06 +00:00
|
|
|
({ this.props.groupId })
|
2017-07-13 17:41:51 +00:00
|
|
|
</span>
|
|
|
|
</div>;
|
|
|
|
} else {
|
2017-09-28 10:21:06 +00:00
|
|
|
nameNode = <span>{ this.props.groupId }</span>;
|
2017-07-13 17:41:51 +00:00
|
|
|
}
|
2017-10-13 15:46:33 +00:00
|
|
|
if (summary.profile && summary.profile.short_description) {
|
|
|
|
shortDescNode = <span>{ summary.profile.short_description }</span>;
|
|
|
|
}
|
2017-06-29 16:17:43 +00:00
|
|
|
|
2017-07-13 17:41:51 +00:00
|
|
|
let description = null;
|
|
|
|
if (summary.profile && summary.profile.long_description) {
|
|
|
|
description = sanitizedHtmlNode(summary.profile.long_description);
|
|
|
|
}
|
2017-10-13 15:46:33 +00:00
|
|
|
bodyNodes = [
|
|
|
|
this._getMembershipSection(),
|
|
|
|
<div key="groupDesc" className="mx_GroupView_groupDesc">{ description }</div>,
|
|
|
|
this._getRoomsNode(),
|
|
|
|
];
|
2017-09-21 15:55:56 +00:00
|
|
|
if (summary.user && summary.user.is_privileged) {
|
|
|
|
rightButtons.push(
|
|
|
|
<AccessibleButton className="mx_GroupHeader_button"
|
|
|
|
onClick={this._onEditClick} title={_t("Edit Group")} key="_editButton"
|
|
|
|
>
|
2017-09-28 10:21:06 +00:00
|
|
|
<TintableSvg src="img/icons-settings-room.svg" width="16" height="16" />
|
2017-09-21 15:55:56 +00:00
|
|
|
</AccessibleButton>,
|
|
|
|
);
|
|
|
|
}
|
2017-08-15 12:12:39 +00:00
|
|
|
if (this.props.collapsedRhs) {
|
|
|
|
rightButtons.push(
|
|
|
|
<AccessibleButton className="mx_GroupHeader_button"
|
2017-09-28 10:21:06 +00:00
|
|
|
onClick={this._onShowRhsClick} title={_t('Show panel')} key="_maximiseButton"
|
2017-08-15 12:12:39 +00:00
|
|
|
>
|
2017-09-28 10:21:06 +00:00
|
|
|
<TintableSvg src="img/maximise.svg" width="10" height="16" />
|
2017-08-21 18:34:07 +00:00
|
|
|
</AccessibleButton>,
|
2017-08-15 12:12:39 +00:00
|
|
|
);
|
|
|
|
}
|
2017-07-21 13:13:57 +00:00
|
|
|
|
|
|
|
headerClasses.mx_GroupView_header_view = true;
|
2017-07-13 17:41:51 +00:00
|
|
|
}
|
2017-07-07 17:34:40 +00:00
|
|
|
|
2017-06-05 15:51:50 +00:00
|
|
|
return (
|
2017-06-27 10:52:23 +00:00
|
|
|
<div className="mx_GroupView">
|
2017-07-21 13:13:57 +00:00
|
|
|
<div className={classnames(headerClasses)}>
|
2017-07-13 17:41:51 +00:00
|
|
|
<div className="mx_GroupView_header_leftCol">
|
|
|
|
<div className="mx_GroupView_header_avatar">
|
2017-09-28 10:21:06 +00:00
|
|
|
{ avatarNode }
|
2017-06-05 15:51:50 +00:00
|
|
|
</div>
|
2017-07-13 17:41:51 +00:00
|
|
|
<div className="mx_GroupView_header_info">
|
|
|
|
<div className="mx_GroupView_header_name">
|
2017-09-28 10:21:06 +00:00
|
|
|
{ nameNode }
|
2017-07-13 17:41:51 +00:00
|
|
|
</div>
|
|
|
|
<div className="mx_GroupView_header_shortDesc">
|
2017-09-28 10:21:06 +00:00
|
|
|
{ shortDescNode }
|
2017-06-05 15:51:50 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2017-07-13 17:41:51 +00:00
|
|
|
</div>
|
|
|
|
<div className="mx_GroupView_header_rightCol">
|
2017-09-28 10:21:06 +00:00
|
|
|
{ rightButtons }
|
2017-06-05 15:51:50 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2017-10-13 15:46:33 +00:00
|
|
|
<ScrollPanel className="mx_GroupView_body"
|
|
|
|
stickyBottom={false}
|
|
|
|
startAtBottom={false}
|
|
|
|
>
|
|
|
|
{ bodyNodes }
|
|
|
|
</ScrollPanel>
|
2017-06-05 15:51:50 +00:00
|
|
|
</div>
|
|
|
|
);
|
2017-06-27 09:28:46 +00:00
|
|
|
} else if (this.state.error) {
|
|
|
|
if (this.state.error.httpStatus === 404) {
|
|
|
|
return (
|
2017-06-27 10:52:23 +00:00
|
|
|
<div className="mx_GroupView_error">
|
2017-09-28 10:21:06 +00:00
|
|
|
Group { this.props.groupId } not found
|
2017-06-27 09:28:46 +00:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
let extraText;
|
|
|
|
if (this.state.error.errcode === 'M_UNRECOGNIZED') {
|
2017-09-28 10:21:06 +00:00
|
|
|
extraText = <div>{ _t('This Home server does not support groups') }</div>;
|
2017-06-27 09:28:46 +00:00
|
|
|
}
|
|
|
|
return (
|
2017-06-27 10:52:23 +00:00
|
|
|
<div className="mx_GroupView_error">
|
2017-09-28 10:21:06 +00:00
|
|
|
Failed to load { this.props.groupId }
|
|
|
|
{ extraText }
|
2017-06-27 09:28:46 +00:00
|
|
|
</div>
|
|
|
|
);
|
2017-06-26 16:47:17 +00:00
|
|
|
}
|
2017-06-27 12:41:43 +00:00
|
|
|
} else {
|
|
|
|
console.error("Invalid state for GroupView");
|
|
|
|
return <div />;
|
2017-06-05 15:51:50 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|