FIX: Revoking admin or moderator status doesn't require refresh to delete/anonymize/merge user (#14073)

FIX: Revoking admin or moderator status doesn’t require refresh to delete/anonymize/merge user (#14073)

  • FIX: Revoking admin or moderator status doesn’t require refresh to delete/anonymize/merge user

On the /admin/users// page, there are action buttons that are either visible or hidden depending on a few fields from the AdminDetailsSerializer: can_be_deleted, can_be_anonymized, can_be_merged, can_delete_all_posts.

These fields are updated when granting/revoking admin or moderator status. However, those updates were not being reflected on the page. E.g. if a user is granted moderation privileges, the ‘anonymize user’ and ‘merge’ buttons still appear on the page, which is inconsistent with the backend state of the user. It requires refreshing the page to update the state.

This commit fixes that issue, by syncing the client model state with the server state when handling a successful response from the server. Now, when revoking privileges, the buttons automatically appear without refreshing the page. Similarly, when granting moderator privileges, the buttons automatically disappear without refreshing the page.

  • Add detailed user response to spec for changed routes.

Add tests to verify that the revoke_moderation, grant_moderation, and revoke_admin routes return a response formatted according to the AdminDetailedUserSerializer.

diff --git a/app/assets/javascripts/admin/addon/models/admin-user.js b/app/assets/javascripts/admin/addon/models/admin-user.js
index cbcdcc2..cfd34b8 100644
--- a/app/assets/javascripts/admin/addon/models/admin-user.js
+++ b/app/assets/javascripts/admin/addon/models/admin-user.js
@@ -86,11 +86,15 @@ const AdminUser = User.extend({
   revokeAdmin() {
     return ajax(`/admin/users/${this.id}/revoke_admin`, {
       type: "PUT",
-    }).then(() => {
+    }).then((resp) => {
       this.setProperties({
         admin: false,
         can_grant_admin: true,
         can_revoke_admin: false,
+        can_be_merged: resp.can_be_merged,
+        can_be_anonymized: resp.can_be_anonymized,
+        can_be_deleted: resp.can_be_deleted,
+        can_delete_all_posts: resp.can_delete_all_posts,
       });
     });
   },
@@ -105,11 +109,13 @@ const AdminUser = User.extend({
     return ajax(`/admin/users/${this.id}/revoke_moderation`, {
       type: "PUT",
     })
-      .then(() => {
+      .then((resp) => {
         this.setProperties({
           moderator: false,
           can_grant_moderation: true,
           can_revoke_moderation: false,
+          can_be_merged: resp.can_be_merged,
+          can_be_anonymized: resp.can_be_anonymized,
         });
       })
       .catch(popupAjaxError);
@@ -119,11 +125,13 @@ const AdminUser = User.extend({
     return ajax(`/admin/users/${this.id}/grant_moderation`, {
       type: "PUT",
     })
-      .then(() => {
+      .then((resp) => {
         this.setProperties({
           moderator: true,
           can_grant_moderation: false,
           can_revoke_moderation: true,
+          can_be_merged: resp.can_be_merged,
+          can_be_anonymized: resp.can_be_anonymized,
         });
       })
       .catch(popupAjaxError);
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 2fb4469..cd8b2f7 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -187,7 +187,7 @@ class Admin::UsersController < Admin::AdminController
     guardian.ensure_can_revoke_admin!(@user)
     @user.revoke_admin!
     StaffActionLogger.new(current_user).log_revoke_admin(@user)
-    render body: nil
+    render_serialized(@user, AdminDetailedUserSerializer, root: false)
   end
 
   def grant_admin
@@ -199,14 +199,14 @@ class Admin::UsersController < Admin::AdminController
     guardian.ensure_can_revoke_moderation!(@user)
     @user.revoke_moderation!
     StaffActionLogger.new(current_user).log_revoke_moderation(@user)
-    render body: nil
+    render_serialized(@user, AdminDetailedUserSerializer, root: false)
   end
 
   def grant_moderation
     guardian.ensure_can_grant_moderation!(@user)
     @user.grant_moderation!
     StaffActionLogger.new(current_user).log_grant_moderation(@user)
-    render_serialized(@user, AdminUserSerializer)
+    render_serialized(@user, AdminDetailedUserSerializer, root: false)
   end
 
   def add_group
diff --git a/spec/requests/admin/users_controller_spec.rb b/spec/requests/admin/users_controller_spec.rb
index 6d7a7b9..d32b6be 100644
--- a/spec/requests/admin/users_controller_spec.rb
+++ b/spec/requests/admin/users_controller_spec.rb
@@ -327,6 +327,14 @@ RSpec.describe Admin::UsersController do
       another_admin.reload
       expect(another_admin.admin).to eq(false)
     end
+
+    it 'returns detailed user schema' do
+      put "/admin/users/#{another_admin.id}/revoke_admin.json"
+      expect(response.parsed_body['can_be_merged']).to eq(true)
+      expect(response.parsed_body['can_be_deleted']).to eq(true)
+      expect(response.parsed_body['can_be_anonymized']).to eq(true)
+      expect(response.parsed_body['can_delete_all_posts']).to eq(true)
+    end
   end
 
   describe '#grant_admin' do
@@ -505,6 +513,12 @@ RSpec.describe Admin::UsersController do
       another_user.reload
       expect(another_user.moderator).to eq(true)
     end
+
+    it 'returns detailed user schema' do
+      put "/admin/users/#{another_user.id}/grant_moderation.json"
+      expect(response.parsed_body['can_be_merged']).to eq(false)
+      expect(response.parsed_body['can_be_anonymized']).to eq(false)
+    end
   end
 
   describe '#revoke_moderation' do
@@ -524,6 +538,12 @@ RSpec.describe Admin::UsersController do
       moderator.reload
       expect(moderator.moderator).to eq(false)
     end
+
+    it 'returns detailed user schema' do
+      put "/admin/users/#{moderator.id}/revoke_moderation.json"
+      expect(response.parsed_body['can_be_merged']).to eq(true)
+      expect(response.parsed_body['can_be_anonymized']).to eq(true)
+    end
   end
 
   describe '#primary_group' do

GitHub sha: 64ead3c3a1333028c03429aa4273df79d6225c5c

This commit appears in #14073 which was approved by tgxworld. It was merged by tgxworld.