Change followers page to relationships page in admin UI (#12927)
Allow browsing and filtering all relationships instead of just followers, unify the codebase with the user-facing relationship manager, add ability to see who the user invited
This commit is contained in:
		
							parent
							
								
									f5236fe95a
								
							
						
					
					
						commit
						67172aa4f9
					
				
					 9 changed files with 181 additions and 94 deletions
				
			
		|  | @ -1,18 +0,0 @@ | ||||||
| # frozen_string_literal: true |  | ||||||
| 
 |  | ||||||
| module Admin |  | ||||||
|   class FollowersController < BaseController |  | ||||||
|     before_action :set_account |  | ||||||
| 
 |  | ||||||
|     PER_PAGE = 40 |  | ||||||
| 
 |  | ||||||
|     def index |  | ||||||
|       authorize :account, :index? |  | ||||||
|       @followers = @account.followers.local.recent.page(params[:page]).per(PER_PAGE) |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     def set_account |  | ||||||
|       @account = Account.find(params[:account_id]) |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| end |  | ||||||
							
								
								
									
										25
									
								
								app/controllers/admin/relationships_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								app/controllers/admin/relationships_controller.rb
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | ||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | module Admin | ||||||
|  |   class RelationshipsController < BaseController | ||||||
|  |     before_action :set_account | ||||||
|  | 
 | ||||||
|  |     PER_PAGE = 40 | ||||||
|  | 
 | ||||||
|  |     def index | ||||||
|  |       authorize :account, :index? | ||||||
|  | 
 | ||||||
|  |       @accounts = RelationshipFilter.new(@account, filter_params).results.page(params[:page]).per(PER_PAGE) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     private | ||||||
|  | 
 | ||||||
|  |     def set_account | ||||||
|  |       @account = Account.find(params[:account_id]) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     def filter_params | ||||||
|  |       params.slice(RelationshipFilter::KEYS).permit(RelationshipFilter::KEYS) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -19,53 +19,13 @@ class RelationshipsController < ApplicationController | ||||||
|   rescue ActionController::ParameterMissing |   rescue ActionController::ParameterMissing | ||||||
|     # Do nothing |     # Do nothing | ||||||
|   ensure |   ensure | ||||||
|     redirect_to relationships_path(current_params) |     redirect_to relationships_path(filter_params) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   private |   private | ||||||
| 
 | 
 | ||||||
|   def set_accounts |   def set_accounts | ||||||
|     @accounts = relationships_scope.page(params[:page]).per(40) |     @accounts = RelationshipFilter.new(current_account, filter_params).results.page(params[:page]).per(40) | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   def relationships_scope |  | ||||||
|     scope = begin |  | ||||||
|       if following_relationship? |  | ||||||
|         current_account.following.eager_load(:account_stat).reorder(nil) |  | ||||||
|       else |  | ||||||
|         current_account.followers.eager_load(:account_stat).reorder(nil) |  | ||||||
|       end |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     scope.merge!(Follow.recent)             if params[:order].blank? || params[:order] == 'recent' |  | ||||||
|     scope.merge!(Account.by_recent_status)  if params[:order] == 'active' |  | ||||||
|     scope.merge!(mutual_relationship_scope) if mutual_relationship? |  | ||||||
|     scope.merge!(moved_account_scope)       if params[:status] == 'moved' |  | ||||||
|     scope.merge!(primary_account_scope)     if params[:status] == 'primary' |  | ||||||
|     scope.merge!(by_domain_scope)           if params[:by_domain].present? |  | ||||||
|     scope.merge!(dormant_account_scope)     if params[:activity] == 'dormant' |  | ||||||
| 
 |  | ||||||
|     scope |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   def mutual_relationship_scope |  | ||||||
|     Account.where(id: current_account.following) |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   def moved_account_scope |  | ||||||
|     Account.where.not(moved_to_account_id: nil) |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   def primary_account_scope |  | ||||||
|     Account.where(moved_to_account_id: nil) |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   def dormant_account_scope |  | ||||||
|     AccountStat.where(last_status_at: nil).or(AccountStat.where(AccountStat.arel_table[:last_status_at].lt(1.month.ago))) |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   def by_domain_scope |  | ||||||
|     Account.where(domain: params[:by_domain]) |  | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def form_account_batch_params |   def form_account_batch_params | ||||||
|  | @ -84,7 +44,7 @@ class RelationshipsController < ApplicationController | ||||||
|     params[:relationship] == 'followed_by' |     params[:relationship] == 'followed_by' | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def current_params |   def filter_params | ||||||
|     params.slice(:page, *RelationshipFilter::KEYS).permit(:page, *RelationshipFilter::KEYS) |     params.slice(:page, *RelationshipFilter::KEYS).permit(:page, *RelationshipFilter::KEYS) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,5 +7,114 @@ class RelationshipFilter | ||||||
|     by_domain |     by_domain | ||||||
|     activity |     activity | ||||||
|     order |     order | ||||||
|  |     location | ||||||
|   ).freeze |   ).freeze | ||||||
|  | 
 | ||||||
