[WIP] FEATURE: merge share and invite actions together (#7021)

[WIP] FEATURE: merge share and invite actions together (#7021)

This commit also:

  • removes [+ New Topic] behaviour from share, this feature has been duplicated in composer actions, months ago
  • introduces our new experimental spacing standard for css: eg: s(2)
  • introduces a new panel UI for modals
diff --git a/app/assets/javascripts/discourse/components/d-modal.js.es6 b/app/assets/javascripts/discourse/components/d-modal.js.es6
index 44792ec..51c5824 100644
--- a/app/assets/javascripts/discourse/components/d-modal.js.es6
+++ b/app/assets/javascripts/discourse/components/d-modal.js.es6
@@ -1,9 +1,17 @@
 import { on } from "ember-addons/ember-computed-decorators";
 
 export default Ember.Component.extend({
-  classNameBindings: [":modal", ":d-modal", "modalClass", "modalStyle"],
+  classNameBindings: [
+    ":modal",
+    ":d-modal",
+    "modalClass",
+    "modalStyle",
+    "hasPanels"
+  ],
   attributeBindings: ["data-keyboard"],
   dismissable: true,
+  title: null,
+  subtitle: null,
 
   init() {
     this._super(...arguments);
diff --git a/app/assets/javascripts/discourse/components/group-selector.js.es6 b/app/assets/javascripts/discourse/components/group-selector.js.es6
index 8746b01..dc3db23 100644
--- a/app/assets/javascripts/discourse/components/group-selector.js.es6
+++ b/app/assets/javascripts/discourse/components/group-selector.js.es6
@@ -30,6 +30,7 @@ export default Ember.Component.extend({
         ? []
         : [groupNames],
       single: this.get("single"),
+      fullWidthWrap: this.get("fullWidthWrap"),
       updateData: opts && opts.updateData ? opts.updateData : false,
       onChangeItems: items => {
         selectedGroups = items;
diff --git a/app/assets/javascripts/discourse/components/invite-panel.js.es6 b/app/assets/javascripts/discourse/components/invite-panel.js.es6
new file mode 100644
index 0000000..2f0fc23
--- /dev/null
+++ b/app/assets/javascripts/discourse/components/invite-panel.js.es6
@@ -0,0 +1,448 @@
+import { emailValid } from "discourse/lib/utilities";
+import computed from "ember-addons/ember-computed-decorators";
+import Group from "discourse/models/group";
+import Invite from "discourse/models/invite";
+import { i18n } from "discourse/lib/computed";
+
+export default Ember.Component.extend({
+  tagName: null,
+
+  inviteModel: Ember.computed.alias("panel.model.inviteModel"),
+  userInvitedShow: Ember.computed.alias("panel.model.userInvitedShow"),
+
+  // If this isn't defined, it will proxy to the user topic on the preferences
+  // page which is wrong.
+  emailOrUsername: null,
+  hasCustomMessage: false,
+  hasCustomMessage: false,
+  customMessage: null,
+  inviteIcon: "envelope",
+  invitingExistingUserToTopic: false,
+
+  isAdmin: Ember.computed.alias("currentUser.admin"),
+
+  willDestroyElement() {
+    this._super(...arguments);
+
+    this.reset();
+  },
+
+  @computed(
+    "isAdmin",
+    "emailOrUsername",
+    "invitingToTopic",
+    "isPrivateTopic",
+    "topic.groupNames",
+    "topic.saving",
+    "topic.details.can_invite_to"
+  )
+  disabled(
+    isAdmin,
+    emailOrUsername,
+    invitingToTopic,
+    isPrivateTopic,
+    groupNames,
+    saving,
+    can_invite_to
+  ) {
+    if (saving) return true;
+    if (Ember.isEmpty(emailOrUsername)) return true;
+
+    const emailTrimmed = emailOrUsername.trim();
+
+    // when inviting to forum, email must be valid
+    if (!invitingToTopic && !emailValid(emailTrimmed)) {
+      return true;
+    }
+
+    // normal users (not admin) can't invite users to private topic via email
+    if (!isAdmin && isPrivateTopic && emailValid(emailTrimmed)) {
+      return true;
+    }
+
+    // when inviting to private topic via email, group name must be specified
+    if (
+      isPrivateTopic &&
+      Ember.isEmpty(groupNames) &&
+      emailValid(emailTrimmed)
+    ) {
+      return true;
+    }
+
+    if (can_invite_to) return false;
+
+    return false;
+  },
+
+  @computed(
+    "isAdmin",
+    "emailOrUsername",
+    "inviteModel.saving",
+    "isPrivateTopic",
+    "inviteModel.groupNames",
+    "hasCustomMessage"
+  )
+  disabledCopyLink(
+    isAdmin,
+    emailOrUsername,
+    saving,
+    isPrivateTopic,
+    groupNames,
+    hasCustomMessage
+  ) {
+    if (hasCustomMessage) return true;
+    if (saving) return true;
+    if (Ember.isEmpty(emailOrUsername)) return true;
+
+    const email = emailOrUsername.trim();
+
+    // email must be valid
+    if (!emailValid(email)) {
+      return true;
+    }
+
+    // normal users (not admin) can't invite users to private topic via email
+    if (!isAdmin && isPrivateTopic && emailValid(email)) {
+      return true;
+    }
+
+    // when inviting to private topic via email, group name must be specified
+    if (isPrivateTopic && Ember.isEmpty(groupNames) && emailValid(email)) {
+      return true;
+    }
+
+    return false;
+  },
+
+  @computed("inviteModel.saving")
+  buttonTitle(saving) {
+    return saving ? "topic.inviting" : "topic.invite_reply.action";
+  },
+
+  // We are inviting to a topic if the topic isn't the current user.
+  // The current user would mean we are inviting to the forum in general.
+  @computed("inviteModel")
+  invitingToTopic(inviteModel) {
+    return inviteModel !== this.currentUser;
+  },
+
+  @computed("inviteModel", "inviteModel.details.can_invite_via_email")
+  canInviteViaEmail(inviteModel, canInviteViaEmail) {
+    return this.get("inviteModel") === this.currentUser
+      ? true
+      : canInviteViaEmail;
+  },
+
+  @computed("isPM", "canInviteViaEmail")
+  showCopyInviteButton(isPM, canInviteViaEmail) {
+    return canInviteViaEmail && !isPM;
+  },
+
+  topicId: Ember.computed.alias("inviteModel.id"),
+
+  // eg: visible only to specific group members
+  isPrivateTopic: Ember.computed.and(
+    "invitingToTopic",
+    "inviteModel.category.read_restricted"
+  ),
+
+  isPM: Ember.computed.equal("inviteModel.archetype", "private_message"),
+
+  // scope to allowed usernames
+  allowExistingMembers: Ember.computed.alias("invitingToTopic"),
+
+  @computed("isAdmin", "inviteModel.group_users")
+  isGroupOwnerOrAdmin(isAdmin, groupUsers) {
+    return (
+      isAdmin || (groupUsers && groupUsers.some(groupUser => groupUser.owner))
+    );
+  },
+
+  // Show Groups? (add invited user to private group)
+  @computed(
+    "isGroupOwnerOrAdmin",
+    "emailOrUsername",
+    "isPrivateTopic",
+    "isPM",
+    "invitingToTopic",
+    "canInviteViaEmail"
+  )
+  showGroups(
+    isGroupOwnerOrAdmin,
+    emailOrUsername,
+    isPrivateTopic,
+    isPM,
+    invitingToTopic,
+    canInviteViaEmail
+  ) {
+    return (
+      isGroupOwnerOrAdmin &&
+      canInviteViaEmail &&
+      !isPM &&
+      (emailValid(emailOrUsername) || isPrivateTopic || !invitingToTopic)
+    );
+  },
+
+  @computed("emailOrUsername")
+  showCustomMessage(emailOrUsername) {
+    return (
+      this.get("inviteModel") === this.currentUser ||
+      emailValid(emailOrUsername)
+    );
+  },
+
+  // Instructional text for the modal.
+  @computed(
+    "isPM",
+    "invitingToTopic",
+    "emailOrUsername",
+    "isPrivateTopic",
+    "isAdmin",
+    "canInviteViaEmail"
+  )
+  inviteInstructions(
+    isPM,
+    invitingToTopic,
+    emailOrUsername,
+    isPrivateTopic,
+    isAdmin,
+    canInviteViaEmail
+  ) {
+    if (!canInviteViaEmail) {
+      // can't invite via email, only existing users
+      return I18n.t("topic.invite_reply.sso_enabled");
+    } else if (isPM) {
+      // inviting to a message
+      return I18n.t("topic.invite_private.email_or_username");
+    } else if (invitingToTopic) {
+      // inviting to a private/public topic
+      if (isPrivateTopic && !isAdmin) {
+        // inviting to a private topic and is not admin
+        return I18n.t("topic.invite_reply.to_username");
+      } else {
+        // when inviting to a topic, display instructions based on provided entity
+        if (Ember.isEmpty(emailOrUsername)) {
+          return I18n.t("topic.invite_reply.to_topic_blank");
+        } else if (emailValid(emailOrUsername)) {
+          this.set("inviteIcon", "envelope");
+          return I18n.t("topic.invite_reply.to_topic_email");
+        } else {
+          this.set("inviteIcon", "hand-point-right");
+          return I18n.t("topic.invite_reply.to_topic_username");
+        }
+      }
+    } else {

[... diff too long, it was truncated ...]

GitHub sha: 04a63cfa

2 Likes