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/flavours/glitch/packs/public.js b/app/javascript/flavours/glitch/packs/public.js
index 342c5265e..56012ba78 100644
--- a/app/javascript/flavours/glitch/packs/public.js
+++ b/app/javascript/flavours/glitch/packs/public.js
@@ -61,6 +61,12 @@ function main() {
if (reactComponents.length > 0) {
import(/* webpackChunkName: "containers/media_container" */ 'flavours/glitch/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(, content);
diff --git a/app/javascript/flavours/glitch/styles/about.scss b/app/javascript/flavours/glitch/styles/about.scss
index f676a8c77..da50581fb 100644
--- a/app/javascript/flavours/glitch/styles/about.scss
+++ b/app/javascript/flavours/glitch/styles/about.scss
@@ -1044,6 +1044,10 @@ $small-breakpoint: 960px;
.scrollable {
height: 400px;
+
+ @media screen and (max-width: $column-breakpoint) {
+ height: 90vh;
+ }
}
p {
diff --git a/app/javascript/flavours/glitch/styles/admin.scss b/app/javascript/flavours/glitch/styles/admin.scss
index e7124a2c0..4e969601b 100644
--- a/app/javascript/flavours/glitch/styles/admin.scss
+++ b/app/javascript/flavours/glitch/styles/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/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(, 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