about summary refs log tree commit diff
diff options
context:
space:
mode:
authorStarfall <us@starfall.systems>2020-09-02 01:15:02 -0500
committerStarfall <us@starfall.systems>2020-09-02 01:15:02 -0500
commitfd0b806603dbd632d259d06af789c7195c7264dc (patch)
treea9ba1b09763ca15a96f927e361bb878b9833bc7d
parenta43c1d3f56c128c992f34b8e2b968de14e02ac48 (diff)
parentd967251fdc3826ad27d30e55258cfa4cdfd7c871 (diff)
Merge branch 'glitch' into main
-rw-r--r--.codeclimate.yml2
-rw-r--r--.rubocop.yml175
-rw-r--r--Dockerfile19
-rw-r--r--Gemfile6
-rw-r--r--Gemfile.lock32
-rw-r--r--app/controllers/api/base_controller.rb1
-rw-r--r--app/controllers/api/v1/admin/accounts_controller.rb2
-rw-r--r--app/controllers/api/v1/admin/reports_controller.rb2
-rw-r--r--app/controllers/api/v1/bookmarks_controller.rb2
-rw-r--r--app/controllers/api/v1/conversations_controller.rb2
-rw-r--r--app/controllers/api/v1/crypto/encrypted_messages_controller.rb2
-rw-r--r--app/controllers/api/v1/favourites_controller.rb2
-rw-r--r--app/controllers/api/v1/scheduled_statuses_controller.rb2
-rw-r--r--app/controllers/concerns/cache_concern.rb2
-rw-r--r--app/controllers/concerns/challengable_concern.rb1
-rw-r--r--app/controllers/concerns/signature_verification.rb2
-rw-r--r--app/helpers/application_helper.rb2
-rw-r--r--app/javascript/flavours/glitch/features/list_timeline/index.js28
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js49
-rw-r--r--app/javascript/flavours/glitch/reducers/compose.js2
-rw-r--r--app/javascript/flavours/glitch/styles/components/index.scss20
-rw-r--r--app/javascript/flavours/glitch/util/emoji/index.js2
-rw-r--r--app/javascript/mastodon/actions/lists.js4
-rw-r--r--app/javascript/mastodon/features/emoji/emoji.js2
-rw-r--r--app/javascript/mastodon/features/getting_started/index.js37
-rw-r--r--app/javascript/mastodon/features/list_timeline/index.js30
-rw-r--r--app/javascript/mastodon/features/ui/components/focal_point_modal.js49
-rw-r--r--app/javascript/styles/mastodon/components.scss27
-rw-r--r--app/lib/activitypub/linked_data_signature.rb4
-rw-r--r--app/lib/entity_cache.rb2
-rw-r--r--app/lib/feed_manager.rb2
-rw-r--r--app/lib/formatter.rb2
-rw-r--r--app/lib/request.rb2
-rw-r--r--app/lib/sidekiq_error_handler.rb2
-rw-r--r--app/models/account_conversation.rb4
-rw-r--r--app/models/concerns/paginable.rb6
-rw-r--r--app/models/preview_card.rb2
-rw-r--r--app/models/user.rb8
-rw-r--r--app/services/fan_out_on_write_service.rb6
-rw-r--r--app/services/fetch_link_card_service.rb2
-rw-r--r--app/services/notify_service.rb2
-rw-r--r--app/services/update_account_service.rb4
-rw-r--r--config/webpack/rules/index.js2
-rw-r--r--config/webpack/rules/node_modules.js5
-rw-r--r--config/webpack/rules/tesseract.js14
-rw-r--r--config/webpack/shared.js7
-rw-r--r--lib/mastodon/media_cli.rb3
-rw-r--r--lib/mastodon/redis_config.rb2
-rw-r--r--lib/mastodon/version.rb6
-rw-r--r--lib/paperclip/color_extractor.rb4
-rw-r--r--lib/tasks/emojis.rake2
-rw-r--r--package.json7
-rw-r--r--public/emoji/1f41e_border.svg29
-rw-r--r--yarn.lock305
54 files changed, 542 insertions, 398 deletions
diff --git a/.codeclimate.yml b/.codeclimate.yml
index b4ec9400e..9c9b4517a 100644
--- a/.codeclimate.yml
+++ b/.codeclimate.yml
@@ -30,7 +30,7 @@ plugins:
     channel: eslint-7
   rubocop:
     enabled: true
-    channel: rubocop-0-82
+    channel: rubocop-0-88
   sass-lint:
     enabled: true
 exclude_patterns:
diff --git a/.rubocop.yml b/.rubocop.yml
index 25e0fa940..14728bf0e 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -25,30 +25,68 @@ Layout/AccessModifierIndentation:
 Layout/EmptyLineAfterMagicComment:
   Enabled: false
 
+Layout/EmptyLineAfterGuardClause:
+  Enabled: false
+
+Layout/EmptyLinesAroundAttributeAccessor:
+  Enabled: true
+
+Layout/HashAlignment:
+  Enabled: false
+  # EnforcedHashRocketStyle: table
+  # EnforcedColonStyle: table
+
+Layout/SpaceAroundMethodCallOperator:
+  Enabled: true
+
 Layout/SpaceInsideHashLiteralBraces:
   EnforcedStyle: space
 
+Lint/DeprecatedOpenSSLConstant:
+  Enabled: true
+
+Lint/DuplicateElsifCondition:
+  Enabled: true
+
+Lint/MixedRegexpCaptureTypes:
+  Enabled: true
+
+Lint/RaiseException:
+  Enabled: true
+
+Lint/StructNewOverride:
+  Enabled: true
+
 Lint/UselessAccessModifier:
   ContextCreatingMethods:
     - class_methods
 
 Metrics/AbcSize:
   Max: 100
+  Exclude:
+    - 'lib/mastodon/*_cli.rb'
 
 Metrics/BlockLength:
-  Max: 35
+  Max: 55
   Exclude:
     - 'lib/tasks/**/*'
+    - 'lib/mastodon/*_cli.rb'
 
 Metrics/BlockNesting:
   Max: 3
+  Exclude:
+    - 'lib/mastodon/*_cli.rb'
 
 Metrics/ClassLength:
   CountComments: false
-  Max: 300
+  Max: 400
+  Exclude:
+    - 'lib/mastodon/*_cli.rb'
 
 Metrics/CyclomaticComplexity:
   Max: 25
+  Exclude:
+    - 'lib/mastodon/*_cli.rb'
 
 Layout/LineLength:
   AllowURI: true
@@ -56,7 +94,9 @@ Layout/LineLength:
 
 Metrics/MethodLength:
   CountComments: false
-  Max: 55
+  Max: 65
+  Exclude:
+    - 'lib/mastodon/*_cli.rb'
 
 Metrics/ModuleLength:
   CountComments: false
@@ -67,24 +107,29 @@ Metrics/ParameterLists:
   CountKeywordArgs: true
 
 Metrics/PerceivedComplexity:
-  Max: 20
+  Max: 25
 
 Naming/MemoizedInstanceVariableName:
   Enabled: false
 
+Naming/MethodParameterName:
+  Enabled: true
+
 Rails:
   Enabled: true
 
-Rails/EnumHash:
+Rails/ApplicationController:
   Enabled: false
+  Exclude:
+    - 'app/controllers/well_known/**/*.rb'
 
-Rails/HasAndBelongsToMany:
+Rails/BelongsTo:
   Enabled: false
 
-Rails/SkipsModelValidations:
+Rails/ContentTag:
   Enabled: false
 
-Rails/HttpStatus:
+Rails/EnumHash:
   Enabled: false
 
 Rails/Exit:
@@ -92,9 +137,60 @@ Rails/Exit:
     - 'lib/mastodon/*'
     - 'lib/cli.rb'
 
+Rails/FilePath:
+  Enabled: false
+
+Rails/HasAndBelongsToMany:
+  Enabled: false
+
+Rails/HasManyOrHasOneDependent:
+  Enabled: false
+
 Rails/HelperInstanceVariable:
   Enabled: false
 
+Rails/HttpStatus:
+  Enabled: false
+
+Rails/IndexBy:
+  Enabled: false
+
+Rails/InverseOf:
+  Enabled: false
+
+Rails/LexicallyScopedActionFilter:
+  Enabled: false
+
+Rails/OutputSafety:
+  Enabled: true
+
+Rails/RakeEnvironment:
+  Enabled: false
+
+Rails/RedundantForeignKey:
+  Enabled: false
+
+Rails/SkipsModelValidations:
+  Enabled: false
+
+Rails/UniqueValidationWithoutIndex:
+  Enabled: false
+
+Style/AccessorGrouping:
+  Enabled: true
+
+Style/AccessModifierDeclarations:
+  Enabled: false
+
+Style/ArrayCoercion:
+  Enabled: true
+
+Style/BisectedAttrAccessor:
+  Enabled: true
+
+Style/CaseLikeIf:
+  Enabled: false
+
 Style/ClassAndModuleChildren:
   Enabled: false
 
@@ -109,6 +205,15 @@ Style/Documentation:
 Style/DoubleNegation:
   Enabled: true
 
+Style/ExpandPathArguments:
+  Enabled: false
+
+Style/ExponentialNotation:
+  Enabled: true
+
+Style/FormatString:
+  Enabled: false
+
 Style/FormatStringToken:
   Enabled: false
 
@@ -118,9 +223,33 @@ Style/FrozenStringLiteralComment:
 Style/GuardClause:
   Enabled: false
 
+Style/HashAsLastArrayItem:
+  Enabled: false
+
+Style/HashEachMethods:
+  Enabled: true
+
+Style/HashLikeCase:
+  Enabled: true
+
+Style/HashTransformKeys:
+  Enabled: true
+
+Style/HashTransformValues:
+  Enabled: false
+
+Style/IfUnlessModifier:
+  Enabled: false
+
+Style/InverseMethods:
+  Enabled: false
+
 Style/Lambda:
   Enabled: false
 
+Style/MutableConstant:
+  Enabled: false
+
 Style/PercentLiteralDelimiters:
   PreferredDelimiters:
     '%i': '()'
@@ -129,9 +258,36 @@ Style/PercentLiteralDelimiters:
 Style/PerlBackrefs:
   AutoCorrect: false
 
+Style/RedundantAssignment:
+  Enabled: false
+
+Style/RedundantFetchBlock:
+  Enabled: true
+
+Style/RedundantFileExtensionInRequire:
+  Enabled: true
+
+Style/RedundantRegexpCharacterClass:
+  Enabled: false
+
+Style/RedundantRegexpEscape:
+  Enabled: false
+
+Style/RedundantReturn:
+  Enabled: true
+
 Style/RegexpLiteral:
   Enabled: false
 
+Style/RescueStandardError:
+  Enabled: false
+
+Style/SignalException:
+  Enabled: false
+
+Style/SlicingWithRange:
+  Enabled: true
+
 Style/SymbolArray:
   Enabled: false
 
@@ -140,3 +296,6 @@ Style/TrailingCommaInArrayLiteral:
 
 Style/TrailingCommaInHashLiteral:
   EnforcedStyleForMultiline: 'comma'
+
+Style/UnpackFirst:
+  Enabled: false
diff --git a/Dockerfile b/Dockerfile
index 78e4daf9e..ad2b20926 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -36,7 +36,8 @@ RUN apt update && \
 	./autogen.sh && \
 	./configure --prefix=/opt/jemalloc && \
 	make -j$(nproc) > /dev/null && \
-	make install_bin install_include install_lib
+	make install_bin install_include install_lib && \
+	cd .. && rm -rf jemalloc-$JE_VER $JE_VER.tar.gz
 
 # Install Ruby
 ENV RUBY_VER="2.6.6"
