about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorabcang <abcang1015@gmail.com>2017-07-18 23:38:22 +0900
committerEugen Rochko <eugen@zeonfederated.com>2017-07-18 16:38:22 +0200
commit4d42a389540690b32886f2a38af1f86aee617d27 (patch)
tree7764542d3e92a977444fb1de17d18cc4d364aa11 /app
parent8387b3928ec7658192907da79df65e65aaa8a7fc (diff)
Improve admin page (#4121)
* Improve admin page

* Fix test

* Add spec

* Improve select style
Diffstat (limited to 'app')
-rw-r--r--app/controllers/admin/reported_statuses_controller.rb15
-rw-r--r--app/controllers/admin/reports_controller.rb4
-rw-r--r--app/controllers/admin/statuses_controller.rb69
-rw-r--r--app/javascript/packs/admin.js40
-rw-r--r--app/javascript/styles/admin.scss45
-rw-r--r--app/models/form/status_batch.rb39
-rw-r--r--app/views/admin/accounts/show.html.haml4
-rw-r--r--app/views/admin/reports/show.html.haml34
-rw-r--r--app/views/admin/statuses/index.html.haml47
9 files changed, 280 insertions, 17 deletions
diff --git a/app/controllers/admin/reported_statuses_controller.rb b/app/controllers/admin/reported_statuses_controller.rb
index 32434d30f..5a31adecf 100644
--- a/app/controllers/admin/reported_statuses_controller.rb
+++ b/app/controllers/admin/reported_statuses_controller.rb
@@ -5,7 +5,14 @@ module Admin
     include Authorization
 
     before_action :set_report
-    before_action :set_status
+    before_action :set_status, only: [:update, :destroy]
+
+    def create
+      @form = Form::StatusBatch.new(form_status_batch_params)
+      flash[:alert] = t('admin.statuses.failed_to_execute') unless @form.save
+
+      redirect_to admin_report_path(@report)
+    end
 
     def update
       @status.update(status_params)
@@ -15,7 +22,7 @@ module Admin
     def destroy
       authorize @status, :destroy?
       RemovalWorker.perform_async(@status.id)
-      redirect_to admin_report_path(@report)
+      render json: @status
     end
 
     private
@@ -24,6 +31,10 @@ module Admin
       params.require(:status).permit(:sensitive)
     end
 
+    def form_status_batch_params
+      params.require(:form_status_batch).permit(:action, status_ids: [])
+    end
+
     def set_report
       @report = Report.find(params[:report_id])
     end
diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb
index 2d8c3c820..226467739 100644
--- a/app/controllers/admin/reports_controller.rb
+++ b/app/controllers/admin/reports_controller.rb
@@ -8,7 +8,9 @@ module Admin
       @reports = filtered_reports.page(params[:page])
     end
 
-    def show; end
+    def show
+      @form = Form::StatusBatch.new
+    end
 
     def update
       process_report
diff --git a/app/controllers/admin/statuses_controller.rb b/app/controllers/admin/statuses_controller.rb
new file mode 100644
index 000000000..50712f0dd
--- /dev/null
+++ b/app/controllers/admin/statuses_controller.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+module Admin
+  class StatusesController < BaseController
+    include Authorization
+
+    helper_method :current_params
+
+    before_action :set_account
+    before_action :set_status, only: [:update, :destroy]
+
+    PAR_PAGE = 20
+
+    def index
+      @statuses = @account.statuses
+      if params[:media]
+        account_media_status_ids = @account.media_attachments.attached.reorder(nil).select(:status_id).distinct
+        @statuses.merge!(Status.where(id: account_media_status_ids))
+      end
+      @statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PAR_PAGE)
+
+      @form = Form::StatusBatch.new
+    end
+
+    def create
+      @form = Form::StatusBatch.new(form_status_batch_params)
+      flash[:alert] = t('admin.statuses.failed_to_execute') unless @form.save
+
+      redirect_to admin_account_statuses_path(@account.id, current_params)
+    end
+
+    def update
+      @status.update(status_params)
+      redirect_to admin_account_statuses_path(@account.id, current_params)
+    end
+
+    def destroy
+      authorize @status, :destroy?
+      RemovalWorker.perform_async(@status.id)
+      render json: @status
+    end
+
+    private
+
+    def status_params
+      params.require(:status).permit(:sensitive)
+    end
+
+    def form_status_batch_params
+      params.require(:form_status_batch).permit(:action, status_ids: [])
+    end
+
+    def set_status
+      @status = @account.statuses.find(params[:id])
+    end
+
+    def set_account
+      @account = Account.find(params[:account_id])
+    end
+
+    def current_params
+      page = (params[:page] || 1).to_i
+      {
+        media: params[:media],
+        page: page > 1 && page,
+      }.select { |_, value| value.present? }
+    end
+  end
+end
diff --git a/app/javascript/packs/admin.js b/app/javascript/packs/admin.js
new file mode 100644
index 000000000..993827db5
--- /dev/null
+++ b/app/javascript/packs/admin.js
@@ -0,0 +1,40 @@
+import { delegate } from 'rails-ujs';
+
+function handleDeleteStatus(event) {
+  const [data] = event.detail;
+  const element = document.querySelector(`[data-id="${data.id}"]`);
+  if (element) {
+    element.parentNode.removeChild(element);
+  }
+}
+
+[].forEach.call(document.querySelectorAll('.trash-button'), (content) => {
+  content.addEventListener('ajax:success', handleDeleteStatus);
+});
+
+const batchCheckboxClassName = '.batch-checkbox input[type="checkbox"]';
+
+delegate(document, '#batch_checkbox_all', 'change', ({ target }) => {
+  [].forEach.call(document.querySelectorAll(batchCheckboxClassName), (content) => {
+    content.checked = target.checked;
+  });
+});
+
+delegate(document, batchCheckboxClassName, 'change', () => {
+  const checkAllElement = document.querySelector('#batch_checkbox_all');
+  if (checkAllElement) {
+    checkAllElement.checked = [].every.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked);
+  }
+});
+
+delegate(document, '.media-spoiler-show-button', 'click', () => {
+  [].forEach.call(document.querySelectorAll('.activity-stream .media-spoiler-wrapper'), (content) => {
+    content.classList.add('media-spoiler-wrapper__visible');
+  });
+});
+
+delegate(document, '.media-spoiler-hide-button', 'click', () => {
+  [].forEach.call(document.querySelectorAll('.activity-stream .media-spoiler-wrapper'), (content) => {
+    content.classList.remove('media-spoiler-wrapper__visible');
+  });
+});
diff --git a/app/javascript/styles/admin.scss b/app/javascript/styles/admin.scss
index 3bc713566..4c3bbdfc5 100644
--- a/app/javascript/styles/admin.scss
+++ b/app/javascript/styles/admin.scss
@@ -253,7 +253,8 @@
   }
 }
 
