diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js
index 14d5d24bb5..e7e1e82b82 100644
--- a/src/components/structures/GroupView.js
+++ b/src/components/structures/GroupView.js
@@ -1,5 +1,6 @@
/*
Copyright 2017 Vector Creations Ltd.
+Copyright 2017 New Vector Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -183,12 +184,19 @@ export default React.createClass({
editing: false,
saving: false,
uploadingAvatar: false,
+ membershipBusy: false,
};
},
componentWillMount: function() {
this._changeAvatarComponent = null;
this._loadGroupFromServer(this.props.groupId);
+
+ MatrixClientPeg.get().on("Group.myMembership", this._onGroupMyMembership);
+ },
+
+ componentWillUnmount: function() {
+ MatrixClientPeg.get().removeListener("Group.myMembership", this._onGroupMyMembership);
},
componentWillReceiveProps: function(newProps) {
@@ -202,6 +210,12 @@ export default React.createClass({
}
},
+ _onGroupMyMembership: function(group) {
+ if (group.groupId !== this.props.groupId) return;
+
+ this.setState({membershipBusy: false});
+ },
+
_loadGroupFromServer: function(groupId) {
MatrixClientPeg.get().getGroupSummary(groupId).done((res) => {
this.setState({
@@ -299,6 +313,59 @@ export default React.createClass({
}).done();
},
+ _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"),
+ });
+ });
+ }
+ });
+ },
+
_getFeaturedRoomsNode() {
const summary = this.state.summary;
@@ -375,6 +442,50 @@ export default React.createClass({
;
},
+ _getMembershipSection: function() {
+ const group = MatrixClientPeg.get().getGroup(this.props.groupId);
+ if (!group) return null;
+
+ if (group.myMembership === 'invite') {
+ const Spinner = sdk.getComponent("elements.Spinner");
+
+ if (this.state.membershipBusy) {
+ return
+
+
;
+ }
+
+ return
+ {_t("%(inviter)s has invited you to join this group", {inviter: group.inviter.userId})}
+
+
+ {_t('Accept')}
+
+
+ {_t('Decline')}
+
+
+
;
+ } else if (group.myMembership === 'join') {
+ return
+ {_t("You are a member of this group")}
+
+
;
+ }
+
+ return null;
+ },
+
render: function() {
const GroupAvatar = sdk.getComponent("avatars.GroupAvatar");
const Loader = sdk.getComponent("elements.Spinner");
@@ -433,14 +544,14 @@ export default React.createClass({
tabIndex="2"
/>;
rightButtons.push(
-
{_t('Save')}
);
rightButtons.push(
-
+
@@ -475,6 +586,7 @@ export default React.createClass({
description = sanitizedHtmlNode(summary.profile.long_description);
}
roomBody =
+ {this._getMembershipSection()}
{description}
{this._getFeaturedRoomsNode()}
{this._getFeaturedUsersNode()}
diff --git a/src/components/views/dialogs/QuestionDialog.js b/src/components/views/dialogs/QuestionDialog.js
index ec9b95d7f7..22e68e3ac3 100644
--- a/src/components/views/dialogs/QuestionDialog.js
+++ b/src/components/views/dialogs/QuestionDialog.js
@@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
+Copyright 2017 New Vector Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,6 +18,7 @@ limitations under the License.
import React from 'react';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';
+import classnames from 'classnames';
export default React.createClass({
displayName: 'QuestionDialog',
@@ -25,6 +27,7 @@ export default React.createClass({
description: React.PropTypes.node,
extraButtons: React.PropTypes.node,
button: React.PropTypes.string,
+ danger: React.PropTypes.bool,
focus: React.PropTypes.bool,
onFinished: React.PropTypes.func.isRequired,
},
@@ -36,6 +39,7 @@ export default React.createClass({
extraButtons: null,
focus: true,
hasCancelButton: true,
+ danger: false,
};
},
@@ -54,6 +58,10 @@ export default React.createClass({
{_t("Cancel")}
) : null;
+ const buttonClasses = classnames({
+ mx_Dialog_primary: true,
+ danger: this.props.danger,
+ });
return (
-