diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 20b444b8da..87e714083b 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -145,7 +145,7 @@ const sanitizeHtmlParams = { font: ['color', 'data-mx-bg-color', 'data-mx-color', 'style'], // custom to matrix span: ['data-mx-bg-color', 'data-mx-color', 'style'], // custom to matrix a: ['href', 'name', 'target', 'rel'], // remote target: custom to matrix - img: ['src'], + img: ['src', 'width', 'height', 'alt', 'title'], ol: ['start'], code: ['class'], // We don't actually allow all classes, we filter them in transformTags }, diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index 30d67202e7..5f7866773d 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -22,6 +22,8 @@ import dis from '../../dispatcher'; import { sanitizedHtmlNode } from '../../HtmlUtils'; import { _t } from '../../languageHandler'; import AccessibleButton from '../views/elements/AccessibleButton'; +import Modal from '../../Modal'; +import classnames from 'classnames'; const RoomSummaryType = PropTypes.shape({ room_id: PropTypes.string.isRequired, @@ -179,10 +181,13 @@ export default React.createClass({ summary: null, error: null, editing: false, + saving: false, + uploadingAvatar: false, }; }, componentWillMount: function() { + this._changeAvatarComponent = null; this._loadGroupFromServer(this.props.groupId); }, @@ -211,8 +216,83 @@ export default React.createClass({ }); }, - _onSettingsClick: function() { - this.setState({editing: true}); + _onEditClick: function() { + this.setState({ + editing: true, + profileForm: Object.assign({}, this.state.summary.profile), + }); + }, + + _onCancelClick: function() { + this.setState({ + editing: false, + profileForm: null, + }); + }, + + _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, + }); + }, + + _onAvatarSelected: function(ev) { + const file = ev.target.files[0]; + if (!file) return; + + 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); + Modal.createDialog(ErrorDialog, { + title: _t('Error'), + description: _t('Failed to upload image'), + }); + }).done(); + }, + + _onSaveClick: function() { + this.setState({saving: true}); + MatrixClientPeg.get().setGroupProfile(this.props.groupId, this.state.profileForm).then((result) => { + this.setState({ + saving: false, + editing: false, + summary: null, + }); + this._loadGroupFromServer(this.props.groupId); + }).catch((e) => { + this.setState({ + saving: false, + }); + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + console.error("Failed to save group profile", e); + Modal.createDialog(ErrorDialog, { + title: _t('Error'), + description: _t('Failed to update group'), + }); + }).done(); }, _getFeaturedRoomsNode() { @@ -296,60 +376,129 @@ export default React.createClass({ const Loader = sdk.getComponent("elements.Spinner"); const TintableSvg = sdk.getComponent("elements.TintableSvg"); - if (this.state.summary === null && this.state.error === null) { + if (this.state.summary === null && this.state.error === null || this.state.saving) { return ; - } else if (this.state.editing) { - return
; } else if (this.state.summary) { const summary = this.state.summary; - let description = null; - if (summary.profile && summary.profile.long_description) { - description = sanitizedHtmlNode(summary.profile.long_description); - } - - const roomBody =
-
{description}
- {this._getFeaturedRoomsNode()} - {this._getFeaturedUsersNode()} -
; + let avatarNode; let nameNode; - if (summary.profile && summary.profile.name) { - nameNode =
- {summary.profile.name} - - ({this.props.groupId}) - + let shortDescNode; + let rightButtons; + let roomBody; + const headerClasses = { + mx_GroupView_header: true, + }; + if (this.state.editing) { + let avatarImage; + if (this.state.uploadingAvatar) { + avatarImage = ; + } else { + const GroupAvatar = sdk.getComponent('avatars.GroupAvatar'); + avatarImage = ; + } + + avatarNode = ( +
+ +
+ + +
+
+ ); + nameNode = ; + shortDescNode = ; + rightButtons = + + {_t('Save')} + + + {_t("Cancel")}/ + + ; + roomBody =
+