-.report-status {
+.report-status,
+.account-status {
   display: flex;
   margin-bottom: 10px;
 
@@ -263,7 +264,8 @@
   }
 }
 
-.report-status__actions {
+.report-status__actions,
+.account-status__actions {
   flex: 0 0 auto;
   display: flex;
   flex-direction: column;
@@ -275,3 +277,42 @@
     margin-bottom: 10px;
   }
 }
+
+.batch-form-box {
+  display: flex;
+  margin-bottom: 10px;
+
+  #form_status_batch_action {
+    margin-right: 5px;
+    font-size: 14px;
+  }
+
+  .media-spoiler-toggle-buttons {
+    margin-left: auto;
+
+    .button {
+      overflow: visible;
+    }
+  }
+}
+
+.batch-checkbox,
+.batch-checkbox-all {
+  display: flex;
+  align-items: center;
+  margin-right: 5px;
+}
+
+.back-link {
+  margin-bottom: 10px;
+  font-size: 14px;
+
+  a {
+    color: $classic-highlight-color;
+    text-decoration: none;
+
+    &:hover {
+      text-decoration: underline;
+    }
+  }
+}
diff --git a/app/models/form/status_batch.rb b/app/models/form/status_batch.rb
new file mode 100644
index 000000000..a97b4aa28
--- /dev/null
+++ b/app/models/form/status_batch.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+class Form::StatusBatch
+  include ActiveModel::Model
+
+  attr_accessor :status_ids, :action
+
+  ACTION_TYPE = %w(nsfw_on nsfw_off delete).freeze
+
+  def save
+    case action
+    when 'nsfw_on', 'nsfw_off'
+      change_sensitive(action == 'nsfw_on')
+    when 'delete'
+      delete_statuses
+    end
+  end
+
+  private
+
+  def change_sensitive(sensitive)
+    media_attached_status_ids = MediaAttachment.where(status_id: status_ids).pluck(:status_id)
+    ApplicationRecord.transaction do
+      Status.where(id: media_attached_status_ids).find_each do |status|
+        status.update!(sensitive: sensitive)
+      end
+    end
+    true
+  rescue ActiveRecord::RecordInvalid
+    false
+  end
+
+  def delete_statuses
+    Status.where(id: status_ids).find_each do |status|
+      RemovalWorker.perform_async(status.id)
+    end
+    true
+  end
+end
diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml
index d91ba9c78..5ad1fd6ee 100644
--- a/app/views/admin/accounts/show.html.haml
+++ b/app/views/admin/accounts/show.html.haml
@@ -53,11 +53,11 @@
       %td= @account.followers_count
     %tr
       %th= t('admin.accounts.statuses')
