FEATURE: Approved notifications (#13)

FEATURE: Approved notifications (#13)

  • Add widget for new notification type

  • Create commit notifications

  • Add user activity pages for approved and pending commits

  • Fix test name

  • Prefabricate the signed in user

  • Add rails tests for new notification behaviour

diff --git a/app/controllers/discourse_code_review/code_review_controller.rb b/app/controllers/discourse_code_review/code_review_controller.rb
index 239bac1..7c0615a 100644
--- a/app/controllers/discourse_code_review/code_review_controller.rb
+++ b/app/controllers/discourse_code_review/code_review_controller.rb
@@ -101,14 +101,51 @@ module DiscourseCodeReview
 
         DiscourseTagging.tag_topic_by_names(topic, Guardian.new(current_user), tags)
 
-        topic.add_moderator_post(
-          current_user,
-          nil,
-          bump: false,
-          post_type: Post.types[:small_action],
-          action_code: "approved"
+        old_highest_post_number = topic.highest_post_number
+        post =
+          topic.add_moderator_post(
+            current_user,
+            nil,
+            bump: false,
+            post_type: Post.types[:small_action],
+            action_code: "approved"
+          )
+
+        PostTiming.pretend_read(
+          topic.id,
+          old_highest_post_number,
+          post.post_number
         )
 
+        Notification.transaction do
+          destroyed_notifications =
+            topic.user.notifications
+              .where(
+                notification_type: Notification.types[:code_review_commit_approved],
+              )
+              .where('created_at > ?', 6.hours.ago)
+              .destroy_all
+
+          previous_approved =
+            destroyed_notifications.inject(0) do |sum, notification|
+              sum + JSON.parse(notification.data)['num_approved_commits'].to_i
+            end
+
+          if previous_approved == 0
+            topic.user.notifications.create(
+              notification_type: Notification.types[:code_review_commit_approved],
+              topic_id: topic.id,
+              post_number: post.post_number,
+              data: {num_approved_commits: 1}.to_json
+            )
+          else
+            topic.user.notifications.create(
+              notification_type: Notification.types[:code_review_commit_approved],
+              data: {num_approved_commits: previous_approved + 1}.to_json
+            )
+          end
+        end
+
         if SiteSetting.code_review_auto_unassign_on_approve && topic.user.staff?
           DiscourseEvent.trigger(:unassign_topic, topic, current_user)
         end
diff --git a/assets/javascripts/discourse/activity-route-map.js.es6 b/assets/javascripts/discourse/activity-route-map.js.es6
new file mode 100644
index 0000000..def5310
--- /dev/null
+++ b/assets/javascripts/discourse/activity-route-map.js.es6
@@ -0,0 +1,7 @@
+export default {
+  resource: "user.userActivity",
+  map() {
+    this.route("approval-given");
+    this.route("approval-pending");
+  }
+};
diff --git a/assets/javascripts/discourse/routes/user-activity-approval-given.js.es6 b/assets/javascripts/discourse/routes/user-activity-approval-given.js.es6
new file mode 100644
index 0000000..8dfb919
--- /dev/null
+++ b/assets/javascripts/discourse/routes/user-activity-approval-given.js.es6
@@ -0,0 +1,9 @@
+import UserTopicListRoute from "discourse/routes/user-topic-list";
+
+export default UserTopicListRoute.extend({
+  model: function() {
+    return this.store.findFiltered("topicList", {
+      filter: `topics/approval-given/${this.modelFor("user").get("username_lower")}`
+    });
+  }
+});
diff --git a/assets/javascripts/discourse/routes/user-activity-approval-pending.js.es6 b/assets/javascripts/discourse/routes/user-activity-approval-pending.js.es6
new file mode 100644
index 0000000..aecd8da
--- /dev/null
+++ b/assets/javascripts/discourse/routes/user-activity-approval-pending.js.es6
@@ -0,0 +1,9 @@
+import UserTopicListRoute from "discourse/routes/user-topic-list";
+
+export default UserTopicListRoute.extend({
+  model: function() {
+    return this.store.findFiltered("topicList", {
+      filter: `topics/approval-pending/${this.modelFor("user").get("username_lower")}`
+    });
+  }
+});
diff --git a/assets/javascripts/discourse/templates/connectors/user-activity-bottom/commit-activity-link.hbs b/assets/javascripts/discourse/templates/connectors/user-activity-bottom/commit-activity-link.hbs
new file mode 100644
index 0000000..2cca9db
--- /dev/null
+++ b/assets/javascripts/discourse/templates/connectors/user-activity-bottom/commit-activity-link.hbs
@@ -0,0 +1,10 @@
+<li>
+  {{#link-to 'userActivity.approval-given'}}
+    {{i18n 'code_review.approval_given'}}
+  {{/link-to}}
+</li>
+<li>
+  {{#link-to 'userActivity.approval-pending'}}
+    {{i18n 'code_review.approval_pending'}}
+  {{/link-to}}
+</li>
diff --git a/assets/javascripts/discourse/widgets/code-review-commit-approved-notification-item.js.es6 b/assets/javascripts/discourse/widgets/code-review-commit-approved-notification-item.js.es6
new file mode 100644
index 0000000..36db2c2
--- /dev/null
+++ b/assets/javascripts/discourse/widgets/code-review-commit-approved-notification-item.js.es6
@@ -0,0 +1,21 @@
+import { createWidgetFrom } from "discourse/widgets/widget";
+import { DefaultNotificationItem } from "discourse/widgets/default-notification-item";
+import { replaceIcon } from "discourse-common/lib/icon-library";
+
+replaceIcon("notification.code_review_commit_approved", "check");
+
+createWidgetFrom(DefaultNotificationItem, "code-review-commit-approved-notification-item", {
+  title() {
+    return I18n.t("notifications.code_review.commit_approved.title");
+  },
+
+  text(notificationName, data) {
+    const num_approved_commits = data.num_approved_commits;
+
+    if (num_approved_commits == 1) {
+      return I18n.t("notifications.code_review.commit_approved.one");
+    } else {
+      return I18n.t("notifications.code_review.commit_approved.many", { num_approved_commits });
+    }
+  }
+});
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index fcf3e31..4d1d993 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -4,6 +4,12 @@ en:
       approved: "Approved"
       followup: "Follow Up"
       followed_up: "Followed Up"
+    notifications:
+      code_review:
+        commit_approved:
+          one: "Your commit was approved"
+          many: "{{num_approved_commits}} of your commits were approved"
+          title: "commit approved"
     code_review:
       approve:
         title: "Approve commit"
@@ -11,3 +17,5 @@ en:
       followup:
         title: "Follow up commit"
         label: "Follow Up"
+      approval_given: "Approval Given"
+      approval_pending: "Approval Pending"
diff --git a/plugin.rb b/plugin.rb
index aacc5b1..10ecbf7 100644
--- a/plugin.rb
+++ b/plugin.rb
@@ -121,6 +121,9 @@ after_initialize do
   end
 
   Discourse::Application.routes.append do
+    get '/topics/approval-given/:username' => 'list#approval_given', as: :topics_approval_given
+    get '/topics/approval-pending/:username' => 'list#approval_pending', as: :topics_approval_pending
+
     mount ::DiscourseCodeReview::Engine, at: '/code-review'
   end
 
@@ -136,6 +139,24 @@ after_initialize do
       doc = DiscourseCodeReview::Importer.new(nil).auto_link_commits(post.raw, doc)[2]
     end
   end
+
+  add_to_class(:list_controller, :approval_given) do
+    respond_with_list(
+      TopicQuery.new(
+        current_user,
+        tags: [SiteSetting.code_review_approved_tag]
+      ).list_topics_by(current_user)
+    )
+  end
+
+  add_to_class(:list_controller, :approval_pending) do
+    respond_with_list(
+      TopicQuery.new(
+        current_user,
+        tags: [SiteSetting.code_review_pending_tag]
+      ).list_topics_by(current_user)
+    )
+  end
 end
 
 Rake::Task.define_task code_review_delete_user_github_access_tokens: :environment do
diff --git a/spec/requests/discourse_code_review/code_review_controller_spec.rb b/spec/requests/discourse_code_review/code_review_controller_spec.rb
index 8e224ef..6b020ca 100644
--- a/spec/requests/discourse_code_review/code_review_controller_spec.rb
+++ b/spec/requests/discourse_code_review/code_review_controller_spec.rb
@@ -3,34 +3,33 @@

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

GitHub sha: 4fe35760

1 Like