about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Gemfile.lock7
-rw-r--r--app/helpers/application_helper.rb8
-rw-r--r--app/javascript/mastodon/service_worker/web_push_notifications.js7
-rw-r--r--app/javascript/packs/public.js6
-rw-r--r--app/javascript/styles/mastodon/about.scss4
-rw-r--r--app/javascript/styles/mastodon/admin.scss10
-rw-r--r--app/views/stream_entries/_attachment_list.html.haml8
-rw-r--r--app/views/stream_entries/_detailed_status.html.haml6
-rw-r--r--app/views/stream_entries/_simple_status.html.haml6
-rw-r--r--spec/models/admin/account_action_spec.rb127
-rw-r--r--spec/models/admin/action_log_spec.rb8
11 files changed, 182 insertions, 15 deletions
diff --git a/Gemfile.lock b/Gemfile.lock
index 7d2878483..38bbd2c1c 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -429,7 +429,7 @@ GEM
     rack-attack (5.4.2)
       rack (>= 1.0, < 3)
     rack-cors (1.0.2)
-    rack-protection (2.0.4)
+    rack-protection (2.0.5)
       rack
     rack-proxy (0.6.4)
       rack
@@ -553,8 +553,9 @@ GEM
     scss_lint (0.57.1)
       rake (>= 0.9, < 13)
       sass (~> 3.5, >= 3.5.5)
-    sidekiq (5.2.3)
+    sidekiq (5.2.5)
       connection_pool (~> 2.2, >= 2.2.2)
+      rack (>= 1.5.0)
       rack-protection (>= 1.5.0)
       redis (>= 3.3.5, < 5)
     sidekiq-bulk (0.2.0)
@@ -639,7 +640,7 @@ GEM
       activesupport (>= 4.2)
       rack-proxy (>= 0.6.1)
       railties (>= 4.2)
-    webpush (0.3.5)
+    webpush (0.3.6)
       hkdf (~> 0.2)
       jwt (~> 2.0)
     websocket-driver (0.7.0)
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 5e7fdffb0..fc006d777 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -69,8 +69,12 @@ module ApplicationHelper
     tag(:meta, content: content, property: property)
   end
 
-  def react_component(name, props = {})
-    content_tag(:div, nil, data: { component: name.to_s.camelcase, props: Oj.dump(props) })
+  def react_component(name, props = {}, &block)
+    if block.nil?
+      content_tag(:div, nil, data: { component: name.to_s.camelcase, props: Oj.dump(props) })
+    else
+      content_tag(:div, data: { component: name.to_s.camelcase, props: Oj.dump(props) }, &block)
+    end
   end
 
   def body_classes