-      %td= @account.statuses_count
+      %td= link_to @account.statuses_count, admin_account_statuses_path(@account.id)
     %tr
       %th= t('admin.accounts.media_attachments')
       %td
-        = @account.media_attachments.count
+        = link_to @account.media_attachments.count, admin_account_statuses_path(@account.id, { media: true })
         = surround '(', ')' do
           = number_to_human_size @account.media_attachments.sum('file_file_size')
     %tr
diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml
index 44486cb42..5747cc274 100644
--- a/app/views/admin/reports/show.html.haml
+++ b/app/views/admin/reports/show.html.haml
@@ -1,3 +1,6 @@
+- content_for :header_tags do
+  = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
+
 - content_for :page_title do
   = t('admin.reports.report', id: @report.id)
 
@@ -19,16 +22,27 @@
 - unless @report.statuses.empty?
   %hr/
 
-  - @report.statuses.each do |status|
-    .report-status
-      .activity-stream.activity-stream-headless
-        .entry= render 'stream_entries/simple_status', status: status
-      .report-status__actions
-        - unless status.media_attachments.empty?
-          = link_to admin_report_reported_status_path(@report, status, status: { sensitive: !status.sensitive }), method: :patch, class: 'icon-button nsfw-button', title: t("admin.reports.nsfw.#{!status.sensitive}") do
-            = fa_icon status.sensitive? ? 'eye' : 'eye-slash'
-        = link_to admin_report_reported_status_path(@report, status), method: :delete, class: 'icon-button trash-button', title: t('admin.reports.delete'), data: { confirm: t('admin.reports.are_you_sure') } do
-          = fa_icon 'trash'
+  = form_for(@form, url: admin_report_reported_statuses_path(@report.id)) do |f|
+    .batch-form-box
+      .batch-checkbox-all
+        = check_box_tag :batch_checkbox_all, nil, false
+      = f.select :action, Form::StatusBatch::ACTION_TYPE.map{|action| [t("admin.statuses.batch.#{action}"), action]}
+      = f.submit t('admin.statuses.execute'), data: { confirm: t('admin.reports.are_you_sure') }, class: 'button'
+      .media-spoiler-toggle-buttons
+        .media-spoiler-show-button.button= t('admin.statuses.media.show')
+        .media-spoiler-hide-button.button= t('admin.statuses.media.hide')
+    - @report.statuses.each do |status|
+      .report-status{ data: { id: status.id } }
+        .batch-checkbox
+          = f.check_box :status_ids, { multiple: true, include_hidden: false }, status.id
+        .activity-stream.activity-stream-headless
+          .entry= render 'stream_entries/simple_status', status: status
+        .report-status__actions
+          - unless status.media_attachments.empty?
+            = link_to admin_report_reported_status_path(@report, status, status: { sensitive: !status.sensitive }), method: :put, class: 'icon-button nsfw-button', title: t("admin.reports.nsfw.#{!status.sensitive}") do
+              = fa_icon status.sensitive? ? 'eye' : 'eye-slash'
+          = link_to admin_report_reported_status_path(@report, status), method: :delete, class: 'icon-button trash-button', title: t('admin.reports.delete'), data: { confirm: t('admin.reports.are_you_sure') }, remote: true do
+            = fa_icon 'trash'
 
 %hr/
 
