about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/controllers/admin/accounts_controller.rb16
-rw-r--r--app/controllers/admin/custom_emojis_controller.rb2
-rw-r--r--app/controllers/admin/instances_controller.rb2
-rw-r--r--app/controllers/admin/invites_controller.rb2
-rw-r--r--app/controllers/admin/reports_controller.rb7
-rw-r--r--app/controllers/admin/tags_controller.rb2
-rw-r--r--app/controllers/api/oembed_controller.rb2
-rw-r--r--app/controllers/relationships_controller.rb2
-rw-r--r--app/helpers/admin/filter_helper.rb18
-rw-r--r--app/helpers/settings_helper.rb1
-rw-r--r--app/javascript/flavours/glitch/components/media_gallery.js16
-rw-r--r--app/javascript/flavours/glitch/locales/ast.js7
-rw-r--r--app/javascript/flavours/glitch/locales/bn.js7
-rw-r--r--app/javascript/flavours/glitch/locales/br.js7
-rw-r--r--app/javascript/flavours/glitch/locales/co.js7
-rw-r--r--app/javascript/flavours/glitch/locales/cs.js7
-rw-r--r--app/javascript/flavours/glitch/locales/cy.js7
-rw-r--r--app/javascript/flavours/glitch/locales/da.js7
-rw-r--r--app/javascript/flavours/glitch/locales/el.js7
-rw-r--r--app/javascript/flavours/glitch/locales/es-AR.js7
-rw-r--r--app/javascript/flavours/glitch/locales/et.js7
-rw-r--r--app/javascript/flavours/glitch/locales/eu.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ga.js7
-rw-r--r--app/javascript/flavours/glitch/locales/gl.js7
-rw-r--r--app/javascript/flavours/glitch/locales/hi.js7
-rw-r--r--app/javascript/flavours/glitch/locales/hy.js7
-rw-r--r--app/javascript/flavours/glitch/locales/is.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ka.js7
-rw-r--r--app/javascript/flavours/glitch/locales/kab.js7
-rw-r--r--app/javascript/flavours/glitch/locales/kk.js7
-rw-r--r--app/javascript/flavours/glitch/locales/kn.js7
-rw-r--r--app/javascript/flavours/glitch/locales/lt.js7
-rw-r--r--app/javascript/flavours/glitch/locales/lv.js7
-rw-r--r--app/javascript/flavours/glitch/locales/mk.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ml.js7
-rw-r--r--app/javascript/flavours/glitch/locales/mr.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ms.js7
-rw-r--r--app/javascript/flavours/glitch/locales/nn.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ro.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sk.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sl.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sq.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sr-Latn.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sr.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ta.js7
-rw-r--r--app/javascript/flavours/glitch/locales/te.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ur.js7
-rw-r--r--app/javascript/flavours/glitch/locales/vi.js7
-rw-r--r--app/javascript/flavours/glitch/packs/public.js9
-rw-r--r--app/javascript/mastodon/components/media_gallery.js16
-rw-r--r--app/javascript/mastodon/components/scrollable_list.js2
-rw-r--r--app/javascript/packs/public.js9
-rw-r--r--app/lib/spam_check.rb2
-rw-r--r--app/models/account_filter.rb17
-rw-r--r--app/models/custom_emoji_filter.rb7
-rw-r--r--app/models/instance_filter.rb5
-rw-r--r--app/models/invite_filter.rb5
-rw-r--r--app/models/relationship_filter.rb11
-rw-r--r--app/models/report_filter.rb7
-rw-r--r--app/models/tag_filter.rb10
-rw-r--r--app/models/user.rb16
-rw-r--r--app/views/admin/accounts/index.html.haml2
-rw-r--r--app/views/admin/accounts/show.html.haml12
-rw-r--r--app/views/admin/custom_emojis/index.html.haml4
-rw-r--r--app/views/admin/email_domain_blocks/index.html.haml2
-rw-r--r--app/views/admin/instances/index.html.haml5
-rw-r--r--app/views/admin/reports/_status.html.haml7
-rw-r--r--app/views/admin/reports/index.html.haml5
-rw-r--r--app/views/admin/tags/index.html.haml5
-rw-r--r--app/views/filters/index.html.haml2
-rw-r--r--app/views/layouts/admin.html.haml3
-rw-r--r--app/views/layouts/embedded.html.haml4
-rw-r--r--app/views/relationships/show.html.haml7
-rw-r--r--app/views/settings/applications/index.html.haml2
-rw-r--r--app/views/statuses/_detailed_status.html.haml4
-rw-r--r--app/views/statuses/_simple_status.html.haml4
76 files changed, 416 insertions, 97 deletions
diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb
index 68b6352f8..7b1783542 100644
--- a/app/controllers/admin/accounts_controller.rb
+++ b/app/controllers/admin/accounts_controller.rb
@@ -109,21 +109,7 @@ module Admin
     end
 
     def filter_params
-      params.permit(
-        :local,
-        :remote,
-        :by_domain,
-        :active,
-        :pending,
-        :disabled,
-        :silenced,
-        :suspended,
-        :username,
-        :display_name,
-        :email,
-        :ip,
-        :staff
-      )
+      params.slice(*AccountFilter::KEYS).permit(*AccountFilter::KEYS)
     end
   end
 end
diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb
index a446465c9..efa8f2950 100644
--- a/app/controllers/admin/custom_emojis_controller.rb
+++ b/app/controllers/admin/custom_emojis_controller.rb
@@ -48,7 +48,7 @@ module Admin
     end
 
     def filter_params
-      params.slice(:local, :remote, :by_domain, :shortcode, :page).permit(:local, :remote, :by_domain, :shortcode, :page)
+      params.slice(:page, *CustomEmojiFilter::KEYS).permit(:page, *CustomEmojiFilter::KEYS)
     end
 
     def action_from_button
diff --git a/app/controllers/admin/instances_controller.rb b/app/controllers/admin/instances_controller.rb
index b47b18f8e..2fc041207 100644
--- a/app/controllers/admin/instances_controller.rb
+++ b/app/controllers/admin/instances_controller.rb
@@ -62,7 +62,7 @@ module Admin
     end
 
     def filter_params
-      params.permit(:limited, :by_domain)
+      params.slice(*InstanceFilter::KEYS).permit(*InstanceFilter::KEYS)
     end
   end
 end
diff --git a/app/controllers/admin/invites_controller.rb b/app/controllers/admin/invites_controller.rb
index 44a8eec77..dabfe9765 100644
--- a/app/controllers/admin/invites_controller.rb
+++ b/app/controllers/admin/invites_controller.rb
@@ -47,7 +47,7 @@ module Admin
     end
 
     def filter_params
-      params.permit(:available, :expired)
+      params.slice(*InviteFilter::KEYS).permit(*InviteFilter::KEYS)
     end
   end
 end
diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb
index 09ce1761c..7c831b3d4 100644
--- a/app/controllers/admin/reports_controller.rb
+++ b/app/controllers/admin/reports_controller.rb
@@ -52,12 +52,7 @@ module Admin
     end
 
     def filter_params
-      params.permit(
-        :account_id,
-        :resolved,
-        :target_account_id,
-        :by_target_domain
-      )
+      params.slice(*ReportFilter::KEYS).permit(*ReportFilter::KEYS)
     end
 
     def set_report
diff --git a/app/controllers/admin/tags_controller.rb b/app/controllers/admin/tags_controller.rb
index 65341bbfb..59df4470e 100644
--- a/app/controllers/admin/tags_controller.rb
+++ b/app/controllers/admin/tags_controller.rb
@@ -73,7 +73,7 @@ module Admin
     end
 
     def filter_params
-      params.slice(:directory, :reviewed, :unreviewed, :pending_review, :page, :popular, :active, :name).permit(:directory, :reviewed, :unreviewed, :pending_review, :page, :popular, :active, :name)
+      params.slice(:page, *TagFilter::KEYS).permit(:page, *TagFilter::KEYS)
     end
 
     def tag_params
diff --git a/app/controllers/api/oembed_controller.rb b/app/controllers/api/oembed_controller.rb
index 37a163cd3..c8c60b1cf 100644
--- a/app/controllers/api/oembed_controller.rb
+++ b/app/controllers/api/oembed_controller.rb
@@ -3,6 +3,8 @@
 class Api::OEmbedController < Api::BaseController
   respond_to :json
 
+  skip_before_action :require_authenticated_user!
+
   def show
     @status = status_finder.status
     render json: @status, serializer: OEmbedSerializer, width: maxwidth_or_default, height: maxheight_or_default
diff --git a/app/controllers/relationships_controller.rb b/app/controllers/relationships_controller.rb
index c87a0cf13..25dd0d2ad 100644
--- a/app/controllers/relationships_controller.rb
+++ b/app/controllers/relationships_controller.rb
@@ -86,7 +86,7 @@ class RelationshipsController < ApplicationController
   end
 
   def current_params
-    params.slice(:page, :status, :relationship, :by_domain, :activity, :order).permit(:page, :status, :relationship, :by_domain, :activity, :order)
+    params.slice(:page, *RelationshipFilter::KEYS).permit(:page, *RelationshipFilter::KEYS)
   end
 
   def action_from_button
diff --git a/app/helpers/admin/filter_helper.rb b/app/helpers/admin/filter_helper.rb
index fc4f15985..130686a02 100644
--- a/app/helpers/admin/filter_helper.rb
+++ b/app/helpers/admin/filter_helper.rb
@@ -1,15 +1,15 @@
 # frozen_string_literal: true
 
 module Admin::FilterHelper
-  ACCOUNT_FILTERS      = %i(local remote by_domain active pending silenced suspended username display_name email ip staff).freeze
-  REPORT_FILTERS       = %i(resolved account_id target_account_id by_target_domain).freeze
-  INVITE_FILTER        = %i(available expired).freeze
-  CUSTOM_EMOJI_FILTERS = %i(local remote by_domain shortcode).freeze
-  TAGS_FILTERS         = %i(directory reviewed unreviewed pending_review popular active name).freeze
-  INSTANCES_FILTERS    = %i(limited by_domain).freeze
-  FOLLOWERS_FILTERS    = %i(relationship status by_domain activity order).freeze
-
-  FILTERS = ACCOUNT_FILTERS + REPORT_FILTERS + INVITE_FILTER + CUSTOM_EMOJI_FILTERS + TAGS_FILTERS + INSTANCES_FILTERS + FOLLOWERS_FILTERS
+  FILTERS = [
+    AccountFilter::KEYS,
+    CustomEmojiFilter::KEYS,
+    ReportFilter::KEYS,
+    TagFilter::KEYS,
+    InstanceFilter::KEYS,
+    InviteFilter::KEYS,
+    RelationshipFilter::KEYS,
+  ].flatten.freeze
 
   def filter_link_to(text, link_to_params, link_class_params = link_to_params)
     new_url   = filtered_url_for(link_to_params)
diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb
index 39eb4180e..10aaf3aca 100644
--- a/app/helpers/settings_helper.rb
+++ b/app/helpers/settings_helper.rb
@@ -32,6 +32,7 @@ module SettingsHelper
     hy: 'Հայերեն',
     id: 'Bahasa Indonesia',
     io: 'Ido',
+    is: 'Íslenska',
     it: 'Italiano',
     ja: '日本語',
     ka: 'ქართული',
diff --git a/app/javascript/flavours/glitch/components/media_gallery.js b/app/javascript/flavours/glitch/components/media_gallery.js
index 85ee79e11..9754c73dc 100644
--- a/app/javascript/flavours/glitch/components/media_gallery.js
+++ b/app/javascript/flavours/glitch/components/media_gallery.js
@@ -43,6 +43,7 @@ class Item extends React.PureComponent {
     onClick: PropTypes.func.isRequired,
     displayWidth: PropTypes.number,
     visible: PropTypes.bool.isRequired,
+    autoplay: PropTypes.bool,
   };
 
   static defaultProps = {
@@ -68,9 +69,13 @@ class Item extends React.PureComponent {
     }
   }
 
+  getAutoPlay() {
+    return this.props.autoplay || autoPlayGif;
+  }
+
   hoverToPlay () {
     const { attachment } = this.props;
-    return !autoPlayGif && attachment.get('type') === 'gifv';
+    return !this.getAutoPlay() && attachment.get('type') === 'gifv';
   }
 
   handleClick = (e) => {
@@ -222,7 +227,7 @@ class Item extends React.PureComponent {
         </a>
       );
     } else if (attachment.get('type') === 'gifv') {
-      const autoPlay = !isIOS() && autoPlayGif;
+      const autoPlay = !isIOS() && this.getAutoPlay();
 
       thumbnail = (
         <div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}>
@@ -271,6 +276,7 @@ class MediaGallery extends React.PureComponent {
     defaultWidth: PropTypes.number,
     cacheWidth: PropTypes.func,
     visible: PropTypes.bool,
+    autoplay: PropTypes.bool,
     onToggleVisibility: PropTypes.func,
   };
 
@@ -328,7 +334,7 @@ class MediaGallery extends React.PureComponent {
   }
 
   render () {
-    const { media, intl, sensitive, letterbox, fullwidth, defaultWidth } = this.props;
+    const { media, intl, sensitive, letterbox, fullwidth, defaultWidth, autoplay } = this.props;
     const { visible } = this.state;
     const size     = media.take(4).size;
     const uncached = media.every(attachment => attachment.get('type') === 'unknown');
@@ -350,9 +356,9 @@ class MediaGallery extends React.PureComponent {
     }
 
     if (this.isStandaloneEligible()) {
-      children = <Item standalone onClick={this.handleClick} attachment={media.get(0)} displayWidth={width} visible={visible} />;
+      children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} displayWidth={width} visible={visible} />;
     } else {
-      children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} letterbox={letterbox} displayWidth={width} visible={visible || uncached} />);
+      children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} autoplay={autoplay} onClick={this.handleClick} attachment={attachment} index={i} size={size} letterbox={letterbox} displayWidth={width} visible={visible || uncached} />);
     }
 
     if (uncached) {
diff --git a/app/javascript/flavours/glitch/locales/ast.js b/app/javascript/flavours/glitch/locales/ast.js
new file mode 100644
index 000000000..41355c24c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ast.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/ast.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/bn.js b/app/javascript/flavours/glitch/locales/bn.js
new file mode 100644
index 000000000..a453498b3
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/bn.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/bn.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/br.js b/app/javascript/flavours/glitch/locales/br.js
new file mode 100644
index 000000000..966bd1b2f
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/br.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/br.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/co.js b/app/javascript/flavours/glitch/locales/co.js
new file mode 100644
index 000000000..6e9e46797
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/co.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/co.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/cs.js b/app/javascript/flavours/glitch/locales/cs.js
new file mode 100644
index 000000000..ac7db0327
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/cs.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/cs.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/cy.js b/app/javascript/flavours/glitch/locales/cy.js
new file mode 100644
index 000000000..09412bd72
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/cy.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/cy.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/da.js b/app/javascript/flavours/glitch/locales/da.js
new file mode 100644
index 000000000..2b08806be
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/da.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/da.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/el.js b/app/javascript/flavours/glitch/locales/el.js
new file mode 100644
index 000000000..2d9bb829f
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/el.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/el.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/es-AR.js b/app/javascript/flavours/glitch/locales/es-AR.js
new file mode 100644
index 000000000..0dffabcd4
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/es-AR.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/es-AR.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/et.js b/app/javascript/flavours/glitch/locales/et.js
new file mode 100644
index 000000000..e3ea6b2a9
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/et.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/et.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/eu.js b/app/javascript/flavours/glitch/locales/eu.js
new file mode 100644
index 000000000..946410b67
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/eu.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/eu.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ga.js b/app/javascript/flavours/glitch/locales/ga.js
new file mode 100644
index 000000000..af2846ff8
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ga.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/ga.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/gl.js b/app/javascript/flavours/glitch/locales/gl.js
new file mode 100644
index 000000000..6a9140b1a
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/gl.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/gl.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/hi.js b/app/javascript/flavours/glitch/locales/hi.js
new file mode 100644
index 000000000..1a569495f
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/hi.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/hi.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/hy.js b/app/javascript/flavours/glitch/locales/hy.js
new file mode 100644
index 000000000..96f6a4d19
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/hy.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/hy.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/is.js b/app/javascript/flavours/glitch/locales/is.js
new file mode 100644
index 000000000..b05a08ad0
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/is.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/is.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ka.js b/app/javascript/flavours/glitch/locales/ka.js
new file mode 100644
index 000000000..3e06f4282
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ka.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/ka.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/kab.js b/app/javascript/flavours/glitch/locales/kab.js
new file mode 100644
index 000000000..5ed1156ef
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/kab.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/kab.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/kk.js b/app/javascript/flavours/glitch/locales/kk.js
new file mode 100644
index 000000000..8d00fb035
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/kk.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/kk.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/kn.js b/app/javascript/flavours/glitch/locales/kn.js
new file mode 100644
index 000000000..1c50e3628
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/kn.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/kn.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/lt.js b/app/javascript/flavours/glitch/locales/lt.js
new file mode 100644
index 000000000..47453aeeb
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/lt.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/lt.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/lv.js b/app/javascript/flavours/glitch/locales/lv.js
new file mode 100644
index 000000000..cdbcdf799
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/lv.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/lv.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/mk.js b/app/javascript/flavours/glitch/locales/mk.js
new file mode 100644
index 000000000..55e510b59
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/mk.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/mk.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ml.js b/app/javascript/flavours/glitch/locales/ml.js
new file mode 100644
index 000000000..d00331a1a
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ml.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/ml.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/mr.js b/app/javascript/flavours/glitch/locales/mr.js
new file mode 100644
index 000000000..fb3cde92a
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/mr.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/mr.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ms.js b/app/javascript/flavours/glitch/locales/ms.js
new file mode 100644
index 000000000..61033c521
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ms.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/ms.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/nn.js b/app/javascript/flavours/glitch/locales/nn.js
new file mode 100644
index 000000000..4c42368cb
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/nn.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/nn.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ro.js b/app/javascript/flavours/glitch/locales/ro.js
new file mode 100644
index 000000000..a16446c6a
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ro.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/ro.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sk.js b/app/javascript/flavours/glitch/locales/sk.js
new file mode 100644
index 000000000..5fba6ab97
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sk.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/sk.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sl.js b/app/javascript/flavours/glitch/locales/sl.js
new file mode 100644
index 000000000..c53c1bae8
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sl.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/sl.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sq.js b/app/javascript/flavours/glitch/locales/sq.js
new file mode 100644
index 000000000..2fb7a2973
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sq.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/sq.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sr-Latn.js b/app/javascript/flavours/glitch/locales/sr-Latn.js
new file mode 100644
index 000000000..b42d5eaaf
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sr-Latn.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/sr-Latn.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sr.js b/app/javascript/flavours/glitch/locales/sr.js
new file mode 100644
index 000000000..8793d8d1e
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sr.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/sr.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ta.js b/app/javascript/flavours/glitch/locales/ta.js
new file mode 100644
index 000000000..d6ecdcb1b
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ta.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/ta.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/te.js b/app/javascript/flavours/glitch/locales/te.js
new file mode 100644
index 000000000..afd6e4f7b
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/te.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/te.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ur.js b/app/javascript/flavours/glitch/locales/ur.js
new file mode 100644
index 000000000..97ba291b0
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ur.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/ur.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/vi.js b/app/javascript/flavours/glitch/locales/vi.js
new file mode 100644
index 000000000..499a96727
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/vi.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/vi.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/packs/public.js b/app/javascript/flavours/glitch/packs/public.js
index 973d6ee46..d1adfb17a 100644
--- a/app/javascript/flavours/glitch/packs/public.js
+++ b/app/javascript/flavours/glitch/packs/public.js
@@ -97,15 +97,6 @@ function main() {
 
     delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original'));
     delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static'));
-
-    delegate(document, '.blocks-table button.icon-button', 'click', function(e) {
-      e.preventDefault();
-
-      const classList = this.firstElementChild.classList;
-      classList.toggle('fa-chevron-down');
-      classList.toggle('fa-chevron-up');
-      this.parentElement.parentElement.nextElementSibling.classList.toggle('hidden');
-    });
   });
 
   delegate(document, '.sidebar__toggle__icon', 'click', () => {
diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js
index 12b7e5b66..cfe164a50 100644
--- a/app/javascript/mastodon/components/media_gallery.js
+++ b/app/javascript/mastodon/components/media_gallery.js
@@ -23,6 +23,7 @@ class Item extends React.PureComponent {
     onClick: PropTypes.func.isRequired,
     displayWidth: PropTypes.number,
     visible: PropTypes.bool.isRequired,
+    autoplay: PropTypes.bool,
   };
 
   static defaultProps = {
@@ -48,9 +49,13 @@ class Item extends React.PureComponent {
     }
   }
 
+  getAutoPlay() {
+    return this.props.autoplay || autoPlayGif;
+  }
+
   hoverToPlay () {
     const { attachment } = this.props;
-    return !autoPlayGif && attachment.get('type') === 'gifv';
+    return !this.getAutoPlay() && attachment.get('type') === 'gifv';
   }
 
   handleClick = (e) => {
@@ -201,7 +206,7 @@ class Item extends React.PureComponent {
         </a>
       );
     } else if (attachment.get('type') === 'gifv') {
-      const autoPlay = !isIOS() && autoPlayGif;
+      const autoPlay = !isIOS() && this.getAutoPlay();
 
       thumbnail = (
         <div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}>
@@ -248,6 +253,7 @@ class MediaGallery extends React.PureComponent {
     defaultWidth: PropTypes.number,
     cacheWidth: PropTypes.func,
     visible: PropTypes.bool,
+    autoplay: PropTypes.bool,
     onToggleVisibility: PropTypes.func,
   };
 
@@ -297,7 +303,7 @@ class MediaGallery extends React.PureComponent {
   }
 
   render () {
-    const { media, intl, sensitive, height, defaultWidth, standalone } = this.props;
+    const { media, intl, sensitive, height, defaultWidth, standalone, autoplay } = this.props;
     const { visible } = this.state;
 
     const width = this.state.width || defaultWidth;
@@ -320,9 +326,9 @@ class MediaGallery extends React.PureComponent {
     const uncached = media.every(attachment => attachment.get('type') === 'unknown');
 
     if (standalone && this.isFullSizeEligible()) {
-      children = <Item standalone onClick={this.handleClick} attachment={media.get(0)} displayWidth={width} visible={visible} />;
+      children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} displayWidth={width} visible={visible} />;
     } else {
-      children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} displayWidth={width} visible={visible || uncached} />);
+      children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} autoplay={autoplay} onClick={this.handleClick} attachment={attachment} index={i} size={size} displayWidth={width} visible={visible || uncached} />);
     }
 
     if (uncached) {
diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js
index 47a87b149..3a490e78e 100644
--- a/app/javascript/mastodon/components/scrollable_list.js
+++ b/app/javascript/mastodon/components/scrollable_list.js
@@ -296,7 +296,7 @@ export default class ScrollableList extends PureComponent {
           </div>
         </div>
       );
-    } else if (isLoading || childrenCount > 0 || hasMore || !emptyMessage) {
+    } else if (isLoading || childrenCount > 0 || numPending > 0 || hasMore || !emptyMessage) {
       scrollableArea = (
         <div className={classNames('scrollable', { fullscreen })} ref={this.setRef} onMouseMove={this.handleMouseMove}>
           <div role='feed' className='item-list'>
diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js
index 3eae1a457..640f0af0a 100644
--- a/app/javascript/packs/public.js
+++ b/app/javascript/packs/public.js
@@ -101,15 +101,6 @@ function main() {
 
     delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original'));
     delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static'));
-
-    delegate(document, '.blocks-table button.icon-button', 'click', function(e) {
-      e.preventDefault();
-
-      const classList = this.firstElementChild.classList;
-      classList.toggle('fa-chevron-down');
-      classList.toggle('fa-chevron-up');
-      this.parentElement.parentElement.nextElementSibling.classList.toggle('hidden');
-    });
   });
 
   delegate(document, '.sidebar__toggle__icon', 'click', () => {
diff --git a/app/lib/spam_check.rb b/app/lib/spam_check.rb
index 5b40514fd..652d03615 100644
--- a/app/lib/spam_check.rb
+++ b/app/lib/spam_check.rb
@@ -143,7 +143,7 @@ class SpamCheck
   end
 
   def trusted?
-    @account.trust_level > Account::TRUST_LEVELS[:untrusted]
+    @account.trust_level > Account::TRUST_LEVELS[:untrusted] || (@account.local? && @account.user_staff?)
   end
 
   def no_unsolicited_mentions?
diff --git a/app/models/account_filter.rb b/app/models/account_filter.rb
index c3b1fe08d..c7bf07787 100644
--- a/app/models/account_filter.rb
+++ b/app/models/account_filter.rb
@@ -1,6 +1,21 @@
 # frozen_string_literal: true
 
 class AccountFilter
+  KEYS = %i(
+    local
+    remote
+    by_domain
+    active
+    pending
+    silenced
+    suspended
+    username
+    display_name
+    email
+    ip
+    staff
+  ).freeze
+
   attr_reader :params
 
   def initialize(params)
@@ -50,7 +65,7 @@ class AccountFilter
     when 'email'
       accounts_with_users.merge User.matches_email(value)
     when 'ip'
-      valid_ip?(value) ? accounts_with_users.where('users.current_sign_in_ip <<= ?', value) : Account.none
+      valid_ip?(value) ? accounts_with_users.merge(User.matches_ip(value)) : Account.none
     when 'staff'
       accounts_with_users.merge User.staff
     else
diff --git a/app/models/custom_emoji_filter.rb b/app/models/custom_emoji_filter.rb
index 15b8da1d1..414e1fcdd 100644
--- a/app/models/custom_emoji_filter.rb
+++ b/app/models/custom_emoji_filter.rb
@@ -1,6 +1,13 @@
 # frozen_string_literal: true
 
 class CustomEmojiFilter
+  KEYS = %i(
+    local
+    remote
+    by_domain
+    shortcode
+  ).freeze
+
   attr_reader :params
 
   def initialize(params)
diff --git a/app/models/instance_filter.rb b/app/models/instance_filter.rb
index 8bfab826d..9c467bc27 100644
--- a/app/models/instance_filter.rb
+++ b/app/models/instance_filter.rb
@@ -1,6 +1,11 @@
 # frozen_string_literal: true
 
 class InstanceFilter
+  KEYS = %i(
+    limited
+    by_domain
+  ).freeze
+
   attr_reader :params
 
   def initialize(params)
diff --git a/app/models/invite_filter.rb b/app/models/invite_filter.rb
index 7d89bad4a..9685d4abb 100644
--- a/app/models/invite_filter.rb
+++ b/app/models/invite_filter.rb
@@ -1,6 +1,11 @@
 # frozen_string_literal: true
 
 class InviteFilter
+  KEYS = %i(
+    available
+    expired
+  ).freeze
+
   attr_reader :params
 
   def initialize(params)
diff --git a/app/models/relationship_filter.rb b/app/models/relationship_filter.rb
new file mode 100644
index 000000000..51640f494
--- /dev/null
+++ b/app/models/relationship_filter.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class RelationshipFilter
+  KEYS = %i(
+    relationship
+    status
+    by_domain
+    activity
+    order
+  ).freeze
+end
diff --git a/app/models/report_filter.rb b/app/models/report_filter.rb
index abf53cbab..c32d4359e 100644
--- a/app/models/report_filter.rb
+++ b/app/models/report_filter.rb
@@ -1,6 +1,13 @@
 # frozen_string_literal: true
 
 class ReportFilter
+  KEYS = %i(
+    resolved
+    account_id
+    target_account_id
+    by_target_domain
+  ).freeze
+
   attr_reader :params
 
   def initialize(params)
diff --git a/app/models/tag_filter.rb b/app/models/tag_filter.rb
index 8921e186b..a9ff5b703 100644
--- a/app/models/tag_filter.rb
+++ b/app/models/tag_filter.rb
@@ -1,6 +1,16 @@
 # frozen_string_literal: true
 
 class TagFilter
+  KEYS = %i(
+    directory
+    reviewed
+    unreviewed
+    pending_review
+    popular
+    active
+    name
+  ).freeze
+
   attr_reader :params
 
   def initialize(params)
diff --git a/app/models/user.rb b/app/models/user.rb
index 49cfc25ca..794c2091c 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -93,6 +93,7 @@ class User < ApplicationRecord
   scope :inactive, -> { where(arel_table[:current_sign_in_at].lt(ACTIVE_DURATION.ago)) }
   scope :active, -> { confirmed.where(arel_table[:current_sign_in_at].gteq(ACTIVE_DURATION.ago)).joins(:account).where(accounts: { suspended_at: nil }) }
   scope :matches_email, ->(value) { where(arel_table[:email].matches("#{value}%")) }
+  scope :matches_ip, ->(value) { left_joins(:session_activations).where('users.current_sign_in_ip <<= ?', value).or(left_joins(:session_activations).where('users.last_sign_in_ip <<= ?', value)).or(left_joins(:session_activations).where('session_activations.ip <<= ?', value)) }
   scope :emailable, -> { confirmed.enabled.joins(:account).merge(Account.searchable) }
 
   before_validation :sanitize_languages
@@ -290,6 +291,21 @@ class User < ApplicationRecord
     setting_display_media == 'hide_all'
   end
 
+  def recent_ips
+    @recent_ips ||= begin
+      arr = []
+
+      session_activations.each do |session_activation|
+        arr << [session_activation.updated_at, session_activation.ip]
+      end
+
+      arr << [current_sign_in_at, current_sign_in_ip] if current_sign_in_ip.present?
+      arr << [last_sign_in_at, last_sign_in_ip] if last_sign_in_ip.present?
+
+      arr.sort_by(&:first).uniq(&:last).reverse!
+    end
+  end
+
   protected
 
   def send_devise_notification(notification, *args)
diff --git a/app/views/admin/accounts/index.html.haml b/app/views/admin/accounts/index.html.haml
index 7e9adb3ff..3a85324c9 100644
--- a/app/views/admin/accounts/index.html.haml
+++ b/app/views/admin/accounts/index.html.haml
@@ -22,7 +22,7 @@
 
 = form_tag admin_accounts_url, method: 'GET', class: 'simple_form' do
   .fields-group
-    - Admin::FilterHelper::ACCOUNT_FILTERS.each do |key|
+    - AccountFilter::KEYS.each do |key|
       - if params[key].present?
         = hidden_field_tag key, params[key]
 
diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml
index 9f1e3816b..1429f56d5 100644
--- a/app/views/admin/accounts/show.html.haml
+++ b/app/views/admin/accounts/show.html.haml
@@ -139,12 +139,12 @@
               %time.formatted{ datetime: @account.created_at.iso8601, title: l(@account.created_at) }= l @account.created_at
             %td
 
-          %tr
-            %th= t('admin.accounts.most_recent_ip')
-            %td= @account.user_current_sign_in_ip
-            %td
-              - if @account.user_current_sign_in_ip
-                = table_link_to 'search', t('admin.accounts.search_same_ip'), admin_accounts_path(ip: @account.user_current_sign_in_ip)
+          - @account.user.recent_ips.each_with_index do |(_, ip), i|
+            %tr
+              - if i.zero?
+                %th{ rowspan: @account.user.recent_ips.size }= t('admin.accounts.most_recent_ip')
+              %td= ip
+              %td= table_link_to 'search', t('admin.accounts.search_same_ip'), admin_accounts_path(ip: ip)
 
           %tr
             %th= t('admin.accounts.most_recent_activity')
diff --git a/app/views/admin/custom_emojis/index.html.haml b/app/views/admin/custom_emojis/index.html.haml
index dc9ebf507..d3705a36e 100644
--- a/app/views/admin/custom_emojis/index.html.haml
+++ b/app/views/admin/custom_emojis/index.html.haml
@@ -22,7 +22,7 @@
 
 = form_tag admin_custom_emojis_url, method: 'GET', class: 'simple_form' do
   .fields-group
-    - Admin::FilterHelper::CUSTOM_EMOJI_FILTERS.each do |key|
+    - CustomEmojiFilter::KEYS.each do |key|
       = hidden_field_tag key, params[key] if params[key].present?
 
     - %i(shortcode by_domain).each do |key|
@@ -36,7 +36,7 @@
 = form_for(@form, url: batch_admin_custom_emojis_path) do |f|
   = hidden_field_tag :page, params[:page] || 1
 
-  - Admin::FilterHelper::CUSTOM_EMOJI_FILTERS.each do |key|
+  - CustomEmojiFilter::KEYS.each do |key|
     = hidden_field_tag key, params[key] if params[key].present?
 
   .batch-table
diff --git a/app/views/admin/email_domain_blocks/index.html.haml b/app/views/admin/email_domain_blocks/index.html.haml
index c1cc470b6..6015cfac0 100644
--- a/app/views/admin/email_domain_blocks/index.html.haml
+++ b/app/views/admin/email_domain_blocks/index.html.haml
@@ -4,7 +4,7 @@
 - content_for :heading_actions do
   = link_to t('admin.email_domain_blocks.add_new'), new_admin_email_domain_block_path, class: 'button'
 
-- if @email_domain_blocks.count == 0
+- if @email_domain_blocks.empty?
   %div.muted-hint.center-text=t 'admin.email_domain_blocks.empty'
 - else
   .table-wrapper
diff --git a/app/views/admin/instances/index.html.haml b/app/views/admin/instances/index.html.haml
index 1d85aa75e..0b299acc5 100644
--- a/app/views/admin/instances/index.html.haml
+++ b/app/views/admin/instances/index.html.haml
@@ -19,9 +19,8 @@
 - unless whitelist_mode?
   = form_tag admin_instances_url, method: 'GET', class: 'simple_form' do
     .fields-group
-      - Admin::FilterHelper::INSTANCES_FILTERS.each do |key|
-        - if params[key].present?
-          = hidden_field_tag key, params[key]
+      - InstanceFilter::KEYS.each do |key|
+        = hidden_field_tag key, params[key] if params[key].present?
 
       - %i(by_domain).each do |key|
         .input.string.optional
diff --git a/app/views/admin/reports/_status.html.haml b/app/views/admin/reports/_status.html.haml
index 425d315e1..fa15796d2 100644
--- a/app/views/admin/reports/_status.html.haml
+++ b/app/views/admin/reports/_status.html.haml
@@ -14,9 +14,12 @@
     - unless status.proper.media_attachments.empty?
       - if status.proper.media_attachments.first.video?
         - video = status.proper.media_attachments.first
-        = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: !current_account&.user&.show_all_media? && status.proper.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), blurhash: video.blurhash, sensitive: status.proper.sensitive?, visible: false, width: 610, height: 343, inline: true, alt: video.description
+      - elsif status.proper.media_attachments.first.audio?
+        - audio = status.proper.media_attachments.first
+        = react_component :audio, src: audio.file.url(:original), height: 110, alt: audio.description, duration: audio.file.meta.dig(:original, :duration)
       - else
-        = react_component :media_gallery, height: 343, sensitive: !current_account&.user&.show_all_media? && status.proper.sensitive? || current_account&.user&.hide_all_media?, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, media: status.proper.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
+        = react_component :media_gallery, height: 343, sensitive: status.proper.sensitive?, visible: false, media: status.proper.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
 
     .detailed-status__meta
       = link_to ActivityPub::TagManager.instance.url_for(status), class: 'detailed-status__datetime', target: stream_link_target, rel: 'noopener noreferrer' do
diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml
index 30c7549b0..0263b80fb 100644
--- a/app/views/admin/reports/index.html.haml
+++ b/app/views/admin/reports/index.html.haml
@@ -10,9 +10,8 @@
 
 = form_tag admin_reports_url, method: 'GET', class: 'simple_form' do
   .fields-group
-    - Admin::FilterHelper::REPORT_FILTERS.each do |key|
-      - if params[key].present?
-        = hidden_field_tag key, params[key]
+    - ReportFilter::KEYS.each do |key|
+      = hidden_field_tag key, params[key] if params[key].present?
 
     - %i(by_target_domain).each do |key|
       .input.string.optional
diff --git a/app/views/admin/tags/index.html.haml b/app/views/admin/tags/index.html.haml
index aa4f4c297..d20ed80f8 100644
--- a/app/views/admin/tags/index.html.haml
+++ b/app/views/admin/tags/index.html.haml
@@ -25,7 +25,7 @@
 
 = form_tag admin_tags_url, method: 'GET', class: 'simple_form' do
   .fields-group
-    - Admin::FilterHelper::TAGS_FILTERS.each do |key|
+    - TagFilter::KEYS.each do |key|
       = hidden_field_tag key, params[key] if params[key].present?
 
     - %i(name).each do |key|
@@ -40,9 +40,8 @@
 
 = form_for(@form, url: batch_admin_tags_path) do |f|
   = hidden_field_tag :page, params[:page] || 1
-  = hidden_field_tag :name, params[:name] if params[:name].present?
 
-  - Admin::FilterHelper::TAGS_FILTERS.each do |key|
+  - TagFilter::KEYS.each do |key|
     = hidden_field_tag key, params[key] if params[key].present?
 
   .batch-table.optional
diff --git a/app/views/filters/index.html.haml b/app/views/filters/index.html.haml
index 8ace638ca..b4d5333aa 100644
--- a/app/views/filters/index.html.haml
+++ b/app/views/filters/index.html.haml
@@ -4,7 +4,7 @@
 - content_for :heading_actions do
   = link_to t('filters.new.title'), new_filter_path, class: 'button'
 
-- if @filters.count == 0
+- if @filters.empty?
   %div.muted-hint.center-text= t 'filters.index.empty'
 - else
   .table-wrapper
diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml
index 10fa42e8b..ec3629dd8 100644
--- a/app/views/layouts/admin.html.haml
+++ b/app/views/layouts/admin.html.haml
@@ -1,3 +1,6 @@
+- content_for :header_tags do
+  = render_initial_state
+
 - content_for :content do
   .admin-wrapper
     .sidebar-wrapper
diff --git a/app/views/layouts/embedded.html.haml b/app/views/layouts/embedded.html.haml
index 33e3714f8..6695b12dd 100644
--- a/app/views/layouts/embedded.html.haml
+++ b/app/views/layouts/embedded.html.haml
@@ -10,6 +10,7 @@
     - if storage_host?
       %link{ rel: 'dns-prefetch', href: storage_host }/
 
+    = render_initial_state
     = javascript_pack_tag "locales", integrity: true, crossorigin: 'anonymous'
     - if @theme
       - if @theme[:supported_locales].include? I18n.locale.to_s
@@ -21,3 +22,6 @@
 
   %body.embed
     = yield
+
+    %div{ style: 'display: none'}
+      = render file: Rails.root.join('app', 'javascript', 'images', 'logo_transparent.svg')
diff --git a/app/views/relationships/show.html.haml b/app/views/relationships/show.html.haml
index 408390a35..a6f8cdc15 100644
--- a/app/views/relationships/show.html.haml
+++ b/app/views/relationships/show.html.haml
@@ -30,10 +30,9 @@
 
 = form_for(@form, url: relationships_path, method: :patch) do |f|
   = hidden_field_tag :page, params[:page] || 1
-  = hidden_field_tag :relationship, params[:relationship]
-  = hidden_field_tag :status, params[:status]
-  = hidden_field_tag :activity, params[:activity]
-  = hidden_field_tag :order, params[:order]
+
+  - RelationshipFilter::KEYS.each do |key|
+    = hidden_field_tag key, params[key] if params[key].present?
 
   .batch-table
     .batch-table__toolbar
diff --git a/app/views/settings/applications/index.html.haml b/app/views/settings/applications/index.html.haml
index 1cb94760f..a1f904a3a 100644
--- a/app/views/settings/applications/index.html.haml
+++ b/app/views/settings/applications/index.html.haml
@@ -4,7 +4,7 @@
 - content_for :heading_actions do
   = link_to t('doorkeeper.applications.index.new'), new_settings_application_path, class: 'button'
 
-- if @applications.count == 0
+- if @applications.empty?
   %div.muted-hint.center-text=t 'doorkeeper.applications.index.empty'
 - else
   .table-wrapper
diff --git a/app/views/statuses/_detailed_status.html.haml b/app/views/statuses/_detailed_status.html.haml
index 3fa52d3f2..021390e47 100644
--- a/app/views/statuses/_detailed_status.html.haml
+++ b/app/views/statuses/_detailed_status.html.haml
@@ -29,14 +29,14 @@
   - 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), blurhash: video.blurhash, 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
+      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), blurhash: video.blurhash, sensitive: status.sensitive?, width: 670, height: 380, detailed: true, inline: true, alt: video.description do
         = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments }
     - elsif status.media_attachments.first.audio?
       - audio = status.media_attachments.first
       = react_component :audio, src: audio.file.url(:original), height: 130, alt: audio.description, preload: true, duration: audio.file.meta.dig(:original, :duration) do
         = render partial: 'statuses/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 } do
+      = react_component :media_gallery, height: 380, sensitive: status.sensitive?, standalone: true, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do
         = render partial: 'statuses/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/statuses/_simple_status.html.haml b/app/views/statuses/_simple_status.html.haml
index e9c22d9ba..4e17b6347 100644
--- a/app/views/statuses/_simple_status.html.haml
+++ b/app/views/statuses/_simple_status.html.haml
@@ -33,14 +33,14 @@
   - 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), blurhash: video.blurhash, 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
+      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), blurhash: video.blurhash, sensitive: status.sensitive?, width: 610, height: 343, inline: true, alt: video.description do
         = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments }
     - elsif status.media_attachments.first.audio?
       - audio = status.media_attachments.first
       = react_component :audio, src: audio.file.url(:original), height: 110, alt: audio.description, duration: audio.file.meta.dig(:original, :duration) do
         = render partial: 'statuses/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 } do
+      = react_component :media_gallery, height: 343, sensitive: status.sensitive?, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do
         = render partial: 'statuses/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