FEATURE: Claim Reviewables by Topic

FEATURE: Claim Reviewables by Topic

This is a feature that used to be present in discourse-assign but is much easier to implement in core. It also allows a topic to be assigned without it claiming for review and vice versa and allows it to work with category group reviewers.

diff --git a/app/assets/javascripts/discourse/components/reviewable-claimed-topic.js.es6 b/app/assets/javascripts/discourse/components/reviewable-claimed-topic.js.es6
new file mode 100644
index 0000000..8d9bd6c
--- /dev/null
+++ b/app/assets/javascripts/discourse/components/reviewable-claimed-topic.js.es6
@@ -0,0 +1,33 @@
+import computed from "ember-addons/ember-computed-decorators";
+import { popupAjaxError } from "discourse/lib/ajax-error";
+import { ajax } from "discourse/lib/ajax";
+
+export default Ember.Component.extend({
+  tagName: "",
+
+  @computed
+  enabled() {
+    return this.siteSettings.reviewable_claiming !== "disabled";
+  },
+
+  actions: {
+    unclaim() {
+      ajax(`/reviewable_claimed_topics/${this.get("topicId")}`, {
+        method: "DELETE"
+      }).then(() => {
+        this.set("claimedBy", null);
+      });
+    },
+
+    claim() {
+      let claim = this.store.createRecord("reviewable-claimed-topic");
+
+      claim
+        .save({ topic_id: this.get("topicId") })
+        .then(() => {
+          this.set("claimedBy", this.currentUser);
+        })
+        .catch(popupAjaxError);
+    }
+  }
+});
diff --git a/app/assets/javascripts/discourse/components/reviewable-item.js.es6 b/app/assets/javascripts/discourse/components/reviewable-item.js.es6
index ffce8c8..27e8aee 100644
--- a/app/assets/javascripts/discourse/components/reviewable-item.js.es6
+++ b/app/assets/javascripts/discourse/components/reviewable-item.js.es6
@@ -18,6 +18,43 @@ export default Ember.Component.extend({
     return type.dasherize();
   },
 
+  @computed("siteSettings.reviewable_claiming", "reviewable.topic")
+  claimEnabled(claimMode, topic) {
+    return claimMode !== "disabled" && !!topic;
+  },
+
+  @computed(
+    "claimEnabled",
+    "siteSettings.reviewable_claiming",
+    "reviewable.claimed_by"
+  )
+  canPerform(claimEnabled, claimMode, claimedBy) {
+    if (!claimEnabled) {
+      return true;
+    }
+
+    if (claimedBy) {
+      return claimedBy.id === this.currentUser.id;
+    }
+
+    return claimMode !== "required";
+  },
+
+  @computed("siteSettings.reviewable_claiming", "reviewable.claimed_by")
+  claimHelp(claimMode, claimedBy) {
+    if (claimedBy) {
+      return claimedBy.id === this.currentUser.id
+        ? I18n.t("review.claim_help.claimed_by_you")
+        : I18n.t("review.claim_help.claimed_by_other", {
+            username: claimedBy.username
+          });
+    }
+
+    return claimMode === "optional"
+      ? I18n.t("review.claim_help.optional")
+      : I18n.t("review.claim_help.required");
+  },
+
   // Find a component to render, if one exists. For example:
   // `ReviewableUser` will return `reviewable-user`
   @computed("reviewable.type")
diff --git a/app/assets/javascripts/discourse/models/store.js.es6 b/app/assets/javascripts/discourse/models/store.js.es6
index 486da7c..4e6e68d 100644
--- a/app/assets/javascripts/discourse/models/store.js.es6
+++ b/app/assets/javascripts/discourse/models/store.js.es6
@@ -311,6 +311,8 @@ export default Ember.Object.extend({
           if (hydrated) {
             obj[subType] = hydrated;
             delete obj[k];
+          } else {
+            obj[subType] = null;
           }
         }
       }
diff --git a/app/assets/javascripts/discourse/templates/components/reviewable-claimed-topic.hbs b/app/assets/javascripts/discourse/templates/components/reviewable-claimed-topic.hbs
new file mode 100644
index 0000000..92f3fcc
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/components/reviewable-claimed-topic.hbs
@@ -0,0 +1,18 @@
+{{#if enabled}}
+  <div class='reviewable-claimed-topic'>
+    {{#if claimedBy}}
+      <div class='claimed-by'>
+        {{avatar claimedBy imageSize="small"}}
+        <span class='claimed-username'>{{claimedBy.username}}</span>
+      </div>
+      {{d-button
+        icon="times"
+        class="btn-small unclaim"
+        action=(action "unclaim")
+        disabled=unassigning
+        title="review.unclaim.help"}}
+    {{else}}
+      {{d-button icon="user-plus" class="btn-small claim" title="review.claim.title" action=(action "claim")}}
+    {{/if}}
+  </div>
+{{/if}}
diff --git a/app/assets/javascripts/discourse/templates/components/reviewable-item.hbs b/app/assets/javascripts/discourse/templates/components/reviewable-item.hbs
index dc1c908..7df7ee1 100644
--- a/app/assets/javascripts/discourse/templates/components/reviewable-item.hbs
+++ b/app/assets/javascripts/discourse/templates/components/reviewable-item.hbs
@@ -41,34 +41,43 @@
     {{/if}}
   </div>
   <div class='reviewable-actions'>
-    {{#if editing}}
-      {{d-button
-        class="btn-primary reviewable-action save-edit"
-        disabled=updating
-        icon="check"
-        action=(action "saveEdit")
-        label="review.save"}}
-      {{d-button
-        class="btn-danger reviewable-action cancel-edit"
-        disabled=updating
-        icon="times"
-        action=(action "cancelEdit")
-        label="review.cancel"}}
-    {{else}}
-      {{#each reviewable.bundled_actions as |bundle|}}
-        {{reviewable-bundled-action
-          bundle=bundle
-          performAction=(action "perform")
-          reviewableUpdating=updating}}
-      {{/each}}
+    {{#if claimEnabled}}
+      <div class='claimed-actions'>
+        <span class='help'>{{{claimHelp}}}</span>
+        {{reviewable-claimed-topic topicId=reviewable.topic.id claimedBy=reviewable.claimed_by}}
+      </div>
+    {{/if}}
 
-      {{#if reviewable.can_edit}}
+    {{#if canPerform}}
+      {{#if editing}}
         {{d-button
-          class="reviewable-action edit"
+          class="btn-primary reviewable-action save-edit"
           disabled=updating
-          icon="pencil-alt"
-          action=(action "edit")
-          label="review.edit"}}
+          icon="check"
+          action=(action "saveEdit")
+          label="review.save"}}
+        {{d-button
+          class="btn-danger reviewable-action cancel-edit"
+          disabled=updating
+          icon="times"
+          action=(action "cancelEdit")
+          label="review.cancel"}}
+      {{else}}
+        {{#each reviewable.bundled_actions as |bundle|}}
+          {{reviewable-bundled-action
+            bundle=bundle
+            performAction=(action "perform")
+            reviewableUpdating=updating}}
+        {{/each}}
+
+        {{#if reviewable.can_edit}}
+          {{d-button
+            class="reviewable-action edit"
+            disabled=updating
+            icon="pencil-alt"
+            action=(action "edit")
+            label="review.edit"}}
+        {{/if}}
       {{/if}}
     {{/if}}
   </div>
diff --git a/app/assets/javascripts/discourse/templates/review-topics.hbs b/app/assets/javascripts/discourse/templates/review-topics.hbs
index 39a76f3..029c5c3 100644
--- a/app/assets/javascripts/discourse/templates/review-topics.hbs
+++ b/app/assets/javascripts/discourse/templates/review-topics.hbs
@@ -22,6 +22,7 @@
           {{i18n "review.topics.unique_users" count=rt.stats.unique_users}}
         </td>
         <td class="reviewable-details">
+          {{reviewable-claimed-topic topicId=rt.id claimedBy=rt.claimed_by}}
           {{#link-to "review.index" (query-params topic_id=rt.id) class="btn btn-primary btn-small"}}
             {{d-icon "list"}}
             <span>{{i18n "review.topics.details"}}</span>
diff --git a/app/assets/stylesheets/common/base/reviewables.scss b/app/assets/stylesheets/common/base/reviewables.scss
index ec2c2df..3f4a183 100644
--- a/app/assets/stylesheets/common/base/reviewables.scss
+++ b/app/assets/stylesheets/common/base/reviewables.scss
@@ -116,6 +116,30 @@
   }
 }
 
+.reviewable-claimed-topic {
+  display: flex;
+  align-items: center;
+  .btn-small {
+    margin-left: 0.5em;
+  }
+}
+
+.reviewable-actions .claimed-actions {
+  display: flex;
+  width: 100%;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 0.5em;
+}
+
+.claimed-by {
+  display: flex;
+  align-items: center;
+  .claimed-username {

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

GitHub sha: b380ed52

2 Likes

This commit has been mentioned on Discourse Meta. There might be relevant details there:

Do we need a reminder somewhere to nuke code from discourse-assign?

It’s no longer leaded in discourse-assign if reviewable is present and I asked @romanrizzi to add a reminder to remove the code. Roman can you confirm you have something set up to remind you to remove the code?

1 Like

I didn’t add it yet, sorry.

Will add today :+1:

2 Likes

@romanrizzi was the code removed?

Not yet. My understanding is that the code needs to be removed after 2.3 becomes the new stable version.

1 Like

This is correct!

1 Like