diff --git a/app/javascript/mastodon/service_worker/web_push_notifications.js b/app/javascript/mastodon/service_worker/web_push_notifications.js
index 80a4fb329..1ab0dc0fa 100644
--- a/app/javascript/mastodon/service_worker/web_push_notifications.js
+++ b/app/javascript/mastodon/service_worker/web_push_notifications.js
@@ -92,11 +92,14 @@ const handlePush = (event) => {
       options.image     = notification.status && notification.status.media_attachments.length > 0 && notification.status.media_attachments[0].preview_url || undefined;
       options.data      = { access_token, preferred_locale, id: notification.status ? notification.status.id : notification.account.id, url: notification.status ? `/web/statuses/${notification.status.id}` : `/web/accounts/${notification.account.id}` };
 
-      if (notification.status && notification.status.sensitive) {
+      if (notification.status && notification.status.spoiler_text || notification.status.sensitive) {
         options.data.hiddenBody  = htmlToPlainText(notification.status.content);
         options.data.hiddenImage = notification.status.media_attachments.length > 0 && notification.status.media_attachments[0].preview_url;
 
-        options.body    = notification.status.spoiler_text;
+        if (notification.status.spoiler_text) {
+          options.body    = notification.status.spoiler_text;
+        }
+
         options.image   = undefined;
         options.actions = [actionExpand(preferred_locale)];
       } else if (notification.type === 'mention') {
diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js
index 196d2d02f..69441d315 100644
--- a/app/javascript/packs/public.js
+++ b/app/javascript/packs/public.js
@@ -66,6 +66,12 @@ function main() {
     if (reactComponents.length > 0) {
       import(/* webpackChunkName: "containers/media_container" */ '../mastodon/containers/media_container')
         .then(({ default: MediaContainer }) => {
+          [].forEach.call(reactComponents, (component) => {
+            [].forEach.call(component.children, (child) => {
+              component.removeChild(child);
+            });
+          });
+
           const content = document.createElement('div');
 
           ReactDOM.render(<MediaContainer locale={locale} components={reactComponents} />, content);
diff --git a/app/javascript/styles/mastodon/about.scss b/app/javascript/styles/mastodon/about.scss
index dc456be3e..4023b34d8 100644
--- a/app/javascript/styles/mastodon/about.scss
+++ b/app/javascript/styles/mastodon/about.scss
@@ -1041,6 +1041,10 @@ $small-breakpoint: 960px;
 
     .scrollable {
       height: 400px;
+
+      @media screen and (max-width: $column-breakpoint) {
+        height: 90vh;
+      }
     }
 
     p {
diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss
index e7124a2c0..4e969601b 100644
--- a/app/javascript/styles/mastodon/admin.scss
+++ b/app/javascript/styles/mastodon/admin.scss
@@ -1,4 +1,6 @@
 $no-columns-breakpoint: 600px;
+$sidebar-width: 240px;
+$content-width: 840px;
 
 .admin-wrapper {
   display: flex;
@@ -6,7 +8,7 @@ $no-columns-breakpoint: 600px;
   height: 100%;
 
   .sidebar-wrapper {
-    flex: 1;
+    flex: 1 1 $sidebar-width;
     height: 100%;
     background: $ui-base-color;
     display: flex;
@@ -14,7 +16,7 @@ $no-columns-breakpoint: 600px;
   }
 
   .sidebar {
-    width: 240px;
+    width: $sidebar-width;
     height: 100%;
     padding: 0;
     overflow-y: auto;
@@ -95,12 +97,12 @@ $no-columns-breakpoint: 600px;
   }
 
   .content-wrapper {
-    flex: 2;
+    flex: 2 1 $content-width;
     overflow: auto;
   }
 
   .content {
-    max-width: 700px;
+    max-width: $content-width;
     padding: 20px 15px;
     padding-top: 60px;
     padding-left: 25px;
diff --git a/app/views/stream_entries/_attachment_list.html.haml b/app/views/stream_entries/_attachment_list.html.haml
new file mode 100644
index 000000000..d9706f47b
--- /dev/null
+++ b/app/views/stream_entries/_attachment_list.html.haml
@@ -0,0 +1,8 @@
+.attachment-list
+  .attachment-list__icon
+    = fa_icon 'link'
+  %ul.attachment-list__list
+    - attachments.each do |media|
+      %li
+        - url = media.remote_url.presence || media.file.url
+        = link_to File.basename(url), url, title: media.description
diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml
index 9298ecbb0..18265e110 100644
--- a/app/views/stream_entries/_detailed_status.html.haml
+++ b/app/views/stream_entries/_detailed_status.html.haml
@@ -25,9 +25,11 @@
   - if !status.media_attachments.empty?
     - if status.media_attachments.first.video?
       - video = status.media_attachments.first
-      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, width: 670, height: 380, detailed: true, inline: true, alt: video.description
+      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, width: 670, height: 380, detailed: true, inline: true, alt: video.description do
+        = render partial: 'stream_entries/attachment_list', locals: { attachments: status.media_attachments }
     - else
-      = react_component :media_gallery, height: 380, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, standalone: true, 'autoPlayGif': current_account&.user&.setting_auto_play_gif || autoplay, 'reduceMotion': current_account&.user&.setting_reduce_motion, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
+      = react_component :media_gallery, height: 380, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, standalone: true, 'autoPlayGif': current_account&.user&.setting_auto_play_gif || autoplay, 'reduceMotion': current_account&.user&.setting_reduce_motion, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do
+        = render partial: 'stream_entries/attachment_list', locals: { attachments: status.media_attachments }
   - elsif status.preview_card
     = react_component :card, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json
 
diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/stream_entries/_simple_status.html.haml
index 1d44be791..28b4e3217 100644
--- a/app/views/stream_entries/_simple_status.html.haml
+++ b/app/views/stream_entries/_simple_status.html.haml
@@ -30,9 +30,11 @@
   - if !status.media_attachments.empty?
     - if status.media_attachments.first.video?
       - video = status.media_attachments.first
-      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, width: 610, height: 343, inline: true, alt: video.description
+      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, width: 610, height: 343, inline: true, alt: video.description do
+        = render partial: 'stream_entries/attachment_list', locals: { attachments: status.media_attachments }
     - else
-      = react_component :media_gallery, height: 343, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, 'autoPlayGif': current_account&.user&.setting_auto_play_gif || autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
+      = react_component :media_gallery, height: 343, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, 'autoPlayGif': current_account&.user&.setting_auto_play_gif || autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do
+        = render partial: 'stream_entries/attachment_list', locals: { attachments: status.media_attachments }
   - elsif status.preview_card
     = react_component :card, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json
 
diff --git a/spec/models/admin/account_action_spec.rb b/spec/models/admin/account_action_spec.rb
index 8c55cf4dd..a3db60cfc 100644
--- a/spec/models/admin/account_action_spec.rb
+++ b/spec/models/admin/account_action_spec.rb
@@ -1,4 +1,131 @@
 require 'rails_helper'
 
 RSpec.describe Admin::AccountAction, type: :model do
+  let(:account_action) { described_class.new }
+
+  describe '#save!' do
+    subject              { account_action.save! }
+    let(:account)        { Fabricate(:account, user: Fabricate(:user, admin: true)) }
+    let(:target_account) { Fabricate(:account, user: Fabricate(:user)) }
+    let(:type)           { 'disable' }
+
+    before do
+      account_action.assign_attributes(
+        type:            type,
+        current_account: account,
+        target_account:  target_account
+      )
+    end
+
+    context 'type is "disable"' do
+      let(:type) { 'disable' }
+
+      it 'disable user' do
+        subject
+        expect(target_account.user).to be_disabled
+      end
+    end
+
+    context 'type is "silence"' do
+      let(:type) { 'silence' }
+
+      it 'silences account' do
+        subject
+        expect(target_account).to be_silenced
+      end
+    end
+
+    context 'type is "suspend"' do
+      let(:type) { 'suspend' }
+
+      it 'suspends account' do
+        subject
+        expect(target_account).to be_suspended
+      end
+
+      it 'queues Admin::SuspensionWorker by 1' do
+        Sidekiq::Testing.fake! do
+          expect do
+            subject
+          end.to change { Admin::SuspensionWorker.jobs.size }.by 1
+        end
+      end
+    end
+
+    it 'creates Admin::ActionLog' do
+      expect do
+        subject
+      end.to change { Admin::ActionLog.count }.by 1
+    end
+
+    it 'calls queue_email!' do
+      expect(account_action).to receive(:queue_email!)
+      subject
+    end
+
+    it 'calls process_reports!' do
+      expect(account_action).to receive(:process_reports!)
+      subject
+    end
+  end
+
+  describe '#report' do
+    subject { account_action.report }
+
+    context 'report_id.present?' do
+      before do
+        account_action.report_id = Fabricate(:report).id
+      end
+
+      it 'returns Report' do
+        expect(subject).to be_instance_of Report
+      end
+    end
+
+    context '!report_id.present?' do
+      it 'returns nil' do
+        expect(subject).to be_nil
+      end
+    end
+  end
+
+  describe '#with_report?' do
+    subject { account_action.with_report? }
+
+    context '!report.nil?' do
+      before do
+        account_action.report_id = Fabricate(:report).id
+      end
+
+      it 'returns true' do
+        expect(subject).to be true
+      end
+    end
+
+    context '!(!report.nil?)' do
+      it 'returns false' do
+        expect(subject).to be false
+      end
+    end
+  end
+
+  describe '.types_for_account' do
+    subject { described_class.types_for_account(account) }
+
+    context 'account.local?' do
+      let(:account) { Fabricate(:account, domain: nil) }
+
+      it 'returns ["none", "disable", "silence", "suspend"]' do
+        expect(subject).to eq %w(none disable silence suspend)
+      end
+    end
+
+    context '!account.local?' do
+      let(:account) { Fabricate(:account, domain: 'hoge.com') }
+
+      it 'returns ["silence", "suspend"]' do
+        expect(subject).to eq %w(silence suspend)
+      end
+    end
+  end
 end
diff --git a/spec/models/admin/action_log_spec.rb b/spec/models/admin/action_log_spec.rb
index 81d7e1be3..3495cc514 100644
--- a/spec/models/admin/action_log_spec.rb
+++ b/spec/models/admin/action_log_spec.rb
@@ -1,4 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Admin::ActionLog, type: :model do
+  describe '#action' do
+    it 'returns action' do
+      action_log = described_class.new(action: 'hoge')
+      expect(action_log.action).to be :hoge
+    end
+  end
 end