|  |   attr_reader :params, :account | ||||||
|  | 
 | ||||||
|  |   def initialize(account, params) | ||||||
|  |     @account = account | ||||||
|  |     @params  = params | ||||||
|  | 
 | ||||||
|  |     set_defaults! | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def results | ||||||
|  |     scope = scope_for('relationship', params['relationship']) | ||||||
|  | 
 | ||||||
|  |     params.each do |key, value| | ||||||
|  |       next if key.to_s == 'page' | ||||||
|  | 
 | ||||||
|  |       scope.merge!(scope_for(key, value)) if value.present? | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     scope | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   private | ||||||
|  | 
 | ||||||
|  |   def set_defaults! | ||||||
|  |     params['relationship'] = 'following' if params['relationship'].blank? | ||||||
|  |     params['order']        = 'recent' if params['order'].blank? | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def scope_for(key, value) | ||||||
|  |     case key.to_s | ||||||
|  |     when 'relationship' | ||||||
|  |       relationship_scope(value) | ||||||
|  |     when 'by_domain' | ||||||
|  |       by_domain_scope(value) | ||||||
|  |     when 'location' | ||||||
|  |       location_scope(value) | ||||||
|  |     when 'status' | ||||||
|  |       status_scope(value) | ||||||
|  |     when 'order' | ||||||
|  |       order_scope(value) | ||||||
|  |     when 'activity' | ||||||
|  |       activity_scope(value) | ||||||
|  |     else | ||||||
|  |       raise "Unknown filter: #{key}" | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def relationship_scope(value) | ||||||
|  |     case value.to_s | ||||||
|  |     when 'following' | ||||||
|  |       account.following.eager_load(:account_stat).reorder(nil) | ||||||
|  |     when 'followed_by' | ||||||
|  |       account.followers.eager_load(:account_stat).reorder(nil) | ||||||
|  |     when 'mutual' | ||||||
|  |       account.followers.eager_load(:account_stat).reorder(nil).merge(Account.where(id: account.following)) | ||||||
|  |     when 'invited' | ||||||
|  |       Account.joins(user: :invite).merge(Invite.where(user: account.user)).eager_load(:account_stat).reorder(nil) | ||||||
|  |     else | ||||||
|  |       raise "Unknown relationship: #{value}" | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def by_domain_scope(value) | ||||||
|  |     Account.where(domain: value.to_s) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def location_scope(value) | ||||||
|  |     case value.to_s | ||||||
|  |     when 'local' | ||||||
|  |       Account.local | ||||||
|  |     when 'remote' | ||||||
|  |       Account.remote | ||||||
|  |     else | ||||||
|  |       raise "Unknown location: #{value}" | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def status_scope(value) | ||||||
|  |     case value.to_s | ||||||
|  |     when 'moved' | ||||||
|  |       Account.where.not(moved_to_account_id: nil) | ||||||
|  |     when 'primary' | ||||||
|  |       Account.where(moved_to_account_id: nil) | ||||||
|  |     else | ||||||
|  |       raise "Unknown status: #{value}" | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def order_scope(value) | ||||||
|  |     case value.to_s | ||||||
|  |     when 'active' | ||||||
|  |       Account.by_recent_status | ||||||
|  |     when 'recent' | ||||||
|  |       Follow.recent | ||||||
|  |     else | ||||||
|  |       raise "Unknown order: #{value}" | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def activity_scope(value) | ||||||
|  |     case value.to_s | ||||||
|  |     when 'dormant' | ||||||
|  |       AccountStat.where(last_status_at: nil).or(AccountStat.where(AccountStat.arel_table[:last_status_at].lt(1.month.ago))) | ||||||
|  |     else | ||||||
|  |       raise "Unknown activity: #{value}" | ||||||
|  |     end | ||||||
|  |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -41,7 +41,7 @@ | ||||||
|       .dashboard__counters__num= number_to_human_size @account.media_attachments.sum('file_file_size') |       .dashboard__counters__num= number_to_human_size @account.media_attachments.sum('file_file_size') | ||||||
|       .dashboard__counters__label= t 'admin.accounts.media_attachments' |       .dashboard__counters__label= t 'admin.accounts.media_attachments' | ||||||
|   %div |   %div | ||||||
|     = link_to admin_account_followers_path(@account.id) do |     = link_to admin_account_relationships_path(@account.id, location: 'local') do | ||||||
|       .dashboard__counters__num= number_with_delimiter @account.local_followers_count |       .dashboard__counters__num= number_with_delimiter @account.local_followers_count | ||||||
|       .dashboard__counters__label= t 'admin.accounts.followers' |       .dashboard__counters__label= t 'admin.accounts.followers' | ||||||
|   %div |   %div | ||||||
|  |  | ||||||
|  | @ -1,28 +0,0 @@ | ||||||
| - content_for :page_title do |  | ||||||
|   = t('admin.followers.title', acct: @account.acct) |  | ||||||
| 
 |  | ||||||
