FEATURE: Split Add Members into Add Users & Invite (#13482)
Add Members could also invite new users via emails, but that was a less known fact. Splitting the previous modal into two more accessible modals should make this feature more discoverable.
This commit is contained in:
parent
fbe004cc63
commit
2c2e81486c
|
@ -1,88 +1,49 @@
|
||||||
import Controller from "@ember/controller";
|
import Controller from "@ember/controller";
|
||||||
import I18n from "I18n";
|
|
||||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
import { emailValid } from "discourse/lib/utilities";
|
|
||||||
import { extractError } from "discourse/lib/ajax-error";
|
|
||||||
import { isEmpty } from "@ember/utils";
|
import { isEmpty } from "@ember/utils";
|
||||||
import { reads } from "@ember/object/computed";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
import { extractError } from "discourse/lib/ajax-error";
|
||||||
|
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||||
|
import I18n from "I18n";
|
||||||
|
|
||||||
export default Controller.extend(ModalFunctionality, {
|
export default Controller.extend(ModalFunctionality, {
|
||||||
loading: false,
|
loading: false,
|
||||||
setAsOwner: false,
|
|
||||||
|
usernames: null,
|
||||||
|
setOwner: false,
|
||||||
notifyUsers: false,
|
notifyUsers: false,
|
||||||
usernamesAndEmails: null,
|
|
||||||
emailsPresent: reads("emails.length"),
|
|
||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
usernamesAndEmails: [],
|
loading: false,
|
||||||
setAsOwner: false,
|
setOwner: false,
|
||||||
notifyUsers: false,
|
notifyUsers: false,
|
||||||
|
usernames: [],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("usernamesAndEmails", "loading")
|
|
||||||
disableAddButton(usernamesAndEmails, loading) {
|
|
||||||
return loading || !usernamesAndEmails || !(usernamesAndEmails.length > 0);
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("usernamesAndEmails")
|
|
||||||
notifyUsersDisabled() {
|
|
||||||
return this.usernames.length === 0 && this.emails.length > 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("model.name", "model.full_name")
|
@discourseComputed("model.name", "model.full_name")
|
||||||
title(name, fullName) {
|
rawTitle(name, fullName) {
|
||||||
return I18n.t("groups.add_members.title", { group_name: fullName || name });
|
return I18n.t("groups.add_members.title", { group_name: fullName || name });
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("usernamesAndEmails.[]")
|
|
||||||
emails(usernamesAndEmails) {
|
|
||||||
return usernamesAndEmails.filter(emailValid).join(",");
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("usernamesAndEmails.[]")
|
|
||||||
usernames(usernamesAndEmails) {
|
|
||||||
return usernamesAndEmails.reject(emailValid).join(",");
|
|
||||||
},
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
addMembers() {
|
addMembers() {
|
||||||
this.set("loading", true);
|
if (isEmpty(this.usernames)) {
|
||||||
|
|
||||||
if (this.emailsPresent) {
|
|
||||||
this.set("setAsOwner", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.notifyUsersDisabled) {
|
|
||||||
this.set("notifyUsers", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEmpty(this.usernamesAndEmails)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const promise = this.setAsOwner
|
this.set("loading", true);
|
||||||
? this.model.addOwners(this.usernames, true, this.notifyUsers)
|
|
||||||
: this.model.addMembers(
|
const usernames = this.usernames.join(",");
|
||||||
this.usernames,
|
const promise = this.setOwner
|
||||||
true,
|
? this.model.addOwners(usernames, true, this.notifyUsers)
|
||||||
this.notifyUsers,
|
: this.model.addMembers(usernames, true, this.notifyUsers);
|
||||||
this.emails
|
|
||||||
);
|
|
||||||
|
|
||||||
promise
|
promise
|
||||||
.then(() => {
|
.then(() => {
|
||||||
let queryParams = {};
|
|
||||||
|
|
||||||
if (this.usernames) {
|
|
||||||
queryParams.filter = this.usernames;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.transitionToRoute("group.members", this.get("model.name"), {
|
this.transitionToRoute("group.members", this.get("model.name"), {
|
||||||
queryParams,
|
queryParams: usernames ? { filter: usernames } : {},
|
||||||
});
|
});
|
||||||
|
|
||||||
this.send("closeModal");
|
this.send("closeModal");
|
||||||
|
|
|
@ -28,6 +28,15 @@ export default DiscourseRoute.extend({
|
||||||
showModal("group-add-members", { model: this.modelFor("group") });
|
showModal("group-add-members", { model: this.modelFor("group") });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
showInviteModal() {
|
||||||
|
const model = this.modelFor("group");
|
||||||
|
const controller = showModal("create-invite");
|
||||||
|
controller.set("showAdvanced", true);
|
||||||
|
controller.buffered.set("groupIds", [model.id]);
|
||||||
|
controller.save({ autogenerated: true });
|
||||||
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
didTransition() {
|
didTransition() {
|
||||||
this.controllerFor("group-index").set("filterInput", this._params.filter);
|
this.controllerFor("group-index").set("filterInput", this._params.filter);
|
||||||
|
|
|
@ -9,14 +9,25 @@
|
||||||
}}
|
}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="group-members-manage">
|
|
||||||
{{#if canManageGroup}}
|
{{#if canManageGroup}}
|
||||||
{{d-button icon="plus"
|
<div class="group-members-manage">
|
||||||
|
{{d-button
|
||||||
|
icon="plus"
|
||||||
action=(route-action "showAddMembersModal")
|
action=(route-action "showAddMembersModal")
|
||||||
label="groups.manage.add_members"
|
label="groups.manage.add_members"
|
||||||
class="btn-default group-members-add"}}
|
class="btn-default group-members-add"
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{#if currentUser.can_invite_to_forum}}
|
||||||
|
{{d-button
|
||||||
|
icon="plus"
|
||||||
|
action=(route-action "showInviteModal")
|
||||||
|
label="groups.manage.invite_members"
|
||||||
|
class="btn-default group-members-add"
|
||||||
|
}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if hasMembers}}
|
{{#if hasMembers}}
|
||||||
|
|
|
@ -1,47 +1,26 @@
|
||||||
{{#d-modal-body rawTitle=title}}
|
{{#d-modal-body rawTitle=rawTitle}}
|
||||||
<form class="form-vertical group-add-members">
|
<form class="form-vertical group-add-members">
|
||||||
<div class="control-group">
|
<p>{{i18n "groups.add_members.description"}}</p>
|
||||||
<label class="control-label">
|
|
||||||
{{#if currentUser.can_invite_to_forum}}
|
|
||||||
{{i18n "groups.add_members.usernames_or_emails.title"}}
|
|
||||||
{{else}}
|
|
||||||
{{i18n "groups.add_members.usernames.title"}}
|
|
||||||
{{/if}}
|
|
||||||
</label>
|
|
||||||
<p class="description">
|
|
||||||
{{i18n "groups.add_members.description"}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
{{email-group-user-chooser
|
{{email-group-user-chooser
|
||||||
class="input-xxlarge"
|
value=usernames
|
||||||
value=usernamesAndEmails
|
onChange=(action (mut usernames))
|
||||||
id="group-add-members-user-selector"
|
|
||||||
onChange=(action (mut usernamesAndEmails))
|
|
||||||
options=(hash
|
|
||||||
allowEmails=currentUser.can_invite_to_forum
|
|
||||||
filterPlaceholder=(if currentUser.can_invite_to_forum "groups.add_members.usernames_or_emails.input_placeholder" "groups.add_members.usernames.input_placeholder")
|
|
||||||
)
|
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if model.can_admin_group}}
|
{{#if model.can_admin_group}}
|
||||||
<div class="control-group group-add-members-make-owner">
|
<div class="input-group">
|
||||||
<label>
|
<label>
|
||||||
{{input type="checkbox"
|
{{input id="set-owner" type="checkbox" checked=setOwner disabled=emailsPresent}}
|
||||||
class="inline"
|
{{i18n "groups.add_members.set_owner"}}
|
||||||
checked=setAsOwner
|
|
||||||
disabled=emailsPresent}}
|
|
||||||
{{i18n "admin.groups.add_members.as_owner"}}
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="control-group group-add-members-notify-users">
|
<div class="input-group">
|
||||||
<label>
|
<label>
|
||||||
{{input type="checkbox"
|
{{input type="checkbox" checked=notifyUsers}}
|
||||||
class="inline"
|
|
||||||
checked=notifyUsers
|
|
||||||
disabled=notifyUsersDisabled}}
|
|
||||||
{{i18n "groups.add_members.notify_users"}}
|
{{i18n "groups.add_members.notify_users"}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -52,6 +31,6 @@
|
||||||
{{d-button action=(action "addMembers")
|
{{d-button action=(action "addMembers")
|
||||||
class="add btn-primary"
|
class="add btn-primary"
|
||||||
icon="plus"
|
icon="plus"
|
||||||
disabled=disableAddButton
|
disabled=(or loading (not usernames))
|
||||||
label="groups.add"}}
|
label="groups.add"}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -49,7 +49,7 @@ acceptance("Group Members", function (needs) {
|
||||||
await click(".group-members-add");
|
await click(".group-members-add");
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
count("#group-add-members-user-selector"),
|
count(".user-chooser"),
|
||||||
1,
|
1,
|
||||||
"it should display the add members modal"
|
"it should display the add members modal"
|
||||||
);
|
);
|
||||||
|
|
|
@ -288,7 +288,7 @@ acceptance("Group - Authenticated", function (needs) {
|
||||||
await click(".group-members-add.btn");
|
await click(".group-members-add.btn");
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
exists(".group-add-members-modal .group-add-members-make-owner"),
|
exists(".group-add-members-modal #set-owner"),
|
||||||
"it allows moderators to set group owners"
|
"it allows moderators to set group owners"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -983,3 +983,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.group-add-members-modal {
|
||||||
|
.input-group {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-chooser {
|
||||||
|
width: calc(100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -668,15 +668,10 @@ en:
|
||||||
member_added: "Added"
|
member_added: "Added"
|
||||||
member_requested: "Requested at"
|
member_requested: "Requested at"
|
||||||
add_members:
|
add_members:
|
||||||
title: "Add members to %{group_name}"
|
title: "Add Users to %{group_name}"
|
||||||
description: "You can also paste in a comma separated list."
|
description: "Enter a list of users you want to invite to the group or paste in a comma separated list:"
|
||||||
usernames_or_emails:
|
|
||||||
title: "Enter usernames or email addresses"
|
|
||||||
input_placeholder: "Usernames or emails"
|
|
||||||
usernames:
|
|
||||||
title: "Enter usernames"
|
|
||||||
input_placeholder: "Usernames"
|
|
||||||
notify_users: "Notify users"
|
notify_users: "Notify users"
|
||||||
|
set_owner: "Set users as owners of this group"
|
||||||
requests:
|
requests:
|
||||||
title: "Requests"
|
title: "Requests"
|
||||||
reason: "Reason"
|
reason: "Reason"
|
||||||
|
@ -690,7 +685,8 @@ en:
|
||||||
title: "Manage"
|
title: "Manage"
|
||||||
name: "Name"
|
name: "Name"
|
||||||
full_name: "Full Name"
|
full_name: "Full Name"
|
||||||
add_members: "Add Members"
|
add_members: "Add Users"
|
||||||
|
invite_members: "Invite"
|
||||||
delete_member_confirm: "Remove '%{username}' from the '%{group}' group?"
|
delete_member_confirm: "Remove '%{username}' from the '%{group}' group?"
|
||||||
profile:
|
profile:
|
||||||
title: Profile
|
title: Profile
|
||||||
|
@ -3926,8 +3922,6 @@ en:
|
||||||
available: "Group name is available"
|
available: "Group name is available"
|
||||||
not_available: "Group name is not available"
|
not_available: "Group name is not available"
|
||||||
blank: "Group name cannot be blank"
|
blank: "Group name cannot be blank"
|
||||||
add_members:
|
|
||||||
as_owner: "Set user(s) as owner(s) of this group"
|
|
||||||
manage:
|
manage:
|
||||||
interaction:
|
interaction:
|
||||||
email: Email
|
email: Email
|
||||||
|
|
Loading…
Reference in New Issue