@@ -56,7 +57,8 @@ RUN apt update && \
 	  --disable-install-doc && \
 	ln -s /opt/jemalloc/lib/* /usr/lib/ && \
 	make -j$(nproc) > /dev/null && \
-	make install
+	make install && \
+	cd .. && rm -rf ruby-$RUBY_VER.tar.gz ruby-$RUBY_VER
 
 ENV PATH="${PATH}:/opt/ruby/bin:/opt/node/bin"
 
@@ -107,11 +109,14 @@ RUN apt -y --no-install-recommends install \
 	rm -rf /var/lib/apt/lists/*
 
 # Add tini
-ENV TINI_VERSION="0.18.0"
-ENV TINI_SUM="12d20136605531b09a2c2dac02ccee85e1b874eb322ef6baf7561cd93f93c855"
-ADD https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini /tini
-RUN echo "$TINI_SUM tini" | sha256sum -c -
-RUN chmod +x /tini
+ENV TINI_VERSION="0.19.0"
+RUN dpkgArch="$(dpkg --print-architecture)" && \
+	ARCH=$dpkgArch && \
+	wget https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-$ARCH \
+	https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-$ARCH.sha256sum && \
+	cat tini-$ARCH.sha256sum | sha256sum -c - && \
+	mv tini-$ARCH /tini && rm tini-$ARCH.sha256sum && \
+	chmod +x /tini
 
 # Copy over mastodon source, and dependencies from building, and set permissions
 COPY --chown=mastodon:mastodon . /opt/mastodon
diff --git a/Gemfile b/Gemfile
index 90c0198b5..6e0ece406 100644
--- a/Gemfile
+++ b/Gemfile
@@ -20,7 +20,7 @@ gem 'makara', '~> 0.4'
 gem 'pghero', '~> 2.7'
 gem 'dotenv-rails', '~> 2.7'
 
-gem 'aws-sdk-s3', '~> 1.78', require: false
+gem 'aws-sdk-s3', '~> 1.79', require: false
 gem 'fog-core', '<= 2.1.0'
 gem 'fog-openstack', '~> 0.3', require: false
 gem 'paperclip', '~> 6.0'
@@ -129,7 +129,7 @@ group :test do
   gem 'rspec-sidekiq', '~> 3.1'
   gem 'simplecov', '~> 0.19', require: false
   gem 'webmock', '~> 3.8'
-  gem 'parallel_tests', '~> 3.1'
+  gem 'parallel_tests', '~> 3.2'
   gem 'rspec_junit_formatter', '~> 0.4'
 end
 
@@ -142,7 +142,7 @@ group :development do
   gem 'letter_opener', '~> 1.7'
   gem 'letter_opener_web', '~> 1.4'
   gem 'memory_profiler'
-  gem 'rubocop', '~> 0.86', require: false
+  gem 'rubocop', '~> 0.88', require: false
   gem 'rubocop-rails', '~> 2.6', require: false
   gem 'brakeman', '~> 4.9', require: false
   gem 'bundler-audit', '~> 0.7', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index a16cacc70..e72a3be84 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -79,16 +79,16 @@ GEM
       cocaine (~> 0.5.3)
     awrence (1.1.1)
     aws-eventstream (1.1.0)
-    aws-partitions (1.358.0)
-    aws-sdk-core (3.104.4)
+    aws-partitions (1.363.0)
+    aws-sdk-core (3.105.0)
       aws-eventstream (~> 1, >= 1.0.2)
       aws-partitions (~> 1, >= 1.239.0)
       aws-sigv4 (~> 1.1)
       jmespath (~> 1.0)
-    aws-sdk-kms (1.36.0)
+    aws-sdk-kms (1.37.0)
       aws-sdk-core (~> 3, >= 3.99.0)
       aws-sigv4 (~> 1.1)
-    aws-sdk-s3 (1.78.0)
+    aws-sdk-s3 (1.79.1)
       aws-sdk-core (~> 3, >= 3.104.3)
       aws-sdk-kms (~> 1)
       aws-sigv4 (~> 1.1)
@@ -99,7 +99,6 @@ GEM
       coderay (>= 1.0.0)
       erubi (>= 1.0.0)
       rack (>= 0.9.0)
-    bigdecimal (2.0.0)
     bindata (2.4.8)
     binding_of_caller (0.8.0)
       debug_inspector (>= 0.0.1)
@@ -247,7 +246,7 @@ GEM
       http (~> 4.0)
       nokogiri (~> 1.8)
       oj (~> 3.0)
-    hamlit (2.11.0)
+    hamlit (2.11.1)
       temple (>= 0.8.2)
       thor
       tilt
@@ -334,7 +333,7 @@ GEM
       activesupport (>= 4)
       railties (>= 4)
       request_store (~> 1.0)
-    loofah (2.6.0)
+    loofah (2.7.0)
       crass (~> 1.0.2)
       nokogiri (>= 1.5.9)
     mail (2.7.1)
@@ -347,7 +346,7 @@ GEM
       redis (>= 3.0.5)
     memory_profiler (0.9.14)
     method_source (1.0.0)
-    microformats (4.2.0)
+    microformats (4.2.1)
       json (~> 2.2)
       nokogiri (~> 1.10)
     mime-types (3.3.1)
@@ -374,8 +373,7 @@ GEM
       concurrent-ruby (~> 1.0, >= 1.0.2)
       sidekiq (>= 3.5)
       statsd-ruby (~> 1.4, >= 1.4.0)
-    oj (3.10.12)
-      bigdecimal (>= 1.0, < 3)
+    oj (3.10.13)
     omniauth (1.9.1)
       hashie (>= 3.4.6)
       rack (>= 1.6.2, < 3)
@@ -400,7 +398,7 @@ GEM
       av (~> 0.9.0)
       paperclip (>= 2.5.2)
     parallel (1.19.2)
-    parallel_tests (3.1.0)
+    parallel_tests (3.2.0)
       parallel
     parser (2.7.1.4)
       ast (~> 2.4.1)
@@ -538,13 +536,13 @@ GEM
     rspec-support (3.9.3)
     rspec_junit_formatter (0.4.1)
       rspec-core (>= 2, < 4, != 2.12.0)
-    rubocop (0.86.0)
+    rubocop (0.88.0)
       parallel (~> 1.10)
-      parser (>= 2.7.0.1)
+      parser (>= 2.7.1.1)
       rainbow (>= 2.2.2, < 4.0)
       regexp_parser (>= 1.7)
       rexml
-      rubocop-ast (>= 0.0.3, < 1.0)
+      rubocop-ast (>= 0.1.0, < 1.0)
       ruby-progressbar (~> 1.7)
       unicode-display_width (>= 1.4.0, < 2.0)
     rubocop-ast (0.3.0)
@@ -683,7 +681,7 @@ DEPENDENCIES
   active_record_query_trace (~> 1.7)
   addressable (~> 2.7)
   annotate (~> 3.1)
-  aws-sdk-s3 (~> 1.78)
+  aws-sdk-s3 (~> 1.79)
   better_errors (~> 2.7)
   binding_of_caller (~> 0.7)
   blurhash (~> 0.1)
@@ -754,7 +752,7 @@ DEPENDENCIES
   paperclip (~> 6.0)
   paperclip-av-transcoder (~> 0.6)
   parallel (~> 1.19)
-  parallel_tests (~> 3.1)
+  parallel_tests (~> 3.2)
   parslet
   pg (~> 1.2)
   pghero (~> 2.7)
@@ -782,7 +780,7 @@ DEPENDENCIES
   rspec-rails (~> 4.0)
   rspec-sidekiq (~> 3.1)
   rspec_junit_formatter (~> 0.4)
-  rubocop (~> 0.86)
+  rubocop (~> 0.88)
   rubocop-rails (~> 2.6)
   ruby-progressbar (~> 1.10)
   sanitize (~> 5.2)
diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb
index 045e7dd26..467225547 100644
--- a/app/controllers/api/base_controller.rb
+++ b/app/controllers/api/base_controller.rb
@@ -71,6 +71,7 @@ class Api::BaseController < ApplicationController
 
   def limit_param(default_limit)
     return default_limit unless params[:limit]
+
     [params[:limit].to_i.abs, default_limit * 2].min
   end
 
diff --git a/app/controllers/api/v1/admin/accounts_controller.rb b/app/controllers/api/v1/admin/accounts_controller.rb
index c35ea5ab2..24c7fbef1 100644
--- a/app/controllers/api/v1/admin/accounts_controller.rb
+++ b/app/controllers/api/v1/admin/accounts_controller.rb
@@ -79,7 +79,7 @@ class Api::V1::Admin::AccountsController < Api::BaseController
   private
 
   def set_accounts
-    @accounts = filtered_accounts.order(id: :desc).includes(user: [:invite_request, :invite]).paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
+    @accounts = filtered_accounts.order(id: :desc).includes(user: [:invite_request, :invite]).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
   end
 
   def set_account
diff --git a/app/controllers/api/v1/admin/reports_controller.rb b/app/controllers/api/v1/admin/reports_controller.rb
index 1d48d3160..c8f4cd8d8 100644
--- a/app/controllers/api/v1/admin/reports_controller.rb
+++ b/app/controllers/api/v1/admin/reports_controller.rb
@@ -63,7 +63,7 @@ class Api::V1::Admin::ReportsController < Api::BaseController
   private
 
   def set_reports
-    @reports = filtered_reports.order(id: :desc).with_accounts.paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
+    @reports = filtered_reports.order(id: :desc).with_accounts.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
   end
 
   def set_report
diff --git a/app/controllers/api/v1/bookmarks_controller.rb b/app/controllers/api/v1/bookmarks_controller.rb
index 5c72f4a1a..aa3fb88f0 100644
--- a/app/controllers/api/v1/bookmarks_controller.rb
+++ b/app/controllers/api/v1/bookmarks_controller.rb
@@ -21,7 +21,7 @@ class Api::V1::BookmarksController < Api::BaseController
   end
 
   def results
-    @_results ||= account_bookmarks.eager_load(:status).paginate_by_id(
+    @_results ||= account_bookmarks.eager_load(:status).to_a_paginated_by_id(
       limit_param(DEFAULT_STATUSES_LIMIT),
       params_slice(:max_id, :since_id, :min_id)
     )
diff --git a/app/controllers/api/v1/conversations_controller.rb b/app/controllers/api/v1/conversations_controller.rb
index bc8013379..6c7583403 100644
--- a/app/controllers/api/v1/conversations_controller.rb
+++ b/app/controllers/api/v1/conversations_controller.rb
@@ -32,7 +32,7 @@ class Api::V1::ConversationsController < Api::BaseController
 
   def paginated_conversations
     AccountConversation.where(account: current_account)
-                       .paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
+                       .to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
   end
 
   def insert_pagination_headers
diff --git a/app/controllers/api/v1/crypto/encrypted_messages_controller.rb b/app/controllers/api/v1/crypto/encrypted_messages_controller.rb
index c764915e5..68cf4384f 100644
--- a/app/controllers/api/v1/crypto/encrypted_messages_controller.rb
+++ b/app/controllers/api/v1/crypto/encrypted_messages_controller.rb
@@ -26,7 +26,7 @@ class Api::V1::Crypto::EncryptedMessagesController < Api::BaseController
   end
 
   def set_encrypted_messages
-    @encrypted_messages = @current_device.encrypted_messages.paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
+    @encrypted_messages = @current_device.encrypted_messages.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
   end
 
   def insert_pagination_headers
diff --git a/app/controllers/api/v1/favourites_controller.rb b/app/controllers/api/v1/favourites_controller.rb
index 71a707d2a..21836bc17 100644
--- a/app/controllers/api/v1/favourites_controller.rb
+++ b/app/controllers/api/v1/favourites_controller.rb
@@ -21,7 +21,7 @@ class Api::V1::FavouritesController < Api::BaseController
   end
 
   def results
-    @_results ||= account_favourites.eager_load(:status).paginate_by_id(
+    @_results ||= account_favourites.eager_load(:status).to_a_paginated_by_id(
       limit_param(DEFAULT_STATUSES_LIMIT),
       params_slice(:max_id, :since_id, :min_id)
     )
diff --git a/app/controllers/api/v1/scheduled_statuses_controller.rb b/app/controllers/api/v1/scheduled_statuses_controller.rb
index 9950296f3..f90642a73 100644
--- a/app/controllers/api/v1/scheduled_statuses_controller.rb
+++ b/app/controllers/api/v1/scheduled_statuses_controller.rb
@@ -32,7 +32,7 @@ class Api::V1::ScheduledStatusesController < Api::BaseController
   private
 
   def set_statuses
-    @statuses = current_account.scheduled_statuses.paginate_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id))
+    @statuses = current_account.scheduled_statuses.to_a_paginated_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id))
   end
 
   def set_status
diff --git a/app/controllers/concerns/cache_concern.rb b/app/controllers/concerns/cache_concern.rb
index 189b92012..abbdb410a 100644
--- a/app/controllers/concerns/cache_concern.rb
+++ b/app/controllers/concerns/cache_concern.rb
@@ -49,6 +49,6 @@ module CacheConcern
   end
 
   def cache_collection_paginated_by_id(raw, klass, limit, options)
-    cache_collection raw.cache_ids.paginate_by_id(limit, options), klass
+    cache_collection raw.cache_ids.to_a_paginated_by_id(limit, options), klass
   end
 end
diff --git a/app/controllers/concerns/challengable_concern.rb b/app/controllers/concerns/challengable_concern.rb
index b29d90b3c..2995a25e0 100644
--- a/app/controllers/concerns/challengable_concern.rb
+++ b/app/controllers/concerns/challengable_concern.rb
@@ -32,7 +32,6 @@ module ChallengableConcern
     if params.key?(:form_challenge)
       if challenge_passed?
         session[:challenge_passed_at] = Time.now.utc
-        return
       else
         flash.now[:alert] = I18n.t('challenge.invalid_password')
         render_challenge
diff --git a/app/controllers/concerns/signature_verification.rb b/app/controllers/concerns/signature_verification.rb
index 18f549de9..f69c62ec2 100644
--- a/app/controllers/concerns/signature_verification.rb
+++ b/app/controllers/concerns/signature_verification.rb
@@ -131,7 +131,7 @@ module SignatureVerification
   end
 
   def verify_signature(account, signature, compare_signed_string)
-    if account.keypair.public_key.verify(OpenSSL::Digest::SHA256.new, signature, compare_signed_string)
+    if account.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), signature, compare_signed_string)
       @signed_request_account = account
       @signed_request_account
     end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 9ca11d573..321283178 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -163,6 +163,8 @@ module ApplicationHelper
     end
 
     json = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(state_params), serializer: InitialStateSerializer).to_json
+    # rubocop:disable Rails/OutputSafety
     content_tag(:script, json_escape(json).html_safe, id: 'initial-state', type: 'application/json')
+    # rubocop:enable Rails/OutputSafety
   end
 end
diff --git a/app/javascript/flavours/glitch/features/list_timeline/index.js b/app/javascript/flavours/glitch/features/list_timeline/index.js
index 908a65597..70e530bae 100644
--- a/app/javascript/flavours/glitch/features/list_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/list_timeline/index.js
@@ -14,13 +14,14 @@ import { openModal } from 'flavours/glitch/actions/modal';
 import MissingIndicator from 'flavours/glitch/components/missing_indicator';
 import LoadingIndicator from 'flavours/glitch/components/loading_indicator';
 import Icon from 'flavours/glitch/components/icon';
+import RadioButton from 'flavours/glitch/components/radio_button';
 
 const messages = defineMessages({
   deleteMessage: { id: 'confirmations.delete_list.message', defaultMessage: 'Are you sure you want to permanently delete this list?' },
   deleteConfirm: { id: 'confirmations.delete_list.confirm', defaultMessage: 'Delete' },
-  all_replies:   { id: 'lists.replies_policy.all_replies', defaultMessage: 'any followed user' },
-  no_replies:    { id: 'lists.replies_policy.no_replies', defaultMessage: 'no one' },
-  list_replies:  { id: 'lists.replies_policy.list_replies', defaultMessage: 'members of the list' },
+  all_replies:   { id: 'lists.replies_policy.all_replies', defaultMessage: 'Any followed user' },
+  no_replies:    { id: 'lists.replies_policy.no_replies', defaultMessage: 'No one' },
+  list_replies:  { id: 'lists.replies_policy.list_replies', defaultMessage: 'Members of the list' },
 });
 
 const mapStateToProps = (state, props) => ({
@@ -176,7 +177,7 @@ class ListTimeline extends React.PureComponent {
           multiColumn={multiColumn}
           bindToDocument={!multiColumn}
         >
-          <div className='column-header__links'>
+          <div className='column-settings__row column-header__links'>
             <button className='text-btn column-header__setting-btn' tabIndex='0' onClick={this.handleEditClick}>
               <Icon id='pencil' /> <FormattedMessage id='lists.edit' defaultMessage='Edit list' />
             </button>
@@ -187,19 +188,14 @@ class ListTimeline extends React.PureComponent {
           </div>
 
           { replies_policy !== undefined && (
-            <div>
+            <div role='group' aria-labelledby={`list-${id}-replies-policy`}>
+              <span id={`list-${id}-replies-policy`} className='column-settings__section'>
+                <FormattedMessage id='lists.replies_policy.title' defaultMessage='Show replies to:' />
+              </span>
               <div className='column-settings__row'>
-                <fieldset>
-                  <legend><FormattedMessage id='lists.replies_policy.title' defaultMessage='Show replies to:' /></legend>
-                  { ['no_replies', 'list_replies', 'all_replies'].map(policy => (
-                    <div className='setting-radio'>
-                      <input className='setting-radio__input' id={['setting', 'radio', id, policy].join('-')} type='radio' value={policy} checked={replies_policy === policy} onChange={this.handleRepliesPolicyChange} />
-                      <label className='setting-radio__label' htmlFor={['setting', 'radio', id, policy].join('-')}>
-                        <FormattedMessage {...messages[policy]} />
-                      </label>
-                    </div>
-                  ))}
-                </fieldset>
+                { ['no_replies', 'list_replies', 'all_replies'].map(policy => (
+                  <RadioButton name='order' value={policy} label={intl.formatMessage(messages[policy])} checked={replies_policy === policy} onChange={this.handleRepliesPolicyChange} />
+                ))}
               </div>
             </div>
           )}
diff --git a/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js b/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js
index c8b0e4bd7..b790b29a0 100644
--- a/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js
@@ -18,6 +18,8 @@ import { length } from 'stringz';
 import { Tesseract as fetchTesseract } from 'flavours/glitch/util/async-components';
 import GIFV from 'flavours/glitch/components/gifv';
 import { me } from 'flavours/glitch/util/initial_state';
+import tesseractCorePath from 'tesseract.js-core/tesseract-core.wasm.js';
+import tesseractWorkerPath from 'tesseract.js/dist/worker.min.js';
 
 const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
@@ -104,6 +106,7 @@ class FocalPointModal extends ImmutablePureComponent {
     dirty: false,
     progress: 0,
     loading: true,
+    ocrStatus: '',
   };
 
   componentWillMount () {
@@ -219,11 +222,18 @@ class FocalPointModal extends ImmutablePureComponent {
 
     this.setState({ detecting: true });
 
-    fetchTesseract().then(({ TesseractWorker }) => {
-      const worker = new TesseractWorker({
-        workerPath: `${assetHost}/packs/ocr/worker.min.js`,
-        corePath: `${assetHost}/packs/ocr/tesseract-core.wasm.js`,
-        langPath: `${assetHost}/ocr/lang-data`,
+    fetchTesseract().then(({ createWorker }) => {
+      const worker = createWorker({
+        workerPath: tesseractWorkerPath,
+        corePath: tesseractCorePath,
+        langPath: assetHost,
+        logger: ({ status, progress }) => {
+          if (status === 'recognizing text') {
+            this.setState({ ocrStatus: 'detecting', progress });
+          } else {
+            this.setState({ ocrStatus: 'preparing', progress });
+          }
+        },
       });
 
       let media_url = media.get('url');
@@ -236,12 +246,18 @@ class FocalPointModal extends ImmutablePureComponent {
         }
       }
 
-      worker.recognize(media_url)
-        .progress(({ progress }) => this.setState({ progress }))
-        .finally(() => worker.terminate())
-        .then(({ text }) => this.setState({ description: removeExtraLineBreaks(text), dirty: true, detecting: false }))
-        .catch(() => this.setState({ detecting: false }));
-    }).catch(() => this.setState({ detecting: false }));
+      (async () => {
+        await worker.load();
+        await worker.loadLanguage('eng');
+        await worker.initialize('eng');
+        const { data: { text } } = await worker.recognize(media_url);
+        this.setState({ description: removeExtraLineBreaks(text), dirty: true, detecting: false });
+        await worker.terminate();
+      })();
+    }).catch((e) => {
+      console.error(e);
+      this.setState({ detecting: false });
+    });
   }
 
   handleThumbnailChange = e => {
@@ -261,7 +277,7 @@ class FocalPointModal extends ImmutablePureComponent {
 
   render () {
     const { media, intl, account, onClose, isUploadingThumbnail } = this.props;
-    const { x, y, dragging, description, dirty, detecting, progress } = this.state;
+    const { x, y, dragging, description, dirty, detecting, progress, ocrStatus } = this.state;
 
     const width  = media.getIn(['meta', 'original', 'width']) || null;
     const height = media.getIn(['meta', 'original', 'height']) || null;
@@ -282,6 +298,13 @@ class FocalPointModal extends ImmutablePureComponent {
       descriptionLabel = <FormattedMessage id='upload_form.description' defaultMessage='Describe for the visually impaired' />;
     }
 
+    let ocrMessage = '';
+    if (ocrStatus === 'detecting') {
+      ocrMessage = <FormattedMessage id='upload_modal.analyzing_picture' defaultMessage='Analyzing picture…' />;
+    } else {
+      ocrMessage = <FormattedMessage id='upload_modal.preparing_ocr' defaultMessage='Preparing OCR…' />;
+    }
+
     return (
       <div className='modal-root__modal report-modal' style={{ maxWidth: 960 }}>
         <div className='report-modal__target'>
@@ -333,7 +356,7 @@ class FocalPointModal extends ImmutablePureComponent {
               />
 
               <div className='setting-text__modifiers'>
-                <UploadProgress progress={progress * 100} active={detecting} icon='file-text-o' message={<FormattedMessage id='upload_modal.analyzing_picture' defaultMessage='Analyzing picture…' />} />
+                <UploadProgress progress={progress * 100} active={detecting} icon='file-text-o' message={ocrMessage} />
               </div>
             </div>
 
diff --git a/app/javascript/flavours/glitch/reducers/compose.js b/app/javascript/flavours/glitch/reducers/compose.js
index 478883f91..e081c31ad 100644
--- a/app/javascript/flavours/glitch/reducers/compose.js
+++ b/app/javascript/flavours/glitch/reducers/compose.js
@@ -184,7 +184,7 @@ function continueThread (state, status) {
     map.set('in_reply_to', status.id);
     map.update(
       'advanced_options',
-      map => map.merge(new ImmutableMap({ do_not_federate: status.get('local_only') }))
+      map => map.merge(new ImmutableMap({ do_not_federate: status.local_only }))
     );
     map.set('privacy', status.visibility);
     map.set('sensitive', false);
diff --git a/app/javascript/flavours/glitch/styles/components/index.scss b/app/javascript/flavours/glitch/styles/components/index.scss
index 2231874bc..306e62342 100644
--- a/app/javascript/flavours/glitch/styles/components/index.scss
+++ b/app/javascript/flavours/glitch/styles/components/index.scss
@@ -1356,7 +1356,6 @@ button.icon-button.active i.fa-retweet {
 }
 
 .setting-toggle__label,
-.setting-radio__label,
 .setting-meta__label {
   color: $darker-text-color;
   display: inline-block;
@@ -1365,25 +1364,8 @@ button.icon-button.active i.fa-retweet {
   vertical-align: middle;
 }
 
-.setting-radio {
+.column-settings__row .radio-button {
   display: block;
-  line-height: 18px;
-}
-
-.setting-radio__label {
-  margin-bottom: 0;
-}
-
-.column-settings__row legend {
-  color: $darker-text-color;
-  cursor: default;
-  display: block;
-  font-weight: 500;
-  margin-top: 10px;
-}
-
-.setting-radio__input {
-  vertical-align: middle;
 }
 
 .setting-meta__label {
diff --git a/app/javascript/flavours/glitch/util/emoji/index.js b/app/javascript/flavours/glitch/util/emoji/index.js
index 61f211c92..a59e17ddb 100644
--- a/app/javascript/flavours/glitch/util/emoji/index.js
+++ b/app/javascript/flavours/glitch/util/emoji/index.js
@@ -12,7 +12,7 @@ const emojiFilenames = (emojis) => {
 };
 
 // Emoji requiring extra borders depending on theme
-const darkEmoji = emojiFilenames(['🎱', '🐜', '⚫', '🖤', '⬛', '◼️', '◾', '◼️', '✒️', '▪️', '💣', '🎳', '📷', '📸', '♣️', '🕶️', '✴️', '🔌', '💂‍♀️', '📽️', '🍳', '🦍', '💂', '🔪', '🕳️', '🕹️', '🕋', '🖊️', '🖋️', '💂‍♂️', '🎤', '🎓', '🎥', '🎼', '♠️', '🎩', '🦃', '📼', '📹', '🎮', '🐃', '🏴']);
+const darkEmoji = emojiFilenames(['🎱', '🐜', '⚫', '🖤', '⬛', '◼️', '◾', '◼️', '✒️', '▪️', '💣', '🎳', '📷', '📸', '♣️', '🕶️', '✴️', '🔌', '💂‍♀️', '📽️', '🍳', '🦍', '💂', '🔪', '🕳️', '🕹️', '🕋', '🖊️', '🖋️', '💂‍♂️', '🎤', '🎓', '🎥', '🎼', '♠️', '🎩', '🦃', '📼', '📹', '🎮', '🐃', '🏴', '🐞']);
 const lightEmoji = emojiFilenames(['👽', '⚾', '🐔', '☁️', '💨', '🕊️', '👀', '🍥', '👻', '🐐', '❕', '❔', '⛸️', '🌩️', '🔊', '🔇', '📃', '🌧️', '🐏', '🍚', '🍙', '🐓', '🐑', '💀', '☠️', '🌨️', '🔉', '🔈', '💬', '💭', '🏐', '🏳️', '⚪', '⬜', '◽', '◻️', '▫️']);
 
 const emojiFilename = (filename) => {
diff --git a/app/javascript/mastodon/actions/lists.js b/app/javascript/mastodon/actions/lists.js
index d736bacef..5ab922436 100644
--- a/app/javascript/mastodon/actions/lists.js
+++ b/app/javascript/mastodon/actions/lists.js
@@ -150,10 +150,10 @@ export const createListFail = error => ({
   error,
 });
 
-export const updateList = (id, title, shouldReset) => (dispatch, getState) => {
+export const updateList = (id, title, shouldReset, replies_policy) => (dispatch, getState) => {
   dispatch(updateListRequest(id));
 
-  api(getState).put(`/api/v1/lists/${id}`, { title }).then(({ data }) => {
+  api(getState).put(`/api/v1/lists/${id}`, { title, replies_policy }).then(({ data }) => {
     dispatch(updateListSuccess(data));
 
     if (shouldReset) {
diff --git a/app/javascript/mastodon/features/emoji/emoji.js b/app/javascript/mastodon/features/emoji/emoji.js
index f7d3cfd08..5237b25f0 100644
--- a/app/javascript/mastodon/features/emoji/emoji.js
+++ b/app/javascript/mastodon/features/emoji/emoji.js
@@ -12,7 +12,7 @@ const emojiFilenames = (emojis) => {
 };
 
 // Emoji requiring extra borders depending on theme
-const darkEmoji = emojiFilenames(['🎱', '🐜', '⚫', '🖤', '⬛', '◼️', '◾', '◼️', '✒️', '▪️', '💣', '🎳', '📷', '📸', '♣️', '🕶️', '✴️', '🔌', '💂‍♀️', '📽️', '🍳', '🦍', '💂', '🔪', '🕳️', '🕹️', '🕋', '🖊️', '🖋️', '💂‍♂️', '🎤', '🎓', '🎥', '🎼', '♠️', '🎩', '🦃', '📼', '📹', '🎮', '🐃', '🏴']);
+const darkEmoji = emojiFilenames(['🎱', '🐜', '⚫', '🖤', '⬛', '◼️', '◾', '◼️', '✒️', '▪️', '💣', '🎳', '📷', '📸', '♣️', '🕶️', '✴️', '🔌', '💂‍♀️', '📽️', '🍳', '🦍', '💂', '🔪', '🕳️', '🕹️', '🕋', '🖊️', '🖋️', '💂‍♂️', '🎤', '🎓', '🎥', '🎼', '♠️', '🎩', '🦃', '📼', '📹', '🎮', '🐃', '🏴', '🐞']);
 const lightEmoji = emojiFilenames(['👽', '⚾', '🐔', '☁️', '💨', '🕊️', '👀', '🍥', '👻', '🐐', '❕', '❔', '⛸️', '🌩️', '🔊', '🔇', '📃', '🌧️', '🐏', '🍚', '🍙', '🐓', '🐑', '💀', '☠️', '🌨️', '🔉', '🔈', '💬', '💭', '🏐', '🏳️', '⚪', '⬜', '◽', '◻️', '▫️']);
 
 const emojiFilename = (filename) => {
diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.js
index d9838e1c7..02e1bbba5 100644
--- a/app/javascript/mastodon/features/getting_started/index.js
+++ b/app/javascript/mastodon/features/getting_started/index.js
@@ -40,6 +40,7 @@ const messages = defineMessages({
 
 const mapStateToProps = state => ({
   myAccount: state.getIn(['accounts', me]),
+  columns: state.getIn(['settings', 'columns']),
   unreadFollowRequests: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableList()).size,
 });
 
@@ -89,60 +90,66 @@ class GettingStarted extends ImmutablePureComponent {
   }
 
   render () {
-    const { intl, myAccount, multiColumn, unreadFollowRequests } = this.props;
+    const { intl, myAccount, columns, multiColumn, unreadFollowRequests } = this.props;
 
     const navItems = [];
-    let i = 1;
     let height = (multiColumn) ? 0 : 60;
 
     if (multiColumn) {
       navItems.push(
-        <ColumnSubheading key={i++} text={intl.formatMessage(messages.discover)} />,
-        <ColumnLink key={i++} icon='users' text={intl.formatMessage(messages.community_timeline)} to='/timelines/public/local' />,
-        <ColumnLink key={i++} icon='globe' text={intl.formatMessage(messages.public_timeline)} to='/timelines/public' />,
+        <ColumnSubheading key='header-discover' text={intl.formatMessage(messages.discover)} />,
+        <ColumnLink key='community_timeline' icon='users' text={intl.formatMessage(messages.community_timeline)} to='/timelines/public/local' />,
+        <ColumnLink key='public_timeline' icon='globe' text={intl.formatMessage(messages.public_timeline)} to='/timelines/public' />,
       );
 
       height += 34 + 48*2;
 
       if (profile_directory) {
         navItems.push(
-          <ColumnLink key={i++} icon='address-book' text={intl.formatMessage(messages.profile_directory)} to='/directory' />,
+          <ColumnLink key='directory' icon='address-book' text={intl.formatMessage(messages.profile_directory)} to='/directory' />,
         );
 
         height += 48;
       }
 
       navItems.push(
-        <ColumnSubheading key={i++} text={intl.formatMessage(messages.personal)} />,
+        <ColumnSubheading key='header-personal' text={intl.formatMessage(messages.personal)} />,
       );
 
       height += 34;
     } else if (profile_directory) {
       navItems.push(
-        <ColumnLink key={i++} icon='address-book' text={intl.formatMessage(messages.profile_directory)} to='/directory' />,
+        <ColumnLink key='directory' icon='address-book' text={intl.formatMessage(messages.profile_directory)} to='/directory' />,
       );
 
       height += 48;
     }
 
+    if (multiColumn && !columns.find(item => item.get('id') === 'HOME')) {
+      navItems.push(
+        <ColumnLink key='home' icon='home' text={intl.formatMessage(messages.home_timeline)} to='/timelines/home' />,
+      );
+      height += 48;
+    }
+
     navItems.push(
-      <ColumnLink key={i++} icon='envelope' text={intl.formatMessage(messages.direct)} to='/timelines/direct' />,
-      <ColumnLink key={i++} icon='bookmark' text={intl.formatMessage(messages.bookmarks)} to='/bookmarks' />,
-      <ColumnLink key={i++} icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />,
-      <ColumnLink key={i++} icon='list-ul' text={intl.formatMessage(messages.lists)} to='/lists' />,
+      <ColumnLink key='direct' icon='envelope' text={intl.formatMessage(messages.direct)} to='/timelines/direct' />,
+      <ColumnLink key='bookmark' icon='bookmark' text={intl.formatMessage(messages.bookmarks)} to='/bookmarks' />,
+      <ColumnLink key='favourites' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />,
+      <ColumnLink key='lists' icon='list-ul' text={intl.formatMessage(messages.lists)} to='/lists' />,
     );
 
     height += 48*4;
 
     if (myAccount.get('locked') || unreadFollowRequests > 0) {
-      navItems.push(<ColumnLink key={i++} icon='user-plus' text={intl.formatMessage(messages.follow_requests)} badge={badgeDisplay(unreadFollowRequests, 40)} to='/follow_requests' />);
+      navItems.push(<ColumnLink key='follow_requests' icon='user-plus' text={intl.formatMessage(messages.follow_requests)} badge={badgeDisplay(unreadFollowRequests, 40)} to='/follow_requests' />);
       height += 48;
     }
 
     if (!multiColumn) {
       navItems.push(
-        <ColumnSubheading key={i++} text={intl.formatMessage(messages.settings_subheading)} />,
-        <ColumnLink key={i++} icon='gears' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />,
+        <ColumnSubheading key='header-settings' text={intl.formatMessage(messages.settings_subheading)} />,
+        <ColumnLink key='preferences' icon='gears' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />,
       );
 
       height += 34 + 48;
diff --git a/app/javascript/mastodon/features/list_timeline/index.js b/app/javascript/mastodon/features/list_timeline/index.js
index f3205b2bf..a3be8fbea 100644
--- a/app/javascript/mastodon/features/list_timeline/index.js
+++ b/app/javascript/mastodon/features/list_timeline/index.js
@@ -10,15 +10,19 @@ import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
 import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
 import { connectListStream } from '../../actions/streaming';
 import { expandListTimeline } from '../../actions/timelines';
-import { fetchList, deleteList } from '../../actions/lists';
+import { fetchList, deleteList, updateList } from '../../actions/lists';
 import { openModal } from '../../actions/modal';
 import MissingIndicator from '../../components/missing_indicator';
 import LoadingIndicator from '../../components/loading_indicator';
 import Icon from 'mastodon/components/icon';
+import RadioButton from 'mastodon/components/radio_button';
 
 const messages = defineMessages({
   deleteMessage: { id: 'confirmations.delete_list.message', defaultMessage: 'Are you sure you want to permanently delete this list?' },
   deleteConfirm: { id: 'confirmations.delete_list.confirm', defaultMessage: 'Delete' },
+  all_replies:   { id: 'lists.replies_policy.all_replies', defaultMessage: 'Any followed user' },
+  no_replies:    { id: 'lists.replies_policy.no_replies', defaultMessage: 'No one' },
+  list_replies:  { id: 'lists.replies_policy.list_replies', defaultMessage: 'Members of the list' },
 });
 
 const mapStateToProps = (state, props) => ({
@@ -131,11 +135,18 @@ class ListTimeline extends React.PureComponent {
     }));
   }
 
+  handleRepliesPolicyChange = ({ target }) => {
+    const { dispatch } = this.props;
+    const { id } = this.props.params;
+    dispatch(updateList(id, undefined, false, target.value));
+  }
+
   render () {
-    const { shouldUpdateScroll, hasUnread, columnId, multiColumn, list } = this.props;
+    const { shouldUpdateScroll, hasUnread, columnId, multiColumn, list, intl } = this.props;
     const { id } = this.props.params;
     const pinned = !!columnId;
     const title  = list ? list.get('title') : id;
+    const replies_policy = list ? list.get('replies_policy') : undefined;
 
     if (typeof list === 'undefined') {
       return (
@@ -166,7 +177,7 @@ class ListTimeline extends React.PureComponent {
           pinned={pinned}
           multiColumn={multiColumn}
         >
-          <div className='column-header__links'>
+          <div className='column-settings__row column-header__links'>
             <button className='text-btn column-header__setting-btn' tabIndex='0' onClick={this.handleEditClick}>
               <Icon id='pencil' /> <FormattedMessage id='lists.edit' defaultMessage='Edit list' />
             </button>
@@ -175,6 +186,19 @@ class ListTimeline extends React.PureComponent {
               <Icon id='trash' /> <FormattedMessage id='lists.delete' defaultMessage='Delete list' />
             </button>
           </div>
+
+          { replies_policy !== undefined && (
+            <div role='group' aria-labelledby={`list-${id}-replies-policy`}>
+              <span id={`list-${id}-replies-policy`} className='column-settings__section'>
+                <FormattedMessage id='lists.replies_policy.title' defaultMessage='Show replies to:' />
+              </span>
+              <div className='column-settings__row'>
+                { ['no_replies', 'list_replies', 'all_replies'].map(policy => (
+                  <RadioButton name='order' value={policy} label={intl.formatMessage(messages[policy])} checked={replies_policy === policy} onChange={this.handleRepliesPolicyChange} />
+                ))}
+              </div>
+            </div>
+          )}
         </ColumnHeader>
 
         <StatusListContainer
diff --git a/app/javascript/mastodon/features/ui/components/focal_point_modal.js b/app/javascript/mastodon/features/ui/components/focal_point_modal.js
index 7348d9599..926a495d1 100644
--- a/app/javascript/mastodon/features/ui/components/focal_point_modal.js
+++ b/app/javascript/mastodon/features/ui/components/focal_point_modal.js
@@ -18,6 +18,8 @@ import { length } from 'stringz';
 import { Tesseract as fetchTesseract } from 'mastodon/features/ui/util/async-components';
 import GIFV from 'mastodon/components/gifv';
 import { me } from 'mastodon/initial_state';
+import tesseractCorePath from 'tesseract.js-core/tesseract-core.wasm.js';
+import tesseractWorkerPath from 'tesseract.js/dist/worker.min.js';
 
 const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
@@ -104,6 +106,7 @@ class FocalPointModal extends ImmutablePureComponent {
     dirty: false,
     progress: 0,
     loading: true,
+    ocrStatus: '',
   };
 
   componentWillMount () {
@@ -219,11 +222,18 @@ class FocalPointModal extends ImmutablePureComponent {
 
     this.setState({ detecting: true });
 
-    fetchTesseract().then(({ TesseractWorker }) => {
-      const worker = new TesseractWorker({
-        workerPath: `${assetHost}/packs/ocr/worker.min.js`,
-        corePath: `${assetHost}/packs/ocr/tesseract-core.wasm.js`,
-        langPath: `${assetHost}/ocr/lang-data`,
+    fetchTesseract().then(({ createWorker }) => {
+      const worker = createWorker({
+        workerPath: tesseractWorkerPath,
+        corePath: tesseractCorePath,
+        langPath: assetHost,
+        logger: ({ status, progress }) => {
+          if (status === 'recognizing text') {
+            this.setState({ ocrStatus: 'detecting', progress });
+          } else {
+            this.setState({ ocrStatus: 'preparing', progress });
+          }
+        },
       });
 
       let media_url = media.get('url');
@@ -236,12 +246,18 @@ class FocalPointModal extends ImmutablePureComponent {
         }
       }
 
-      worker.recognize(media_url)
-        .progress(({ progress }) => this.setState({ progress }))
-        .finally(() => worker.terminate())
-        .then(({ text }) => this.setState({ description: removeExtraLineBreaks(text), dirty: true, detecting: false }))
-        .catch(() => this.setState({ detecting: false }));
-    }).catch(() => this.setState({ detecting: false }));
+      (async () => {
+        await worker.load();
+        await worker.loadLanguage('eng');
+        await worker.initialize('eng');
+        const { data: { text } } = await worker.recognize(media_url);
+        this.setState({ description: removeExtraLineBreaks(text), dirty: true, detecting: false });
+        await worker.terminate();
+      })();
+    }).catch((e) => {
+      console.error(e);
+      this.setState({ detecting: false });
+    });
   }
 
   handleThumbnailChange = e => {
@@ -261,7 +277,7 @@ class FocalPointModal extends ImmutablePureComponent {
 
   render () {
     const { media, intl, account, onClose, isUploadingThumbnail } = this.props;
-    const { x, y, dragging, description, dirty, detecting, progress } = this.state;
+    const { x, y, dragging, description, dirty, detecting, progress, ocrStatus } = this.state;
 
     const width  = media.getIn(['meta', 'original', 'width']) || null;
     const height = media.getIn(['meta', 'original', 'height']) || null;
@@ -282,6 +298,13 @@ class FocalPointModal extends ImmutablePureComponent {
       descriptionLabel = <FormattedMessage id='upload_form.description' defaultMessage='Describe for the visually impaired' />;
     }
 
+    let ocrMessage = '';
+    if (ocrStatus === 'detecting') {
+      ocrMessage = <FormattedMessage id='upload_modal.analyzing_picture' defaultMessage='Analyzing picture…' />;
+    } else {
+      ocrMessage = <FormattedMessage id='upload_modal.preparing_ocr' defaultMessage='Preparing OCR…' />;
+    }
+
     return (
       <div className='modal-root__modal report-modal' style={{ maxWidth: 960 }}>
         <div className='report-modal__target'>
@@ -333,7 +356,7 @@ class FocalPointModal extends ImmutablePureComponent {
               />
 
               <div className='setting-text__modifiers'>
-                <UploadProgress progress={progress * 100} active={detecting} icon='file-text-o' message={<FormattedMessage id='upload_modal.analyzing_picture' defaultMessage='Analyzing picture…' />} />
+                <UploadProgress progress={progress * 100} active={detecting} icon='file-text-o' message={ocrMessage} />
               </div>
             </div>
 
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index f6deadffa..4f4507b37 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -980,14 +980,6 @@
     outline: 0;
     background: lighten($ui-base-color, 4%);
 
-    .status.status-direct {
-      background: lighten($ui-base-color, 12%);
-
-      &.muted {
-        background: transparent;
-      }
-    }
-
     .detailed-status,
     .detailed-status__action-bar {
       background: lighten($ui-base-color, 8%);
@@ -1022,11 +1014,6 @@
     margin-top: 8px;
   }
 
-  &.status-direct:not(.read) {
-    background: lighten($ui-base-color, 8%);
-    border-bottom-color: lighten($ui-base-color, 12%);
-  }
-
   &.light {
     .status__relative-time,
     .status__visibility-icon {
@@ -1064,16 +1051,6 @@
   }
 }
 
-.notification-favourite {
-  .status.status-direct {
-    background: transparent;
-
-    .icon-button.disabled {
-      color: lighten($action-button-color, 13%);
-    }
-  }
-}
-
 .status__relative-time,
 .status__visibility-icon,
 .notification__relative_time {
@@ -5957,6 +5934,10 @@ a.status-card.compact:hover {
   }
 }
 
+.column-settings__row .radio-button {
+  display: block;
+}
+
 .radio-button {
   font-size: 14px;
   position: relative;
diff --git a/app/lib/activitypub/linked_data_signature.rb b/app/lib/activitypub/linked_data_signature.rb
index f52a8f406..e853a970e 100644
--- a/app/lib/activitypub/linked_data_signature.rb
+++ b/app/lib/activitypub/linked_data_signature.rb
@@ -27,7 +27,7 @@ class ActivityPub::LinkedDataSignature
     document_hash  = hash(@json.without('signature'))
     to_be_verified = options_hash + document_hash
 
-    if creator.keypair.public_key.verify(OpenSSL::Digest::SHA256.new, Base64.decode64(signature), to_be_verified)
+    if creator.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), Base64.decode64(signature), to_be_verified)
       creator
     end
   end
@@ -44,7 +44,7 @@ class ActivityPub::LinkedDataSignature
     to_be_signed  = options_hash + document_hash
     keypair       = sign_with.present? ? OpenSSL::PKey::RSA.new(sign_with) : creator.keypair
 
-    signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest::SHA256.new, to_be_signed))
+    signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest.new('SHA256'), to_be_signed))
 
     @json.merge('signature' => options.merge('signatureValue' => signature))
   end
diff --git a/app/lib/entity_cache.rb b/app/lib/entity_cache.rb
index afdbd70f2..89cbf8d2c 100644
--- a/app/lib/entity_cache.rb
+++ b/app/lib/entity_cache.rb
@@ -16,7 +16,7 @@ class EntityCache
   end
 
   def emoji(shortcodes, domain)
-    shortcodes   = [shortcodes] unless shortcodes.is_a?(Array)
+    shortcodes   = Array(shortcodes)
     cached       = Rails.cache.read_multi(*shortcodes.map { |shortcode| to_key(:emoji, shortcode, domain) })
     uncached_ids = []
 
diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb
index 2dc60092c..915f3fa58 100644
--- a/app/lib/feed_manager.rb
+++ b/app/lib/feed_manager.rb
@@ -159,7 +159,7 @@ class FeedManager
     aggregate    = account.user&.aggregates_reblogs?
     timeline_key = key(:home, account.id)
 
-    account.statuses.where.not(visibility: :direct).limit(limit).each do |status|
+    account.statuses.limit(limit).each do |status|
       add_to_feed(:home, account.id, status, aggregate)
     end
 
diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb
index 051f27408..c0f7866bf 100644
--- a/app/lib/formatter.rb
+++ b/app/lib/formatter.rb
@@ -198,6 +198,7 @@ class Formatter
     end
   end
 
+  # rubocop:disable Metrics/BlockNesting
   def encode_custom_emojis(html, emojis, animate = false)
     return html if emojis.empty?
 
@@ -252,6 +253,7 @@ class Formatter
 
     html
   end
+  # rubocop:enable Metrics/BlockNesting
 
   def rewrite(text, entities, keep_html = false)
     text = text.to_s
diff --git a/app/lib/request.rb b/app/lib/request.rb
index bcba1eebf..89fbeaf95 100644
--- a/app/lib/request.rb
+++ b/app/lib/request.rb
@@ -114,7 +114,7 @@ class Request
 
   def signature
     algorithm = 'rsa-sha256'
-    signature = Base64.strict_encode64(@keypair.sign(OpenSSL::Digest::SHA256.new, signed_string))
+    signature = Base64.strict_encode64(@keypair.sign(OpenSSL::Digest.new('SHA256'), signed_string))
 
     "keyId=\"#{key_id}\",algorithm=\"#{algorithm}\",headers=\"#{signed_headers.keys.join(' ').downcase}\",signature=\"#{signature}\""
   end
diff --git a/app/lib/sidekiq_error_handler.rb b/app/lib/sidekiq_error_handler.rb
index b07817d45..ab555b1be 100644
--- a/app/lib/sidekiq_error_handler.rb
+++ b/app/lib/sidekiq_error_handler.rb
@@ -17,8 +17,10 @@ class SidekiqErrorHandler
 
   private
 
+  # rubocop:disable Naming/MethodParameterName
   def limit_backtrace_and_raise(e)
     e.set_backtrace(e.backtrace.first(BACKTRACE_LIMIT))
     raise e
   end
+  # rubocop:enable Naming/MethodParameterName
 end
diff --git a/app/models/account_conversation.rb b/app/models/account_conversation.rb
index b43816588..5e2ddd083 100644
--- a/app/models/account_conversation.rb
+++ b/app/models/account_conversation.rb
@@ -36,11 +36,11 @@ class AccountConversation < ApplicationRecord
   end
 
   class << self
-    def paginate_by_id(limit, options = {})
+    def to_a_paginated_by_id(limit, options = {})
       if options[:min_id]
         paginate_by_min_id(limit, options[:min_id]).reverse
       else
-        paginate_by_max_id(limit, options[:max_id], options[:since_id])
+        paginate_by_max_id(limit, options[:max_id], options[:since_id]).to_a
       end
     end
 
diff --git a/app/models/concerns/paginable.rb b/app/models/concerns/paginable.rb
index 8863094f7..760cc3df4 100644
--- a/app/models/concerns/paginable.rb
+++ b/app/models/concerns/paginable.rb
@@ -20,12 +20,12 @@ module Paginable
       query
     }
 
-    scope :paginate_by_id, ->(limit, options = {}) {
+    def self.to_a_paginated_by_id(limit, options = {})
       if options[:min_id].present?
         paginate_by_min_id(limit, options[:min_id]).reverse
       else
-        paginate_by_max_id(limit, options[:max_id], options[:since_id])
+        paginate_by_max_id(limit, options[:max_id], options[:since_id]).to_a
       end
-    }
+    end
   end
 end
diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb
index 235928260..a6ec839f8 100644
--- a/app/models/preview_card.rb
+++ b/app/models/preview_card.rb
@@ -72,6 +72,7 @@ class PreviewCard < ApplicationRecord
   class << self
     private
 
+    # rubocop:disable Naming/MethodParameterName
     def image_styles(f)
       styles = {
         original: {
@@ -85,6 +86,7 @@ class PreviewCard < ApplicationRecord
       styles[:original][:format] = 'jpg' if f.instance.image_content_type == 'image/gif'
       styles
     end
+    # rubocop:enable Naming/MethodParameterName
   end
 
   private
diff --git a/app/models/user.rb b/app/models/user.rb
index 77b50d966..4467362e1 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -254,16 +254,16 @@ class User < ApplicationRecord
     @shows_application ||= settings.show_application
   end
 
+  # rubocop:disable Naming/MethodParameterName
   def token_for_app(a)
     return nil if a.nil? || a.owner != self
-    Doorkeeper::AccessToken
-      .find_or_create_by(application_id: a.id, resource_owner_id: id) do |t|
-
+    Doorkeeper::AccessToken.find_or_create_by(application_id: a.id, resource_owner_id: id) do |t|
       t.scopes = a.scopes
       t.expires_in = Doorkeeper.configuration.access_token_expires_in
       t.use_refresh_token = Doorkeeper.configuration.refresh_token_enabled?
     end
   end
+  # rubocop:enable Naming/MethodParameterName
 
   def activate_session(request)
     session_activations.activate(session_id: SecureRandom.hex,
@@ -414,7 +414,7 @@ class User < ApplicationRecord
   end
 
   def notify_staff_about_pending_account!
-    User.staff.includes(:account).each do |u|
+    User.staff.includes(:account).find_each do |u|
       next unless u.allows_pending_account_emails?
       AdminMailer.new_pending_account(u.account, self).deliver_later
     end
diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb
index 5f3feeed9..6fa98ce12 100644
--- a/app/services/fan_out_on_write_service.rb
+++ b/app/services/fan_out_on_write_service.rb
@@ -63,9 +63,9 @@ class FanOutOnWriteService < BaseService
   def deliver_to_mentioned_followers(status)
     Rails.logger.debug "Delivering status #{status.id} to limited followers"
 
-    status.mentions.joins(:account).merge(status.account.followers_for_local_distribution).select(:id).reorder(nil).find_in_batches do |followers|
-      FeedInsertWorker.push_bulk(followers) do |follower|
-        [status.id, follower.id, :home]
+    status.mentions.joins(:account).merge(status.account.followers_for_local_distribution).select(:id, :account_id).reorder(nil).find_in_batches do |mentions|
+      FeedInsertWorker.push_bulk(mentions) do |mention|
+        [status.id, mention.account_id, :home]
       end
     end
   end
diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb
index beab449b2..7efa31054 100644
--- a/app/services/fetch_link_card_service.rb
+++ b/app/services/fetch_link_card_service.rb
@@ -78,6 +78,7 @@ class FetchLinkCardService < BaseService
     uri.host.blank? || TagManager.instance.local_url?(uri.to_s) || !%w(http https).include?(uri.scheme)
   end
 
+  # rubocop:disable Naming/MethodParameterName
   def mention_link?(a)
     @status.mentions.any? do |mention|
       a['href'] == ActivityPub::TagManager.instance.url_for(mention.account)
@@ -88,6 +89,7 @@ class FetchLinkCardService < BaseService
     # Avoid links for hashtags and mentions (microformats)
     a['rel']&.include?('tag') || a['class']&.match?(/u-url|h-card/) || mention_link?(a)
   end
+  # rubocop:enable Naming/MethodParameterName
 
   def attempt_oembed
     service         = FetchOEmbedService.new
diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb
index 9364a6ae8..abd676494 100644
--- a/app/services/notify_service.rb
+++ b/app/services/notify_service.rb
@@ -13,7 +13,9 @@ class NotifyService < BaseService
     push_to_conversation! if direct_message?
     send_email! if email_enabled?
   rescue ActiveRecord::RecordInvalid
+    # rubocop:disable Style/RedundantReturn
     return
+    # rubocop:enable Style/RedundantReturn
   end
 
   private
diff --git a/app/services/update_account_service.rb b/app/services/update_account_service.rb
index 666db5c71..77f794e17 100644
--- a/app/services/update_account_service.rb
+++ b/app/services/update_account_service.rb
@@ -12,8 +12,8 @@ class UpdateAccountService < BaseService
       check_links(account)
       process_hashtags(account)
     end
-  rescue Mastodon::DimensionsValidationError, Mastodon::StreamValidationError => de
-    account.errors.add(:avatar, de.message)
+  rescue Mastodon::DimensionsValidationError, Mastodon::StreamValidationError => e
+    account.errors.add(:avatar, e.message)
     false
   end
 
diff --git a/config/webpack/rules/index.js b/config/webpack/rules/index.js
index bbf6b0187..92a384e6d 100644
--- a/config/webpack/rules/index.js
+++ b/config/webpack/rules/index.js
@@ -1,6 +1,7 @@
 const babel = require('./babel');
 const css = require('./css');
 const file = require('./file');
+const tesseract = require('./tesseract');
 const nodeModules = require('./node_modules');
 
 // Webpack loaders are processed in reverse order
@@ -8,6 +9,7 @@ const nodeModules = require('./node_modules');
 // Lastly, process static files using file loader
 module.exports = {
   file,
+  tesseract,
   css,
   nodeModules,
   babel,
diff --git a/config/webpack/rules/node_modules.js b/config/webpack/rules/node_modules.js
index 7ed05504b..c084d3a57 100644
--- a/config/webpack/rules/node_modules.js
+++ b/config/webpack/rules/node_modules.js
@@ -4,7 +4,10 @@ const { settings, env } = require('../configuration');
 module.exports = {
   test: /\.(js|mjs)$/,
   include: /node_modules/,
-  exclude: /@babel(?:\/|\\{1,2})runtime/,
+  exclude: [
+    /@babel(?:\/|\\{1,2})runtime/,
+    /tesseract.js/,
+  ],
   use: [
     {
       loader: 'babel-loader',
diff --git a/config/webpack/rules/tesseract.js b/config/webpack/rules/tesseract.js
new file mode 100644
index 000000000..de647c8d1
--- /dev/null
+++ b/config/webpack/rules/tesseract.js
@@ -0,0 +1,14 @@
+module.exports = {
+  test: [
+    /tesseract\.js\/dist\/worker\.min\.js$/,
+    /tesseract\.js\/dist\/worker\.min\.js.map$/,
+    /tesseract\.js-core\/tesseract-core\.wasm$/,
+    /tesseract\.js-core\/tesseract-core\.wasm.js$/,
+  ],
+  use: {
+    loader: 'file-loader',
+    options: {
+      name: 'ocr/[name]-[hash].[ext]',
+    },
+  },
+};
diff --git a/config/webpack/shared.js b/config/webpack/shared.js
index 36b5a459e..11c321c58 100644
--- a/config/webpack/shared.js
+++ b/config/webpack/shared.js
@@ -5,7 +5,6 @@ const { basename, dirname, join, relative, resolve } = require('path');
 const { sync } = require('glob');
 const MiniCssExtractPlugin = require('mini-css-extract-plugin');
 const AssetsManifestPlugin = require('webpack-assets-manifest');
-const CopyPlugin = require('copy-webpack-plugin');
 const { env, settings, core, flavours, output } = require('./configuration.js');
 const rules = require('./rules');
 const localePacks = require('./generateLocalePacks');
@@ -110,12 +109,6 @@ module.exports = {
       writeToDisk: true,
       publicPath: true,
     }),
-    new CopyPlugin({
-      patterns: [
-        { from: 'node_modules/tesseract.js/dist/worker.min.js', to: 'ocr' },
-        { from: 'node_modules/tesseract.js-core/tesseract-core.wasm.js', to: 'ocr' },
-      ],
-    }),
   ],
 
   resolve: {
diff --git a/lib/mastodon/media_cli.rb b/lib/mastodon/media_cli.rb
index 54da5b2cd..31135f7fc 100644
--- a/lib/mastodon/media_cli.rb
+++ b/lib/mastodon/media_cli.rb
@@ -47,6 +47,7 @@ module Mastodon
 
     option :start_after
     option :prefix
+    option :fix_permissions, type: :boolean, default: false
     option :dry_run, type: :boolean, default: false
     desc 'remove-orphans', 'Scan storage and check for files that do not belong to existing media attachments'
     long_desc <<~LONG_DESC
@@ -86,6 +87,8 @@ module Mastodon
           record_map = preload_records_from_mixed_objects(objects)
 
           objects.each do |object|
+            object.acl.put(acl: 'public-read') if options[:fix_permissions] && !options[:dry_run]
+
             path_segments = object.key.split('/')
             path_segments.delete('cache')
 
diff --git a/lib/mastodon/redis_config.rb b/lib/mastodon/redis_config.rb
index e9db9122f..c3c8ff800 100644
--- a/lib/mastodon/redis_config.rb
+++ b/lib/mastodon/redis_config.rb
@@ -23,7 +23,7 @@ end
 setup_redis_env_url
 setup_redis_env_url(:cache, false)
 
-namespace       = ENV.fetch('REDIS_NAMESPACE') { nil }
+namespace       = ENV.fetch('REDIS_NAMESPACE', nil)
 cache_namespace = namespace ? namespace + '_cache' : 'cache'
 
 REDIS_CACHE_PARAMS = {
diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb
index ccd583e7d..4bec3c04d 100644
--- a/lib/mastodon/version.rb
+++ b/lib/mastodon/version.rb
@@ -37,16 +37,16 @@ module Mastodon
     end
 
     def repository
-      ENV.fetch('GITHUB_REPOSITORY') { 'pluralcafe/mastodon' }
+      ENV.fetch('GITHUB_REPOSITORY', 'pluralcafe/mastodon')
     end
 
     def source_base_url
-      ENV.fetch('SOURCE_BASE_URL') { "https://github.com/#{repository}" }
+      ENV.fetch('SOURCE_BASE_URL', "https://github.com/#{repository}")
     end
 
     # specify git tag or commit hash here
     def source_tag
-      ENV.fetch('SOURCE_TAG') { nil }
+      ENV.fetch('SOURCE_TAG', nil)
     end
 
     def source_url
diff --git a/lib/paperclip/color_extractor.rb b/lib/paperclip/color_extractor.rb
index c8bb771a0..f850dc067 100644
--- a/lib/paperclip/color_extractor.rb
+++ b/lib/paperclip/color_extractor.rb
@@ -89,7 +89,7 @@ module Paperclip
       end
     end
 
-    # rubocop:disable Style/MethodParameterName
+    # rubocop:disable Naming/MethodParameterName
     def rgb_to_hsl(r, g, b)
       r /= 255.0
       g /= 255.0
@@ -156,7 +156,7 @@ module Paperclip
 
       [(r * 255).round, (g * 255).round, (b * 255).round]
     end
-    # rubocop:enable Style/MethodParameterName
+    # rubocop:enable Naming/MethodParameterName
 
     def lighten_or_darken(color, by)
       hue, saturation, light = rgb_to_hsl(color.r, color.g, color.b)
diff --git a/lib/tasks/emojis.rake b/lib/tasks/emojis.rake
index 0e7921ffc..2ac8bc059 100644
--- a/lib/tasks/emojis.rake
+++ b/lib/tasks/emojis.rake
@@ -91,7 +91,7 @@ namespace :emojis do
   desc 'Generate emoji variants with white borders'
   task :generate_borders do
     src = Rails.root.join('app', 'javascript', 'mastodon', 'features', 'emoji', 'emoji_map.json')
-    emojis = '🎱🐜⚫🖤⬛◼️◾◼️✒️▪️💣🎳📷📸♣️🕶️✴️🔌💂‍♀️📽️🍳🦍💂🔪🕳️🕹️🕋🖊️🖋️💂‍♂️🎤🎓🎥🎼♠️🎩🦃📼📹🎮🐃🏴👽⚾🐔☁️💨🕊️👀🍥👻🐐❕❔⛸️🌩️🔊🔇📃🌧️🐏🍚🍙🐓🐑💀☠️🌨️🔉🔈💬💭🏐🏳️⚪⬜◽◻️▫️'
+    emojis = '🎱🐜⚫🖤⬛◼️◾◼️✒️▪️💣🎳📷📸♣️🕶️✴️🔌💂‍♀️📽️🍳🦍💂🔪🕳️🕹️🕋🖊️🖋️💂‍♂️🎤🎓🎥🎼♠️🎩🦃📼📹🎮🐃🏴🐞👽⚾🐔☁️💨🕊️👀🍥👻🐐❕❔⛸️🌩️🔊🔇📃🌧️🐏🍚🍙🐓🐑💀☠️🌨️🔉🔈💬💭🏐🏳️⚪⬜◽◻️▫️'
 
     map = Oj.load(File.read(src))
 
diff --git a/package.json b/package.json
index 658944c7e..3982f7cde 100644
--- a/package.json
+++ b/package.json
@@ -86,7 +86,6 @@
     "blurhash": "^1.1.3",
     "classnames": "^2.2.5",
     "compression-webpack-plugin": "^5.0.1",
-    "copy-webpack-plugin": "^6.0.3",
     "cross-env": "^7.0.2",
     "css-loader": "^4.2.2",
     "cssnano": "^4.1.10",
@@ -114,7 +113,7 @@
     "lodash": "^4.17.19",
     "mark-loader": "^0.1.6",
     "marky": "^1.2.1",
-    "mini-css-extract-plugin": "^0.9.0",
+    "mini-css-extract-plugin": "^0.11.0",
     "mkdirp": "^1.0.4",
     "npmlog": "^4.1.2",
     "object-assign": "^4.1.1",
@@ -162,7 +161,7 @@
     "stringz": "^2.1.0",
     "substring-trie": "^1.0.2",
     "terser-webpack-plugin": "^3.0.6",
-    "tesseract.js": "2.0.0-alpha.16",
+    "tesseract.js": "^2.1.1",
     "throng": "^4.0.0",
     "tiny-queue": "^0.2.1",
     "uuid": "^8.2.0",
@@ -174,7 +173,7 @@
     "wicg-inert": "^3.0.3"
   },
   "devDependencies": {
-    "@testing-library/jest-dom": "^5.11.2",
+    "@testing-library/jest-dom": "^5.11.4",
     "@testing-library/react": "^10.4.7",
     "babel-eslint": "^10.1.0",
     "babel-jest": "^26.3.0",
diff --git a/public/emoji/1f41e_border.svg b/public/emoji/1f41e_border.svg
new file mode 100644
index 000000000..5d4b1e3d7
--- /dev/null
+++ b/public/emoji/1f41e_border.svg
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="-2 -2 40 40">
+  <g>
+    <path d="M35 21H1c-.552 0-1-.447-1-1s.448-1 1-1h34c.553 0 1 .447 1 1s-.447 1-1 1zm-22.177-2l-.069-.002c-.092-.007-9.214-.714-10.734-8.235-.109-.542.241-1.069.782-1.178.543-.113 1.069.241 1.178.782 1.221 6.044 8.833 6.631 8.91 6.636.551.038.967.515.93 1.066-.036.527-.476.931-.997.931zM3 31c-.142 0-.286-.03-.423-.094-.5-.234-.716-.829-.482-1.33 3.166-6.77 11.038-7.721 11.372-7.758.548-.056 1.042.334 1.103.882.062.548-.332 1.043-.88 1.106-.071.008-7.099.876-9.783 6.617-.171.364-.532.577-.907.577zm19.753-12c-.522 0-.961-.405-.996-.934-.036-.551.381-1.027.931-1.064.081-.005 8.116-.617 9.332-6.636.108-.541.633-.895 1.179-.782.541.109.892.637.782 1.178-1.521 7.525-10.769 8.21-11.162 8.235l-.066.003zm10.248 12c-.377 0-.737-.213-.907-.576-2.694-5.763-10.124-6.609-10.198-6.617-.55-.058-.948-.55-.89-1.099.058-.55.555-.952 1.099-.89.352.037 8.634.983 11.802 7.758.233.501.018 1.096-.482 1.33-.139.064-.282.094-.424.094z" stroke="white" stroke-linejoin="round" stroke-width="4px"/>
+    <path d="M24.989 7.766c-.069-2.626-1.277-4.216-3.095-5.04C21.961 2.5 22 2.257 22 2c0-1.105-.672-2-1.5-2S19 .895 19 2c0 .015.003.028.003.043C18.675 2.017 18.342 2 18 2s-.675.017-1.003.043c0-.015.003-.028.003-.043 0-1.105-.671-2-1.5-2S14 .895 14 2c0 .257.04.5.106.726-1.817.824-3.025 2.414-3.095 5.04C7.98 9.551 6 12.662 6 17c0 7.159 5.373 16.923 12 16.923 6.628 0 12-9.764 12-16.923 0-4.338-1.98-7.45-5.011-9.234z" stroke="white" stroke-linejoin="round" stroke-width="4px"/>
+    <path d="M16.001 34.959C9.564 34.364 4.5 28.064 4.5 20.378 4.5 12.693 9.564 9.597 16 9c.33-.03 1-.046 1 2.294v22.541c0 1.171-.669 1.156-.999 1.124zm3.998 0c6.436-.595 11.501-6.895 11.501-14.581C31.5 12.693 26.435 9.597 20 9c-.33-.03-1-.046-1 2.294v22.541c0 1.171.67 1.156.999 1.124z" stroke="white" stroke-linejoin="round" stroke-width="4px"/>
+    <g stroke="white" stroke-linejoin="round" stroke-width="4px">
+      <circle cx="13" cy="15" r="2"/>
+      <circle cx="10" cy="22" r="3"/>
+      <circle cx="13" cy="29" r="2"/>
+      <circle cx="24.5" cy="14.5" r="2.5"/>
+      <circle cx="22.5" cy="20.5" r="1.5"/>
+      <circle cx="28" cy="23" r="2"/>
+      <circle cx="24" cy="29" r="3"/>
+    </g>
+  </g>
+  <path d="M35 21H1c-.552 0-1-.447-1-1s.448-1 1-1h34c.553 0 1 .447 1 1s-.447 1-1 1zm-22.177-2l-.069-.002c-.092-.007-9.214-.714-10.734-8.235-.109-.542.241-1.069.782-1.178.543-.113 1.069.241 1.178.782 1.221 6.044 8.833 6.631 8.91 6.636.551.038.967.515.93 1.066-.036.527-.476.931-.997.931zM3 31c-.142 0-.286-.03-.423-.094-.5-.234-.716-.829-.482-1.33 3.166-6.77 11.038-7.721 11.372-7.758.548-.056 1.042.334 1.103.882.062.548-.332 1.043-.88 1.106-.071.008-7.099.876-9.783 6.617-.171.364-.532.577-.907.577zm19.753-12c-.522 0-.961-.405-.996-.934-.036-.551.381-1.027.931-1.064.081-.005 8.116-.617 9.332-6.636.108-.541.633-.895 1.179-.782.541.109.892.637.782 1.178-1.521 7.525-10.769 8.21-11.162 8.235l-.066.003zm10.248 12c-.377 0-.737-.213-.907-.576-2.694-5.763-10.124-6.609-10.198-6.617-.55-.058-.948-.55-.89-1.099.058-.55.555-.952 1.099-.89.352.037 8.634.983 11.802 7.758.233.501.018 1.096-.482 1.33-.139.064-.282.094-.424.094z" fill="#31373D"/>
+  <path fill="#31373D" d="M24.989 7.766c-.069-2.626-1.277-4.216-3.095-5.04C21.961 2.5 22 2.257 22 2c0-1.105-.672-2-1.5-2S19 .895 19 2c0 .015.003.028.003.043C18.675 2.017 18.342 2 18 2s-.675.017-1.003.043c0-.015.003-.028.003-.043 0-1.105-.671-2-1.5-2S14 .895 14 2c0 .257.04.5.106.726-1.817.824-3.025 2.414-3.095 5.04C7.98 9.551 6 12.662 6 17c0 7.159 5.373 16.923 12 16.923 6.628 0 12-9.764 12-16.923 0-4.338-1.98-7.45-5.011-9.234z"/>
+  <path fill="#DD2E44" d="M16.001 34.959C9.564 34.364 4.5 28.064 4.5 20.378 4.5 12.693 9.564 9.597 16 9c.33-.03 1-.046 1 2.294v22.541c0 1.171-.669 1.156-.999 1.124zm3.998 0c6.436-.595 11.501-6.895 11.501-14.581C31.5 12.693 26.435 9.597 20 9c-.33-.03-1-.046-1 2.294v22.541c0 1.171.67 1.156.999 1.124z"/>
+  <g fill="#31373D">
+    <circle cx="13" cy="15" r="2"/>
+    <circle cx="10" cy="22" r="3"/>
+    <circle cx="13" cy="29" r="2"/>
+    <circle cx="24.5" cy="14.5" r="2.5"/>
+    <circle cx="22.5" cy="20.5" r="1.5"/>
+    <circle cx="28" cy="23" r="2"/>
+    <circle cx="24" cy="29" r="3"/>
+  </g>
+</svg>
diff --git a/yarn.lock b/yarn.lock
index bc58b51ca..b0d8622dc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1341,27 +1341,6 @@
     "@types/yargs" "^15.0.0"
     chalk "^4.0.0"
 
-"@nodelib/fs.scandir@2.1.3":
-  version "2.1.3"
-  resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
-  integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==
-  dependencies:
-    "@nodelib/fs.stat" "2.0.3"
-    run-parallel "^1.1.9"
-
-"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2":
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3"
-  integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==
-
-"@nodelib/fs.walk@^1.2.3":
-  version "1.2.4"
-  resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976"
-  integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==
-  dependencies:
-    "@nodelib/fs.scandir" "2.1.3"
-    fastq "^1.6.0"
-
 "@npmcli/move-file@^1.0.1":
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.0.1.tgz#de103070dac0f48ce49cf6693c23af59c0f70464"
@@ -1398,10 +1377,10 @@
     dom-accessibility-api "^0.4.5"
     pretty-format "^25.5.0"
 
-"@testing-library/jest-dom@^5.11.2":
-  version "5.11.2"
-  resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.11.2.tgz#c49de331555c70127b5d7fc97344ad5265f4c54c"
-  integrity sha512-s+rWJx+lanEGKqvOl4qJR0rGjCrxsEjj9qjxFlg4NV4/FRD7fnUUAWPHqwpyafNHfLYArs58FADgdn4UKmjFmw==
+"@testing-library/jest-dom@^5.11.4":
+  version "5.11.4"
+  resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.11.4.tgz#f325c600db352afb92995c2576022b35621ddc99"
+  integrity sha512-6RRn3epuweBODDIv3dAlWjOEHQLpGJHB2i912VS3JQtsD22+ENInhdDNl4ZZQiViLlIfFinkSET/J736ytV9sw==
   dependencies:
     "@babel/runtime" "^7.9.2"
     "@types/testing-library__jest-dom" "^5.9.1"
@@ -1409,8 +1388,6 @@
     chalk "^3.0.0"
     css "^3.0.0"
     css.escape "^1.5.1"
-    jest-diff "^25.1.0"
-    jest-matcher-utils "^25.1.0"
     lodash "^4.17.15"
     redent "^3.0.0"
 
@@ -1996,11 +1973,6 @@ array-union@^1.0.1:
   dependencies:
     array-uniq "^1.0.1"
 
-array-union@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
-  integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
-
 array-uniq@^1.0.1:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
@@ -2138,14 +2110,6 @@ axe-core@^3.5.4:
   resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.5.5.tgz#84315073b53fa3c0c51676c588d59da09a192227"
   integrity sha512-5P0QZ6J5xGikH780pghEdbEKijCTrruK9KxtPZCFWUpef0f6GipO+xEZ5GKCb020mmqgbiNO6TcA55CriL784Q==
 
-axios@^0.18.0:
-  version "0.18.1"
-  resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3"
-  integrity sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==
-  dependencies:
-    follow-redirects "1.5.10"
-    is-buffer "^2.0.2"
-
 axios@^0.19.2:
   version "0.19.2"
   resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
@@ -2740,9 +2704,9 @@ caniuse-api@^3.0.0:
     lodash.uniq "^4.5.0"
 
 caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001111:
-  version "1.0.30001117"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001117.tgz#69a9fae5d480eaa9589f7641a83842ad396d17c4"
-  integrity sha512-4tY0Fatzdx59kYjQs+bNxUwZB03ZEBgVmJ1UkFPz/Q8OLiUUbjct2EdpnXj0fvFTPej2EkbPIG0w8BWsjAyk1Q==
+  version "1.0.30001120"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001120.tgz#cd21d35e537214e19f7b9f4f161f7b0f2710d46c"
+  integrity sha512-JBP68okZs1X8D7MQTY602jxMYBmXEKOFkzTBaNSkubooMPFOAv2TXWaKle7qgHpjLDhUzA/TMT0qsNleVyXGUQ==
 
 capture-exit@^2.0.0:
   version "2.0.0"
@@ -2797,11 +2761,6 @@ char-regex@^1.0.2:
   resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
   integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==
 
-check-types@^7.4.0:
-  version "7.4.0"
-  resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.4.0.tgz#0378ec1b9616ec71f774931a3c6516fad8c152f4"
-  integrity sha512-YbulWHdfP99UfZ73NcUDlNJhEIDgm9Doq9GhpyXbF+7Aegi3CVV7qqMCKTTqJxlvEvnQBp9IA+dxsGN6xK/nSg==
-
 check-types@^8.0.3:
   version "8.0.3"
   resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552"
@@ -3157,23 +3116,6 @@ copy-descriptor@^0.1.0:
   resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
   integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
 
-copy-webpack-plugin@^6.0.3:
-  version "6.0.3"
-  resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.0.3.tgz#2b3d2bfc6861b96432a65f0149720adbd902040b"
-  integrity sha512-q5m6Vz4elsuyVEIUXr7wJdIdePWTubsqVbEMvf1WQnHGv0Q+9yPRu7MtYFPt+GBOXRav9lvIINifTQ1vSCs+eA==
-  dependencies:
-    cacache "^15.0.4"
-    fast-glob "^3.2.4"
-    find-cache-dir "^3.3.1"
-    glob-parent "^5.1.1"
-    globby "^11.0.1"
-    loader-utils "^2.0.0"
-    normalize-path "^3.0.0"
-    p-limit "^3.0.1"
-    schema-utils "^2.7.0"
-    serialize-javascript "^4.0.0"
-    webpack-sources "^1.4.3"
-
 core-js-compat@^3.6.2:
   version "3.6.5"
   resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.5.tgz#2a51d9a4e25dfd6e690251aa81f99e3c05481f1c"
@@ -3739,13 +3681,6 @@ diffie-hellman@^5.0.0:
     miller-rabin "^4.0.0"
     randombytes "^2.0.0"
 
-dir-glob@^3.0.1:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
-  integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
-  dependencies:
-    path-type "^4.0.0"
-
 dns-equal@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
@@ -4014,6 +3949,24 @@ es-abstract@^1.17.0, es-abstract@^1.17.0-next.0, es-abstract@^1.17.0-next.1, es-
     string.prototype.trimend "^1.0.1"
     string.prototype.trimstart "^1.0.1"
 
+es-abstract@^1.18.0-next.0:
+  version "1.18.0-next.0"
+  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.0.tgz#b302834927e624d8e5837ed48224291f2c66e6fc"
+  integrity sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==
+  dependencies:
+    es-to-primitive "^1.2.1"
+    function-bind "^1.1.1"
+    has "^1.0.3"
+    has-symbols "^1.0.1"
+    is-callable "^1.2.0"
+    is-negative-zero "^2.0.0"
+    is-regex "^1.1.1"
+    object-inspect "^1.8.0"
+    object-keys "^1.1.1"
+    object.assign "^4.1.0"
+    string.prototype.trimend "^1.0.1"
+    string.prototype.trimstart "^1.0.1"
+
 es-to-primitive@^1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
@@ -4576,18 +4529,6 @@ fast-deep-equal@^3.1.1:
   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
   integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
 
-fast-glob@^3.1.1, fast-glob@^3.2.4:
-  version "3.2.4"
-  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3"
-  integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==
-  dependencies:
-    "@nodelib/fs.stat" "^2.0.2"
-    "@nodelib/fs.walk" "^1.2.3"
-    glob-parent "^5.1.0"
-    merge2 "^1.3.0"
-    micromatch "^4.0.2"
-    picomatch "^2.2.1"
-
 fast-json-stable-stringify@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
@@ -4603,13 +4544,6 @@ favico.js@^0.3.10:
   resolved "https://registry.yarnpkg.com/favico.js/-/favico.js-0.3.10.tgz#80586e27a117f24a8d51c18a99bdc714d4339301"
   integrity sha1-gFhuJ6EX8kqNUcGKmb3HFNQzkwE=
 
-fastq@^1.6.0:
-  version "1.8.0"
-  resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481"
-  integrity sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==
-  dependencies:
-    reusify "^1.0.4"
-
 faye-websocket@^0.10.0:
   version "0.10.0"
   resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
@@ -4667,10 +4601,10 @@ file-loader@^6.0.0:
     loader-utils "^2.0.0"
     schema-utils "^2.6.5"
 
-file-type@^10.5.0:
-  version "10.11.0"
-  resolved "https://registry.yarnpkg.com/file-type/-/file-type-10.11.0.tgz#2961d09e4675b9fb9a3ee6b69e9cd23f43fd1890"
-  integrity sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw==
+file-type@^12.4.1:
+  version "12.4.2"
+  resolved "https://registry.yarnpkg.com/file-type/-/file-type-12.4.2.tgz#a344ea5664a1d01447ee7fb1b635f72feb6169d9"
+  integrity sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg==
 
 file-uri-to-path@1.0.0:
   version "1.0.0"
@@ -5014,7 +4948,7 @@ glob-parent@^3.1.0:
     is-glob "^3.1.0"
     path-dirname "^1.0.0"
 
-glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@^5.1.1, glob-parent@~5.1.0:
+glob-parent@^5.0.0, glob-parent@~5.1.0:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
   integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
@@ -5086,18 +5020,6 @@ globals@^9.2.0:
   resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
   integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==
 
-globby@^11.0.1:
-  version "11.0.1"
-  resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357"
-  integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==
-  dependencies:
-    array-union "^2.1.0"
-    dir-glob "^3.0.1"
-    fast-glob "^3.1.1"
-    ignore "^5.1.4"
-    merge2 "^1.3.0"
-    slash "^3.0.0"
-
 globby@^6.1.0:
   version "6.1.0"
   resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c"
@@ -5455,7 +5377,7 @@ icss-utils@^4.0.0, icss-utils@^4.1.1:
   dependencies:
     postcss "^7.0.14"
 
-idb-keyval@^3.1.0:
+idb-keyval@^3.2.0:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-3.2.0.tgz#cbbf354deb5684b6cdc84376294fc05932845bd6"
   integrity sha512-slx8Q6oywCCSfKgPgL0sEsXtPVnSbTLWpyiDcu6msHOyKOLari1TD1qocXVCft80umnkk3/Qqh3lwoFt8T/BPQ==
@@ -5480,11 +5402,6 @@ ignore@^4.0.6:
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
   integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
 
-ignore@^5.1.4:
-  version "5.1.6"
-  resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.6.tgz#643194ad4bf2712f37852e386b6998eff0db2106"
-  integrity sha512-cgXgkypZBcCnOgSihyeqbo6gjIaIyDqPQB7Ra4vhE9m6kigdGoQDMHjviFhRZo3IMlRy6yElosoviMs5YxZXUA==
-
 immutable@^3.8.2:
   version "3.8.2"
   resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
@@ -5756,11 +5673,6 @@ is-binary-path@~2.1.0:
   dependencies:
     binary-extensions "^2.0.0"
 
-is-buffer@^2.0.2:
-  version "2.0.4"
-  resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623"
-  integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==
-
 is-callable@^1.1.4, is-callable@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb"
@@ -5832,6 +5744,11 @@ is-docker@^2.0.0:
   resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.1.1.tgz#4125a88e44e450d384e09047ede71adc2d144156"
   integrity sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==
 
+is-electron@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/is-electron/-/is-electron-2.2.0.tgz#8943084f09e8b731b3a7a0298a7b5d56f6b7eef0"
+  integrity sha512-SpMppC2XR3YdxSzczXReBjqs2zGscWQpBIKqwXYBFic0ERaxNVgwLCHwOLZeESfdJQjX0RDvrJ1lBXX2ij+G1Q==
+
 is-extendable@^0.1.0, is-extendable@^0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
@@ -5908,6 +5825,11 @@ is-nan@^1.3.0:
   dependencies:
     define-properties "^1.1.3"
 
+is-negative-zero@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461"
+  integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=
+
 is-number@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
@@ -5966,13 +5888,20 @@ is-property@^1.0.0, is-property@^1.0.2:
   resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
   integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=
 
-is-regex@^1.0.4, is-regex@^1.1.0:
+is-regex@^1.0.4:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.0.tgz#ece38e389e490df0dc21caea2bd596f987f767ff"
   integrity sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==
   dependencies:
     has-symbols "^1.0.1"
 
+is-regex@^1.1.0, is-regex@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9"
+  integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==
+  dependencies:
+    has-symbols "^1.0.1"
+
 is-resolvable@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
@@ -6012,11 +5941,6 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0:
   resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
   integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
 
-is-url@1.2.2:
-  version "1.2.2"
-  resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.2.tgz#498905a593bf47cc2d9e7f738372bbf7696c7f26"
-  integrity sha1-SYkFpZO/R8wtnn9zg3K792lsfyY=
-
 is-url@^1.2.4:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52"
@@ -6164,7 +6088,7 @@ jest-config@^26.4.2:
     micromatch "^4.0.2"
     pretty-format "^26.4.2"
 
-jest-diff@^25.1.0, jest-diff@^25.2.1, jest-diff@^25.5.0:
+jest-diff@^25.2.1:
   version "25.5.0"
   resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.5.0.tgz#1dd26ed64f96667c068cef026b677dfa01afcfa9"
   integrity sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A==
@@ -6290,16 +6214,6 @@ jest-leak-detector@^26.4.2:
     jest-get-type "^26.3.0"
     pretty-format "^26.4.2"
 
-jest-matcher-utils@^25.1.0:
-  version "25.5.0"
-  resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.5.0.tgz#fbc98a12d730e5d2453d7f1ed4a4d948e34b7867"
-  integrity sha512-VWI269+9JS5cpndnpCwm7dy7JtGQT30UHfrnM3mXl22gHGt/b7NkjBqXfbhZ8V4B7ANUsjK18PlSBmG0YH7gjw==
-  dependencies:
-    chalk "^3.0.0"
-    jest-diff "^25.5.0"
-    jest-get-type "^25.2.6"
-    pretty-format "^25.5.0"
-
 jest-matcher-utils@^26.4.2:
   version "26.4.2"
   resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.4.2.tgz#fa81f3693f7cb67e5fc1537317525ef3b85f4b06"
@@ -6885,20 +6799,15 @@ lodash.uniq@^4.5.0:
   resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
   integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
 
-lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.3.0, lodash@~4.17.10:
-  version "4.17.19"
-  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
-  integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
-
-lodash@^4.17.14, lodash@^4.17.19:
+lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.3.0, lodash@~4.17.10:
   version "4.17.20"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
   integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
 
 loglevel@^1.6.8:
-  version "1.6.8"
-  resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171"
-  integrity sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0"
+  integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ==
 
 loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
   version "1.4.0"
@@ -7020,11 +6929,6 @@ merge-stream@^2.0.0:
   resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
   integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
 
-merge2@^1.3.0:
-  version "1.4.1"
-  resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
-  integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
-
 merge@^1.2.0:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145"
@@ -7102,10 +7006,10 @@ min-indent@^1.0.0:
   resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
   integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
 
-mini-css-extract-plugin@^0.9.0:
-  version "0.9.0"
-  resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz#47f2cf07aa165ab35733b1fc97d4c46c0564339e"
-  integrity sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==
+mini-css-extract-plugin@^0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.0.tgz#3918953075109d4ca204bf1e8a393a78d3cc821f"
+  integrity sha512-dVWGuWJlQw2lZxsxBI3hOsoxg1k3DruLR0foHQLSkQMfk+qLJbv9dUk8fjmjWQKN9ef2n54ehA2FjClAsQhrWQ==
   dependencies:
     loader-utils "^1.1.0"
     normalize-url "1.9.1"
@@ -7308,6 +7212,11 @@ nice-try@^1.0.4:
   resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
   integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
 
+node-fetch@^2.6.0:
+  version "2.6.0"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
+  integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
+
 node-forge@0.9.0:
   version "0.9.0"
   resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579"
@@ -7486,7 +7395,7 @@ object-fit-images@^3.2.3:
   resolved "https://registry.yarnpkg.com/object-fit-images/-/object-fit-images-3.2.4.tgz#6c299d38fdf207746e5d2d46c2877f6f25d15b52"
   integrity sha512-G+7LzpYfTfqUyrZlfrou/PLLLAPNC52FTy5y1CBywX+1/FkxIloOyQXBmZ3Zxa2AWO+lMF0JTuvqbr7G5e5CWg==
 
-object-inspect@^1.7.0:
+object-inspect@^1.7.0, object-inspect@^1.8.0:
   version "1.8.0"
   resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0"
   integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==
@@ -7618,9 +7527,9 @@ opencollective-postinstall@^2.0.2:
   integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==
 
 opener@^1.5.1:
-  version "1.5.1"
-  resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed"
-  integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==
+  version "1.5.2"
+  resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
+  integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==
 
 opn@^5.5.0:
   version "5.5.0"
@@ -8068,9 +7977,9 @@ posix-character-classes@^0.1.0:
   integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
 
 postcss-calc@^7.0.1:
-  version "7.0.2"
-  resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.2.tgz#504efcd008ca0273120568b0792b16cdcde8aac1"
-  integrity sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ==
+  version "7.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.3.tgz#d65cca92a3c52bf27ad37a5f732e0587b74f1623"
+  integrity sha512-IB/EAEmZhIMEIhG7Ov4x+l47UaXOS1n2f4FBUk/aKllQhtSCxWhTzn0nJgkqN7fo/jcWySvWTSB6Syk9L+31bA==
   dependencies:
     postcss "^7.0.27"
     postcss-selector-parser "^6.0.2"
@@ -8641,9 +8550,9 @@ querystring@0.2.0:
   integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
 
 querystringify@^2.1.1:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
-  integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
+  integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
 
 quote@^0.4.0:
   version "0.4.0"
@@ -9128,7 +9037,7 @@ regenerator-runtime@^0.12.0:
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
   integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
 
-regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7:
+regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7:
   version "0.13.7"
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
   integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
@@ -9372,11 +9281,6 @@ retry@^0.12.0:
   resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
   integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
 
-reusify@^1.0.4:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
-  integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
-
 rgb-regex@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1"
@@ -9428,11 +9332,6 @@ run-async@^0.1.0:
   dependencies:
     once "^1.3.0"
 
-run-parallel@^1.1.9:
-  version "1.1.9"
-  resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679"
-  integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==
-
 run-queue@^1.0.0, run-queue@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47"
@@ -9735,12 +9634,12 @@ shellwords@^0.1.1:
   integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
 
 side-channel@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.2.tgz#df5d1abadb4e4bf4af1cd8852bf132d2f7876947"
-  integrity sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA==
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.3.tgz#cdc46b057550bbab63706210838df5d4c19519c3"
+  integrity sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g==
   dependencies:
-    es-abstract "^1.17.0-next.1"
-    object-inspect "^1.7.0"
+    es-abstract "^1.18.0-next.0"
+    object-inspect "^1.8.0"
 
 signal-exit@^3.0.0, signal-exit@^3.0.2:
   version "3.0.3"
@@ -10428,35 +10327,27 @@ terser@^4.1.2, terser@^4.8.0:
     source-map "~0.6.1"
     source-map-support "~0.5.12"
 
-tesseract.js-core@^2.0.0-beta.12:
+tesseract.js-core@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/tesseract.js-core/-/tesseract.js-core-2.2.0.tgz#6ef78051272a381969fac3e45a226e85022cffef"
   integrity sha512-a8L+OJTbUipBsEDsJhDPlnLB0TY1MkTZqw5dqUwmiDSjUzwvU7HWLg/2+WDRulKUi4LE+7PnHlaBlW0k+V0U0w==
 
-tesseract.js-utils@^1.0.0-beta.8:
-  version "1.0.0-beta.8"
-  resolved "https://registry.yarnpkg.com/tesseract.js-utils/-/tesseract.js-utils-1.0.0-beta.8.tgz#d1ef25c12609a337c3e0ac12a33f9903f3145a68"
-  integrity sha512-qjHBfWfzo2o1ZY9XI0Wh2hmpp38+mIgCMOk60W5Yyie/pBl421VLBKOZUEwQgpbLnOJ24VU6Q8yXsVgtFFHcFg==
+tesseract.js@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/tesseract.js/-/tesseract.js-2.1.1.tgz#5c50fc95542ce8d834cb952bfb75a8fc85f1441d"
+  integrity sha512-utg0A8UzT1KwBvZf+UMGmM8LU6izeol6yIem0Z44+7Qqd/YWgRVQ99XOG18ApTOXX48lGE++PDwlcZYkv0ygRQ==
   dependencies:
-    axios "^0.18.0"
     bmp-js "^0.1.0"
-    file-type "^10.5.0"
-    idb-keyval "^3.1.0"
+    file-type "^12.4.1"
+    idb-keyval "^3.2.0"
+    is-electron "^2.2.0"
     is-url "^1.2.4"
-    zlibjs "^0.3.1"
-
-tesseract.js@2.0.0-alpha.16:
-  version "2.0.0-alpha.16"
-  resolved "https://registry.yarnpkg.com/tesseract.js/-/tesseract.js-2.0.0-alpha.16.tgz#1e17717234a1464481abe12283f2c3ac79603d2e"
-  integrity sha512-8g3je2Kl8rkAFtpmwilGGj+8rCiPClNQaCjW6IafOPNn7hzFnVdL6fU6rG1Xsrc4Twv0HOa75kbpx5u70/WbTA==
-  dependencies:
-    axios "^0.18.0"
-    check-types "^7.4.0"
-    is-url "1.2.2"
+    node-fetch "^2.6.0"
     opencollective-postinstall "^2.0.2"
+    regenerator-runtime "^0.13.3"
     resolve-url "^0.2.1"
-    tesseract.js-core "^2.0.0-beta.12"
-    tesseract.js-utils "^1.0.0-beta.8"
+    tesseract.js-core "^2.2.0"
+    zlibjs "^0.3.1"
 
 test-exclude@^6.0.0:
   version "6.0.0"
@@ -10797,9 +10688,9 @@ upath@^1.1.1:
   integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
 
 uri-js@^4.2.2:
-  version "4.2.2"
-  resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
-  integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602"
+  integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==
   dependencies:
     punycode "^2.1.0"
 
@@ -11217,13 +11108,13 @@ whatwg-mimetype@^2.3.0:
   integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
 
 whatwg-url@^8.0.0:
-  version "8.1.0"
-  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.1.0.tgz#c628acdcf45b82274ce7281ee31dd3c839791771"
-  integrity sha512-vEIkwNi9Hqt4TV9RdnaBPNt+E2Sgmo3gePebCRgZ1R7g6d23+53zCTnuB0amKI4AXq6VM8jj2DUAa0S1vjJxkw==
+  version "8.2.1"
+  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.2.1.tgz#ed73417230784b281fb2a32c3c501738b46167c3"
+  integrity sha512-ZmVCr6nfBeaMxEHALLEGy0LszYjpJqf6PVNQUQ1qd9Et+q7Jpygd4rGGDXgHjD8e99yLFseD69msHDM4YwPZ4A==
   dependencies:
     lodash.sortby "^4.7.0"
     tr46 "^2.0.2"
-    webidl-conversions "^5.0.0"
+    webidl-conversions "^6.1.0"
 
 which-module@^2.0.0:
   version "2.0.0"