| .filters |  | ||||||
|   .filter-subset |  | ||||||
|     %strong= t('admin.accounts.location.title') |  | ||||||
|     %ul |  | ||||||
|       %li= link_to t('admin.accounts.location.local'), admin_account_followers_path(@account.id), class: 'selected' |  | ||||||
|   .back-link{ style: 'flex: 1 1 auto; text-align: right' } |  | ||||||
|     = link_to admin_account_path(@account.id) do |  | ||||||
|       = fa_icon 'chevron-left fw' |  | ||||||
|       = t('admin.followers.back_to_account') |  | ||||||
| 
 |  | ||||||
| %hr.spacer/ |  | ||||||
| 
 |  | ||||||
| .table-wrapper |  | ||||||
|   %table.table |  | ||||||
|     %thead |  | ||||||
|       %tr |  | ||||||
|         %th= t('admin.accounts.username') |  | ||||||
|         %th= t('admin.accounts.role') |  | ||||||
|         %th= t('admin.accounts.most_recent_ip') |  | ||||||
|         %th= t('admin.accounts.most_recent_activity') |  | ||||||
|         %th |  | ||||||
|     %tbody |  | ||||||
|       = render partial: 'admin/accounts/account', collection: @followers |  | ||||||
| 
 |  | ||||||
| = paginate @followers |  | ||||||
							
								
								
									
										39
									
								
								app/views/admin/relationships/index.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								app/views/admin/relationships/index.html.haml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | ||||||
|  | - content_for :page_title do | ||||||
|  |   = t('admin.relationships.title', acct: @account.acct) | ||||||
|  | 
 | ||||||
|  | .filters | ||||||
|  |   .filter-subset | ||||||
|  |     %strong= t 'relationships.relationship' | ||||||
|  |     %ul | ||||||
|  |       %li= filter_link_to t('relationships.following'), relationship: nil | ||||||
|  |       %li= filter_link_to t('relationships.followers'), relationship: 'followed_by' | ||||||
|  |       %li= filter_link_to t('relationships.mutual'), relationship: 'mutual' | ||||||
|  |       %li= filter_link_to t('relationships.invited'), relationship: 'invited' | ||||||
|  | 
 | ||||||