diff --git a/app/views/admin/statuses/index.html.haml b/app/views/admin/statuses/index.html.haml
new file mode 100644
index 000000000..fe2581527
--- /dev/null
+++ b/app/views/admin/statuses/index.html.haml
@@ -0,0 +1,47 @@
+- content_for :header_tags do
+  = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
+
+- content_for :page_title do
+  = t('admin.statuses.title')
+
+.back-link
+  = link_to admin_account_path(@account.id) do
+    %i.fa.fa-chevron-left.fa-fw
+    = t('admin.statuses.back_to_account')
+
+.filters
+  .filter-subset
+    %strong= t('admin.statuses.media.title')
+    %ul
+      %li= link_to t('admin.statuses.no_media'), admin_account_statuses_path(@account.id, current_params.merge(media: nil)), class: !params[:media] && 'selected'
+      %li= link_to t('admin.statuses.with_media'), admin_account_statuses_path(@account.id, current_params.merge(media: true)), class: params[:media] && 'selected'
+
+- if @statuses.empty?
+  .accounts-grid
+    = render 'accounts/nothing_here'
+- else
+  = form_for(@form, url: admin_account_statuses_path(@account.id)) do |f|
+    = hidden_field_tag :page, params[:page]
+    = hidden_field_tag :media, params[:media]
+    .batch-form-box
+      .batch-checkbox-all
+        = check_box_tag :batch_checkbox_all, nil, false
+      = f.select :action, Form::StatusBatch::ACTION_TYPE.map{|action| [t("admin.statuses.batch.#{action}"), action]}
+      = f.submit t('admin.statuses.execute'), data: { confirm: t('admin.reports.are_you_sure') }, class: 'button'
+      .media-spoiler-toggle-buttons
+        .media-spoiler-show-button.button= t('admin.statuses.media.show')
+        .media-spoiler-hide-button.button= t('admin.statuses.media.hide')
+    - @statuses.each do |status|
+      .account-status{ data: { id: status.id } }
+        .batch-checkbox
+          = f.check_box :status_ids, { multiple: true, include_hidden: false }, status.id
+        .activity-stream.activity-stream-headless
+          .entry= render 'stream_entries/simple_status', status: status
+        .account-status__actions
+          - unless status.media_attachments.empty?
+            = link_to admin_account_status_path(@account.id, status, current_params.merge(status: { sensitive: !status.sensitive })), method: :patch, class: 'icon-button nsfw-button', title: t("admin.reports.nsfw.#{!status.sensitive}") do
+              = fa_icon status.sensitive? ? 'eye' : 'eye-slash'
+          = link_to admin_account_status_path(@account.id, status), method: :delete, class: 'icon-button trash-button', title: t('admin.reports.delete'), data: { confirm: t('admin.reports.are_you_sure') }, remote: true do
+            = fa_icon 'trash'
+
+= paginate @statuses