Add ability to select all accounts matching search for batch actions (#19053)

rebase/4.0.0rc1
Eugen Rochko 2022-08-25 23:33:34 +02:00 committed by GitHub
parent d696f729f1
commit 5b0e8cc92b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 138 additions and 4 deletions

View File

@ -16,7 +16,11 @@ module Admin
def batch def batch
authorize :account, :index? 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 @form.save
rescue ActionController::ParameterMissing rescue ActionController::ParameterMissing
flash[:alert] = I18n.t('admin.accounts.no_account_selected') flash[:alert] = I18n.t('admin.accounts.no_account_selected')

View File

@ -4,18 +4,71 @@ import ready from '../mastodon/ready';
const batchCheckboxClassName = '.batch-checkbox input[type="checkbox"]'; 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 }) => { delegate(document, '#batch_checkbox_all', 'change', ({ target }) => {
const selectAllMatchingElement = document.querySelector('.batch-table__select-all');
[].forEach.call(document.querySelectorAll(batchCheckboxClassName), (content) => { [].forEach.call(document.querySelectorAll(batchCheckboxClassName), (content) => {
content.checked = target.checked; 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', () => { delegate(document, batchCheckboxClassName, 'change', () => {
const checkAllElement = document.querySelector('#batch_checkbox_all'); const checkAllElement = document.querySelector('#batch_checkbox_all');
const selectAllMatchingElement = document.querySelector('.batch-table__select-all');
if (checkAllElement) { if (checkAllElement) {
checkAllElement.checked = [].every.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked); checkAllElement.checked = [].every.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked);
checkAllElement.indeterminate = !checkAllElement.checked && [].some.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();
}
}
} }
}); });

View File

@ -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 { &__form {
padding: 16px; padding: 16px;
border: 1px solid darken($ui-base-color, 8%); border: 1px solid darken($ui-base-color, 8%);

View File

@ -5,7 +5,7 @@
# #
# id :bigint(8) not null, primary key # id :bigint(8) not null, primary key
# custom_filter_id :bigint(8) not null # 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 # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# #

View File

@ -6,7 +6,8 @@ class Form::AccountBatch
include AccountableConcern include AccountableConcern
include Payloadable include Payloadable
attr_accessor :account_ids, :action, :current_account attr_accessor :account_ids, :action, :current_account,
:select_all_matching, :query
def save def save
case action case action
@ -60,7 +61,11 @@ class Form::AccountBatch
end end
def accounts def accounts
Account.where(id: account_ids) if select_all_matching?
query
else
Account.where(id: account_ids)
end
end end
def approve! def approve!
@ -118,4 +123,8 @@ class Form::AccountBatch
log_action(:approve, account.user) log_action(:approve, account.user)
account.user.approve! account.user.approve!
end end
def select_all_matching?
select_all_matching == '1'
end
end end

View File

@ -37,6 +37,7 @@
= form_for(@form, url: batch_admin_accounts_path) do |f| = form_for(@form, url: batch_admin_accounts_path) do |f|
= hidden_field_tag :page, params[:page] || 1 = hidden_field_tag :page, params[:page] || 1
= hidden_field_tag :select_all_matching, '0'
- AccountFilter::KEYS.each do |key| - AccountFilter::KEYS.each do |key|
= hidden_field_tag key, params[key] if params[key].present? = 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('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') } = 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 .batch-table__body
- if @accounts.empty? - if @accounts.empty?
= nothing_here 'nothing-here--under-tabs' = nothing_here 'nothing-here--under-tabs'

View File

@ -1227,12 +1227,22 @@ en:
trending_now: Trending now trending_now: Trending now
generic: generic:
all: All all: All
all_items_on_page_selected_html:
one: "<strong>%{count}</strong> item on this page is selected."
other: All <strong>%{count}</strong> items on this page are selected.
all_matching_items_selected_html:
one: "<strong>%{count}</strong> item matching your search is selected."
other: All <strong>%{count}</strong> items matching your search are selected.
changes_saved_msg: Changes successfully saved! changes_saved_msg: Changes successfully saved!
copy: Copy copy: Copy
delete: Delete delete: Delete
deselect: Deselect all
none: None none: None
order_by: Order by order_by: Order by
save_changes: Save changes 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 today: today
validation_errors: validation_errors:
one: Something isn't quite right yet! Please review the error below one: Something isn't quite right yet! Please review the error below