|  |   .filter-subset | ||||||
|  |     %strong= t('admin.accounts.location.title') | ||||||
|  |     %ul | ||||||
|  |       %li= filter_link_to t('admin.accounts.moderation.all'), location: nil | ||||||
|  |       %li= filter_link_to t('admin.accounts.location.local'), location: 'local' | ||||||
|  |       %li= filter_link_to t('admin.accounts.location.remote'), location: 'remote' | ||||||
|  | 
 | ||||||
|  |   .back-link{ style: 'flex: 1 1 auto; text-align: right' } | ||||||
|  |     = link_to admin_account_path(@account.id) do | ||||||
|  |       = fa_icon 'chevron-left fw' | ||||||
|  |       = t('admin.statuses.back_to_account') | ||||||
|  | 
 | ||||||
|  | %hr.spacer/ | ||||||
|  | 
 | ||||||
|  | .table-wrapper | ||||||
|  |   %table.table | ||||||
|  |     %thead | ||||||
|  |       %tr | ||||||
|  |         %th= t('admin.accounts.username') | ||||||
|  |         %th= t('admin.accounts.role') | ||||||
|  |         %th= t('admin.accounts.most_recent_ip') | ||||||
|  |         %th= t('admin.accounts.most_recent_activity') | ||||||
|  |         %th | ||||||
|  |     %tbody | ||||||
|  |       = render partial: 'admin/accounts/account', collection: @accounts | ||||||
|  | 
 | ||||||
|  | = paginate @accounts | ||||||
|  | @ -344,9 +344,6 @@ en: | ||||||
|         create: Add domain |         create: Add domain | ||||||
|         title: New e-mail blacklist entry |         title: New e-mail blacklist entry | ||||||
|       title: E-mail blacklist |       title: E-mail blacklist | ||||||
|     followers: |  | ||||||
|       back_to_account: Back To Account |  | ||||||
|       title: "%{acct}'s Followers" |  | ||||||
|     instances: |     instances: | ||||||
|       by_domain: Domain |       by_domain: Domain | ||||||
|       delivery_available: Delivery is available |       delivery_available: Delivery is available | ||||||
|  | @ -375,6 +372,8 @@ en: | ||||||
|       title: Invites |       title: Invites | ||||||
|     pending_accounts: |     pending_accounts: | ||||||
|       title: Pending accounts (%{count}) |       title: Pending accounts (%{count}) | ||||||
|  |     relationships: | ||||||
|  |       title: "%{acct}'s relationships" | ||||||
|     relays: |     relays: | ||||||
|       add_new: Add new relay |       add_new: Add new relay | ||||||
|       delete: Delete |       delete: Delete | ||||||
|  | @ -935,6 +934,7 @@ en: | ||||||
|     dormant: Dormant |     dormant: Dormant | ||||||
|     followers: Followers |     followers: Followers | ||||||
|     following: Following |     following: Following | ||||||
|  |     invited: Invited | ||||||
|     last_active: Last active |     last_active: Last active | ||||||
|     most_recent: Most recent |     most_recent: Most recent | ||||||
|     moved: Moved |     moved: Moved | ||||||
|  |  | ||||||
|  | @ -223,7 +223,7 @@ Rails.application.routes.draw do | ||||||
|       resource :reset, only: [:create] |       resource :reset, only: [:create] | ||||||
|       resource :action, only: [:new, :create], controller: 'account_actions' |       resource :action, only: [:new, :create], controller: 'account_actions' | ||||||
|       resources :statuses, only: [:index, :show, :create, :update, :destroy] |       resources :statuses, only: [:index, :show, :create, :update, :destroy] | ||||||
|       resources :followers, only: [:index] |       resources :relationships, only: [:index] | ||||||
| 
 | 
 | ||||||
|       resource :confirmation, only: [:create] do |       resource :confirmation, only: [:create] do | ||||||
|         collection do |         collection do | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue