diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb
index 46c9aba91..40bf685c5 100644
--- a/app/controllers/admin/accounts_controller.rb
+++ b/app/controllers/admin/accounts_controller.rb
@@ -16,7 +16,11 @@ module Admin
def batch
authorize :account, :index?
- @form = Form::AccountBatch.new(form_account_batch_params.merge(current_account: current_account, action: action_from_button))
+ @form = Form::AccountBatch.new(form_account_batch_params)
+ @form.current_account = current_account
+ @form.action = action_from_button
+ @form.select_all_matching = params[:select_all_matching]
+ @form.query = filtered_accounts
@form.save
rescue ActionController::ParameterMissing
flash[:alert] = I18n.t('admin.accounts.no_account_selected')
diff --git a/app/javascript/packs/admin.js b/app/javascript/packs/admin.js
index a3ed1ffed..b733d6b18 100644
--- a/app/javascript/packs/admin.js
+++ b/app/javascript/packs/admin.js
@@ -4,18 +4,71 @@ import ready from '../mastodon/ready';
const batchCheckboxClassName = '.batch-checkbox input[type="checkbox"]';
+const showSelectAll = () => {
+ const selectAllMatchingElement = document.querySelector('.batch-table__select-all');
+ selectAllMatchingElement.classList.add('active');
+};
+
+const hideSelectAll = () => {
+ const selectAllMatchingElement = document.querySelector('.batch-table__select-all');
+ const hiddenField = document.querySelector('#select_all_matching');
+ const selectedMsg = document.querySelector('.batch-table__select-all .selected');
+ const notSelectedMsg = document.querySelector('.batch-table__select-all .not-selected');
+
+ selectAllMatchingElement.classList.remove('active');
+ selectedMsg.classList.remove('active');
+ notSelectedMsg.classList.add('active');
+ hiddenField.value = '0';
+};
+
delegate(document, '#batch_checkbox_all', 'change', ({ target }) => {
+ const selectAllMatchingElement = document.querySelector('.batch-table__select-all');
+
[].forEach.call(document.querySelectorAll(batchCheckboxClassName), (content) => {
content.checked = target.checked;
});
+
+ if (selectAllMatchingElement) {
+ if (target.checked) {
+ showSelectAll();
+ } else {
+ hideSelectAll();
+ }
+ }
+});
+
+delegate(document, '.batch-table__select-all button', 'click', () => {
+ const hiddenField = document.querySelector('#select_all_matching');
+ const active = hiddenField.value === '1';
+ const selectedMsg = document.querySelector('.batch-table__select-all .selected');
+ const notSelectedMsg = document.querySelector('.batch-table__select-all .not-selected');
+
+ if (active) {
+ hiddenField.value = '0';
+ selectedMsg.classList.remove('active');
+ notSelectedMsg.classList.add('active');
+ } else {
+ hiddenField.value = '1';
+ notSelectedMsg.classList.remove('active');
+ selectedMsg.classList.add('active');
+ }
});
delegate(document, batchCheckboxClassName, 'change', () => {
const checkAllElement = document.querySelector('#batch_checkbox_all');
+ const selectAllMatchingElement = document.querySelector('.batch-table__select-all');
if (checkAllElement) {
checkAllElement.checked = [].every.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked);
checkAllElement.indeterminate = !checkAllElement.checked && [].some.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked);
+
+ if (selectAllMatchingElement) {
+ if (checkAllElement.checked) {
+ showSelectAll();
+ } else {
+ hideSelectAll();
+ }
+ }
}
});
diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss
index 431b8a73a..39211910f 100644
--- a/app/javascript/styles/mastodon/tables.scss
+++ b/app/javascript/styles/mastodon/tables.scss
@@ -190,6 +190,55 @@ a.table-action-link {
}
}
+ &__select-all {
+ background: $ui-base-color;
+ height: 47px;
+ align-items: center;
+ justify-content: center;
+ border: 1px solid darken($ui-base-color, 8%);
+ border-top: 0;
+ color: $secondary-text-color;
+ display: none;
+
+ &.active {
+ display: flex;
+ }
+
+ .selected,
+ .not-selected {
+ display: none;
+
+ &.active {
+ display: block;
+ }
+ }
+
+ strong {
+ font-weight: 700;
+ }
+
+ span {
+ padding: 8px;
+ display: inline-block;
+ }
+
+ button {
+ background: transparent;
+ border: 0;
+ font: inherit;
+ color: $highlight-text-color;
+ border-radius: 4px;
+ font-weight: 700;
+ padding: 8px;
+
+ &:hover,
+ &:focus,
+ &:active {
+ background: lighten($ui-base-color, 8%);
+ }
+ }
+ }
+
&__form {
padding: 16px;
border: 1px solid darken($ui-base-color, 8%);
diff --git a/app/models/custom_filter_status.rb b/app/models/custom_filter_status.rb
index b6bea1394..e748d6963 100644
--- a/app/models/custom_filter_status.rb
+++ b/app/models/custom_filter_status.rb
@@ -5,7 +5,7 @@
#
# id :bigint(8) not null, primary key
# custom_filter_id :bigint(8) not null
-# status_id :bigint(8) default(""), not null
+# status_id :bigint(8) not null
# created_at :datetime not null
# updated_at :datetime not null
#
diff --git a/app/models/form/account_batch.rb b/app/models/form/account_batch.rb
index 98f2cad3e..5cfcf7205 100644
--- a/app/models/form/account_batch.rb
+++ b/app/models/form/account_batch.rb
@@ -6,7 +6,8 @@ class Form::AccountBatch
include AccountableConcern
include Payloadable
- attr_accessor :account_ids, :action, :current_account
+ attr_accessor :account_ids, :action, :current_account,
+ :select_all_matching, :query
def save
case action
@@ -60,7 +61,11 @@ class Form::AccountBatch
end
def accounts
- Account.where(id: account_ids)
+ if select_all_matching?
+ query
+ else
+ Account.where(id: account_ids)
+ end
end
def approve!
@@ -118,4 +123,8 @@ class Form::AccountBatch
log_action(:approve, account.user)
account.user.approve!
end
+
+ def select_all_matching?
+ select_all_matching == '1'
+ end
end
diff --git a/app/views/admin/accounts/index.html.haml b/app/views/admin/accounts/index.html.haml
index cb378f0ed..670a09a2d 100644
--- a/app/views/admin/accounts/index.html.haml
+++ b/app/views/admin/accounts/index.html.haml
@@ -37,6 +37,7 @@
= form_for(@form, url: batch_admin_accounts_path) do |f|
= hidden_field_tag :page, params[:page] || 1
+ = hidden_field_tag :select_all_matching, '0'
- AccountFilter::KEYS.each do |key|
= hidden_field_tag key, params[key] if params[key].present?
@@ -52,6 +53,14 @@
= f.button safe_join([fa_icon('times'), t('admin.accounts.reject')]), name: :reject, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
= f.button safe_join([fa_icon('lock'), t('admin.accounts.perform_full_suspension')]), name: :suspend, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
+ - if true || @accounts.total_count > @accounts.size
+ .batch-table__select-all
+ .not-selected.active
+ %span= t('generic.all_items_on_page_selected_html', count: @accounts.size)
+ %button{ type: 'button' }= t('generic.select_all_matching_items', count: @accounts.total_count)
+ .selected
+ %span= t('generic.all_matching_items_selected_html', count: @accounts.total_count)
+ %button{ type: 'button' }= t('generic.deselect')
.batch-table__body
- if @accounts.empty?
= nothing_here 'nothing-here--under-tabs'
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 5c309ab11..6aa87e4a0 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -1227,12 +1227,22 @@ en:
trending_now: Trending now
generic:
all: All
+ all_items_on_page_selected_html:
+ one: "%{count} item on this page is selected."
+ other: All %{count} items on this page are selected.
+ all_matching_items_selected_html:
+ one: "%{count} item matching your search is selected."
+ other: All %{count} items matching your search are selected.
changes_saved_msg: Changes successfully saved!
copy: Copy
delete: Delete
+ deselect: Deselect all
none: None
order_by: Order by
save_changes: Save changes
+ select_all_matching_items:
+ one: Select %{count} item matching your search.
+ other: Select all %{count} items matching your search.
today: today
validation_errors:
one: Something isn't quite right yet! Please review the error below