FIX: Make favorite work with multiple grant badges (#13492)

FIX: Make favorite work with multiple grant badges (#13492)

Badges that are awarded multiple times can be favorite and not favorite at the same time. This caused few problems when users tried to favorite them as they were counted multiple times or their state was incorrectly displayed.

diff --git a/app/controllers/user_badges_controller.rb b/app/controllers/user_badges_controller.rb
index 42f298b..ff13086 100644
--- a/app/controllers/user_badges_controller.rb
+++ b/app/controllers/user_badges_controller.rb
@@ -105,11 +105,13 @@ class UserBadgesController < ApplicationController
       return render json: failed_json, status: 403
     end
 
-    if !user_badge.is_favorite && user_badges.where(is_favorite: true).count >= SiteSetting.max_favorite_badges
+    if !user_badge.is_favorite && user_badges.select(:badge_id).distinct.where(is_favorite: true).count >= SiteSetting.max_favorite_badges
       return render json: failed_json, status: 400
     end
 
-    user_badge.toggle!(:is_favorite)
+    UserBadge
+      .where(user_id: user_badge.user_id, badge_id: user_badge.badge_id)
+      .update(is_favorite: !user_badge.is_favorite)
     UserBadge.update_featured_ranks!(user_badge.user_id)
     render_serialized(user_badge, DetailedUserBadgeSerializer, root: :user_badge)
   end
diff --git a/spec/requests/user_badges_controller_spec.rb b/spec/requests/user_badges_controller_spec.rb
index 9e68bbc..42ea307 100644
--- a/spec/requests/user_badges_controller_spec.rb
+++ b/spec/requests/user_badges_controller_spec.rb
@@ -309,5 +309,31 @@ describe UserBadgesController do
       user_badge = UserBadge.find_by(user: user, badge: badge)
       expect(user_badge.is_favorite).to be false
     end
+
+    it "works with multiple grants" do
+      SiteSetting.max_favorite_badges = 2
+
+      sign_in(user)
+
+      badge = Fabricate(:badge, multiple_grant: true)
+      user_badge = UserBadge.create(badge: badge, user: user, granted_by: Discourse.system_user, granted_at: Time.now, seq: 0, is_favorite: true)
+      user_badge2 = UserBadge.create(badge: badge, user: user, granted_by: Discourse.system_user, granted_at: Time.now, seq: 1, is_favorite: true)
+      other_badge = Fabricate(:badge)
+      other_user_badge = UserBadge.create(badge: other_badge, user: user, granted_by: Discourse.system_user, granted_at: Time.now)
+
+      put "/user_badges/#{user_badge.id}/toggle_favorite.json"
+      expect(response.status).to eq(200)
+      expect(user_badge.reload.is_favorite).to eq(false)
+      expect(user_badge2.reload.is_favorite).to eq(false)
+
+      put "/user_badges/#{user_badge.id}/toggle_favorite.json"
+      expect(response.status).to eq(200)
+      expect(user_badge.reload.is_favorite).to eq(true)
+      expect(user_badge2.reload.is_favorite).to eq(true)
+
+      put "/user_badges/#{other_user_badge.id}/toggle_favorite.json"
+      expect(response.status).to eq(200)
+      expect(other_user_badge.reload.is_favorite).to eq(true)
+    end
   end
 end

GitHub sha: a22aa7562a096647f939c1314b8c0f8f74e3e541

This commit appears in #13492 which was approved by ZogStriP. It was merged by nbianca.