Merge pull request #1496 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes
This commit is contained in:
		
						commit
						4ed9576cd2
					
				| 
						 | 
					@ -30,7 +30,7 @@ plugins:
 | 
				
			||||||
    channel: eslint-7
 | 
					    channel: eslint-7
 | 
				
			||||||
  rubocop:
 | 
					  rubocop:
 | 
				
			||||||
    enabled: true
 | 
					    enabled: true
 | 
				
			||||||
    channel: rubocop-1-70
 | 
					    channel: rubocop-1-8-1
 | 
				
			||||||
  sass-lint:
 | 
					  sass-lint:
 | 
				
			||||||
    enabled: true
 | 
					    enabled: true
 | 
				
			||||||
exclude_patterns:
 | 
					exclude_patterns:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					version = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test_patterns = ["/app/javascript/mastodon/**/__tests__/**"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[analyzers]]
 | 
				
			||||||
 | 
					name = "ruby"
 | 
				
			||||||
 | 
					enabled = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[analyzers]]
 | 
				
			||||||
 | 
					name = "javascript"
 | 
				
			||||||
 | 
					enabled = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [analyzers.meta]
 | 
				
			||||||
 | 
					  environment = [
 | 
				
			||||||
 | 
					    "nodejs",
 | 
				
			||||||
 | 
					    "browser",
 | 
				
			||||||
 | 
					    "jest"
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ FROM ubuntu:20.04 as build-dep
 | 
				
			||||||
SHELL ["/usr/bin/bash", "-c"]
 | 
					SHELL ["/usr/bin/bash", "-c"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Install Node v12 (LTS)
 | 
					# Install Node v12 (LTS)
 | 
				
			||||||
ENV NODE_VER="12.20.0"
 | 
					ENV NODE_VER="12.20.1"
 | 
				
			||||||
RUN ARCH= && \
 | 
					RUN ARCH= && \
 | 
				
			||||||
    dpkgArch="$(dpkg --print-architecture)" && \
 | 
					    dpkgArch="$(dpkg --print-architecture)" && \
 | 
				
			||||||
  case "${dpkgArch##*-}" in \
 | 
					  case "${dpkgArch##*-}" in \
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										12
									
								
								Gemfile
								
								
								
								
							
							
						
						
									
										12
									
								
								Gemfile
								
								
								
								
							| 
						 | 
					@ -8,7 +8,7 @@ gem 'pkg-config', '~> 1.4'
 | 
				
			||||||
gem 'puma', '~> 5.1'
 | 
					gem 'puma', '~> 5.1'
 | 
				
			||||||
gem 'rails', '~> 5.2.4.4'
 | 
					gem 'rails', '~> 5.2.4.4'
 | 
				
			||||||
gem 'sprockets', '~> 3.7.2'
 | 
					gem 'sprockets', '~> 3.7.2'
 | 
				
			||||||
gem 'thor', '~> 1.0'
 | 
					gem 'thor', '~> 1.1'
 | 
				
			||||||
gem 'rack', '~> 2.2.3'
 | 
					gem 'rack', '~> 2.2.3'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
gem 'hamlit-rails', '~> 0.2'
 | 
					gem 'hamlit-rails', '~> 0.2'
 | 
				
			||||||
| 
						 | 
					@ -31,7 +31,7 @@ gem 'bootsnap', '~> 1.5', require: false
 | 
				
			||||||
gem 'browser'
 | 
					gem 'browser'
 | 
				
			||||||
gem 'charlock_holmes', '~> 0.7.7'
 | 
					gem 'charlock_holmes', '~> 0.7.7'
 | 
				
			||||||
gem 'iso-639'
 | 
					gem 'iso-639'
 | 
				
			||||||
gem 'chewy', '~> 5.1'
 | 
					gem 'chewy', '~> 5.2'
 | 
				
			||||||
gem 'cld3', '~> 3.4.1'
 | 
					gem 'cld3', '~> 3.4.1'
 | 
				
			||||||
gem 'devise', '~> 4.7'
 | 
					gem 'devise', '~> 4.7'
 | 
				
			||||||
gem 'devise-two-factor', '~> 3.1'
 | 
					gem 'devise-two-factor', '~> 3.1'
 | 
				
			||||||
| 
						 | 
					@ -73,7 +73,7 @@ gem 'parallel', '~> 1.20'
 | 
				
			||||||
gem 'posix-spawn'
 | 
					gem 'posix-spawn'
 | 
				
			||||||
gem 'pundit', '~> 2.1'
 | 
					gem 'pundit', '~> 2.1'
 | 
				
			||||||
gem 'premailer-rails'
 | 
					gem 'premailer-rails'
 | 
				
			||||||
gem 'rack-attack', '~> 6.3'
 | 
					gem 'rack-attack', '~> 6.4'
 | 
				
			||||||
gem 'rack-cors', '~> 1.1', require: 'rack/cors'
 | 
					gem 'rack-cors', '~> 1.1', require: 'rack/cors'
 | 
				
			||||||
gem 'rails-i18n', '~> 5.1'
 | 
					gem 'rails-i18n', '~> 5.1'
 | 
				
			||||||
gem 'rails-settings-cached', '~> 0.6'
 | 
					gem 'rails-settings-cached', '~> 0.6'
 | 
				
			||||||
| 
						 | 
					@ -94,7 +94,7 @@ gem 'stoplight', '~> 2.2.1'
 | 
				
			||||||
gem 'strong_migrations', '~> 0.7'
 | 
					gem 'strong_migrations', '~> 0.7'
 | 
				
			||||||
gem 'tty-prompt', '~> 0.23', require: false
 | 
					gem 'tty-prompt', '~> 0.23', require: false
 | 
				
			||||||
gem 'twitter-text', '~> 1.14'
 | 
					gem 'twitter-text', '~> 1.14'
 | 
				
			||||||
gem 'tzinfo-data', '~> 1.2020'
 | 
					gem 'tzinfo-data', '~> 1.2021'
 | 
				
			||||||
gem 'webpacker', '~> 5.2'
 | 
					gem 'webpacker', '~> 5.2'
 | 
				
			||||||
gem 'webpush'
 | 
					gem 'webpush'
 | 
				
			||||||
gem 'webauthn', '~> 3.0.0.alpha1'
 | 
					gem 'webauthn', '~> 3.0.0.alpha1'
 | 
				
			||||||
| 
						 | 
					@ -119,7 +119,7 @@ group :production, :test do
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
group :test do
 | 
					group :test do
 | 
				
			||||||
  gem 'capybara', '~> 3.34'
 | 
					  gem 'capybara', '~> 3.35'
 | 
				
			||||||
  gem 'climate_control', '~> 0.2'
 | 
					  gem 'climate_control', '~> 0.2'
 | 
				
			||||||
  gem 'faker', '~> 2.15'
 | 
					  gem 'faker', '~> 2.15'
 | 
				
			||||||
  gem 'microformats', '~> 4.2'
 | 
					  gem 'microformats', '~> 4.2'
 | 
				
			||||||
| 
						 | 
					@ -140,7 +140,7 @@ group :development do
 | 
				
			||||||
  gem 'letter_opener', '~> 1.7'
 | 
					  gem 'letter_opener', '~> 1.7'
 | 
				
			||||||
  gem 'letter_opener_web', '~> 1.4'
 | 
					  gem 'letter_opener_web', '~> 1.4'
 | 
				
			||||||
  gem 'memory_profiler'
 | 
					  gem 'memory_profiler'
 | 
				
			||||||
  gem 'rubocop', '~> 1.7', require: false
 | 
					  gem 'rubocop', '~> 1.8', require: false
 | 
				
			||||||
  gem 'rubocop-rails', '~> 2.9', require: false
 | 
					  gem 'rubocop-rails', '~> 2.9', require: false
 | 
				
			||||||
  gem 'brakeman', '~> 4.10', require: false
 | 
					  gem 'brakeman', '~> 4.10', require: false
 | 
				
			||||||
  gem 'bundler-audit', '~> 0.7', require: false
 | 
					  gem 'bundler-audit', '~> 0.7', require: false
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										64
									
								
								Gemfile.lock
								
								
								
								
							
							
						
						
									
										64
									
								
								Gemfile.lock
								
								
								
								
							| 
						 | 
					@ -109,7 +109,7 @@ GEM
 | 
				
			||||||
    brakeman (4.10.1)
 | 
					    brakeman (4.10.1)
 | 
				
			||||||
    browser (4.2.0)
 | 
					    browser (4.2.0)
 | 
				
			||||||
    builder (3.2.4)
 | 
					    builder (3.2.4)
 | 
				
			||||||
    bullet (6.1.2)
 | 
					    bullet (6.1.3)
 | 
				
			||||||
      activesupport (>= 3.0.0)
 | 
					      activesupport (>= 3.0.0)
 | 
				
			||||||
      uniform_notifier (~> 1.11)
 | 
					      uniform_notifier (~> 1.11)
 | 
				
			||||||
    bundler-audit (0.7.0.1)
 | 
					    bundler-audit (0.7.0.1)
 | 
				
			||||||
| 
						 | 
					@ -131,20 +131,20 @@ GEM
 | 
				
			||||||
      sshkit (~> 1.3)
 | 
					      sshkit (~> 1.3)
 | 
				
			||||||
    capistrano-yarn (2.0.2)
 | 
					    capistrano-yarn (2.0.2)
 | 
				
			||||||
      capistrano (~> 3.0)
 | 
					      capistrano (~> 3.0)
 | 
				
			||||||
    capybara (3.34.0)
 | 
					    capybara (3.35.3)
 | 
				
			||||||
      addressable
 | 
					      addressable
 | 
				
			||||||
      mini_mime (>= 0.1.3)
 | 
					      mini_mime (>= 0.1.3)
 | 
				
			||||||
      nokogiri (~> 1.8)
 | 
					      nokogiri (~> 1.8)
 | 
				
			||||||
      rack (>= 1.6.0)
 | 
					      rack (>= 1.6.0)
 | 
				
			||||||
      rack-test (>= 0.6.3)
 | 
					      rack-test (>= 0.6.3)
 | 
				
			||||||
      regexp_parser (~> 1.5)
 | 
					      regexp_parser (>= 1.5, < 3.0)
 | 
				
			||||||
      xpath (~> 3.2)
 | 
					      xpath (~> 3.2)
 | 
				
			||||||
    case_transform (0.2)
 | 
					    case_transform (0.2)
 | 
				
			||||||
      activesupport
 | 
					      activesupport
 | 
				
			||||||
    cbor (0.5.9.6)
 | 
					    cbor (0.5.9.6)
 | 
				
			||||||
    charlock_holmes (0.7.7)
 | 
					    charlock_holmes (0.7.7)
 | 
				
			||||||
    chewy (5.1.0)
 | 
					    chewy (5.2.0)
 | 
				
			||||||
      activesupport (>= 4.0)
 | 
					      activesupport (>= 5.2)
 | 
				
			||||||
      elasticsearch (>= 2.0.0)
 | 
					      elasticsearch (>= 2.0.0)
 | 
				
			||||||
      elasticsearch-dsl
 | 
					      elasticsearch-dsl
 | 
				
			||||||
    chunky_png (1.3.15)
 | 
					    chunky_png (1.3.15)
 | 
				
			||||||
| 
						 | 
					@ -195,13 +195,13 @@ GEM
 | 
				
			||||||
      railties (>= 3.2)
 | 
					      railties (>= 3.2)
 | 
				
			||||||
    e2mmap (0.1.0)
 | 
					    e2mmap (0.1.0)
 | 
				
			||||||
    ed25519 (1.2.4)
 | 
					    ed25519 (1.2.4)
 | 
				
			||||||
    elasticsearch (7.9.0)
 | 
					    elasticsearch (7.10.1)
 | 
				
			||||||
      elasticsearch-api (= 7.9.0)
 | 
					      elasticsearch-api (= 7.10.1)
 | 
				
			||||||
      elasticsearch-transport (= 7.9.0)
 | 
					      elasticsearch-transport (= 7.10.1)
 | 
				
			||||||
    elasticsearch-api (7.9.0)
 | 
					    elasticsearch-api (7.10.1)
 | 
				
			||||||
      multi_json
 | 
					      multi_json
 | 
				
			||||||
    elasticsearch-dsl (0.1.9)
 | 
					    elasticsearch-dsl (0.1.9)
 | 
				
			||||||
    elasticsearch-transport (7.9.0)
 | 
					    elasticsearch-transport (7.10.1)
 | 
				
			||||||
      faraday (~> 1)
 | 
					      faraday (~> 1)
 | 
				
			||||||
      multi_json
 | 
					      multi_json
 | 
				
			||||||
    encryptor (3.0.0)
 | 
					    encryptor (3.0.0)
 | 
				
			||||||
| 
						 | 
					@ -212,10 +212,13 @@ GEM
 | 
				
			||||||
    fabrication (2.21.1)
 | 
					    fabrication (2.21.1)
 | 
				
			||||||
    faker (2.15.1)
 | 
					    faker (2.15.1)
 | 
				
			||||||
      i18n (>= 1.6, < 2)
 | 
					      i18n (>= 1.6, < 2)
 | 
				
			||||||
    faraday (1.0.1)
 | 
					    faraday (1.3.0)
 | 
				
			||||||
 | 
					      faraday-net_http (~> 1.0)
 | 
				
			||||||
      multipart-post (>= 1.2, < 3)
 | 
					      multipart-post (>= 1.2, < 3)
 | 
				
			||||||
 | 
					      ruby2_keywords
 | 
				
			||||||
 | 
					    faraday-net_http (1.0.1)
 | 
				
			||||||
    fast_blank (1.0.0)
 | 
					    fast_blank (1.0.0)
 | 
				
			||||||
    fastimage (2.2.1)
 | 
					    fastimage (2.2.2)
 | 
				
			||||||
    ffi (1.10.0)
 | 
					    ffi (1.10.0)
 | 
				
			||||||
    ffi-compiler (1.0.1)
 | 
					    ffi-compiler (1.0.1)
 | 
				
			||||||
      ffi (>= 1.0.0)
 | 
					      ffi (>= 1.0.0)
 | 
				
			||||||
| 
						 | 
					@ -369,7 +372,7 @@ GEM
 | 
				
			||||||
      concurrent-ruby (~> 1.0, >= 1.0.2)
 | 
					      concurrent-ruby (~> 1.0, >= 1.0.2)
 | 
				
			||||||
      sidekiq (>= 3.5)
 | 
					      sidekiq (>= 3.5)
 | 
				
			||||||
      statsd-ruby (~> 1.4, >= 1.4.0)
 | 
					      statsd-ruby (~> 1.4, >= 1.4.0)
 | 
				
			||||||
    oj (3.11.0)
 | 
					    oj (3.11.2)
 | 
				
			||||||
    omniauth (1.9.1)
 | 
					    omniauth (1.9.1)
 | 
				
			||||||
      hashie (>= 3.4.6)
 | 
					      hashie (>= 3.4.6)
 | 
				
			||||||
      rack (>= 1.6.2, < 3)
 | 
					      rack (>= 1.6.2, < 3)
 | 
				
			||||||
| 
						 | 
					@ -436,7 +439,7 @@ GEM
 | 
				
			||||||
    raabro (1.3.3)
 | 
					    raabro (1.3.3)
 | 
				
			||||||
    racc (1.5.2)
 | 
					    racc (1.5.2)
 | 
				
			||||||
    rack (2.2.3)
 | 
					    rack (2.2.3)
 | 
				
			||||||
    rack-attack (6.3.1)
 | 
					    rack-attack (6.4.0)
 | 
				
			||||||
      rack (>= 1.0, < 3)
 | 
					      rack (>= 1.0, < 3)
 | 
				
			||||||
    rack-cors (1.1.1)
 | 
					    rack-cors (1.1.1)
 | 
				
			||||||
      rack (>= 2.0.0)
 | 
					      rack (>= 2.0.0)
 | 
				
			||||||
| 
						 | 
					@ -504,7 +507,7 @@ GEM
 | 
				
			||||||
      redis-store (>= 1.2, < 2)
 | 
					      redis-store (>= 1.2, < 2)
 | 
				
			||||||
    redis-store (1.9.0)
 | 
					    redis-store (1.9.0)
 | 
				
			||||||
      redis (>= 4, < 5)
 | 
					      redis (>= 4, < 5)
 | 
				
			||||||
    regexp_parser (1.8.2)
 | 
					    regexp_parser (2.0.3)
 | 
				
			||||||
    request_store (1.5.0)
 | 
					    request_store (1.5.0)
 | 
				
			||||||
      rack (>= 1.4)
 | 
					      rack (>= 1.4)
 | 
				
			||||||
    responders (3.0.1)
 | 
					    responders (3.0.1)
 | 
				
			||||||
| 
						 | 
					@ -539,16 +542,16 @@ GEM
 | 
				
			||||||
    rspec-support (3.10.1)
 | 
					    rspec-support (3.10.1)
 | 
				
			||||||
    rspec_junit_formatter (0.4.1)
 | 
					    rspec_junit_formatter (0.4.1)
 | 
				
			||||||
      rspec-core (>= 2, < 4, != 2.12.0)
 | 
					      rspec-core (>= 2, < 4, != 2.12.0)
 | 
				
			||||||
    rubocop (1.7.0)
 | 
					    rubocop (1.8.1)
 | 
				
			||||||
      parallel (~> 1.10)
 | 
					      parallel (~> 1.10)
 | 
				
			||||||
      parser (>= 2.7.1.5)
 | 
					      parser (>= 3.0.0.0)
 | 
				
			||||||
      rainbow (>= 2.2.2, < 4.0)
 | 
					      rainbow (>= 2.2.2, < 4.0)
 | 
				
			||||||
      regexp_parser (>= 1.8, < 3.0)
 | 
					      regexp_parser (>= 1.8, < 3.0)
 | 
				
			||||||
      rexml
 | 
					      rexml
 | 
				
			||||||
      rubocop-ast (>= 1.2.0, < 2.0)
 | 
					      rubocop-ast (>= 1.2.0, < 2.0)
 | 
				
			||||||
      ruby-progressbar (~> 1.7)
 | 
					      ruby-progressbar (~> 1.7)
 | 
				
			||||||
      unicode-display_width (>= 1.4.0, < 2.0)
 | 
					      unicode-display_width (>= 1.4.0, < 3.0)
 | 
				
			||||||
    rubocop-ast (1.3.0)
 | 
					    rubocop-ast (1.4.0)
 | 
				
			||||||
      parser (>= 2.7.1.5)
 | 
					      parser (>= 2.7.1.5)
 | 
				
			||||||
    rubocop-rails (2.9.1)
 | 
					    rubocop-rails (2.9.1)
 | 
				
			||||||
      activesupport (>= 4.2.0)
 | 
					      activesupport (>= 4.2.0)
 | 
				
			||||||
| 
						 | 
					@ -557,6 +560,7 @@ GEM
 | 
				
			||||||
    ruby-progressbar (1.11.0)
 | 
					    ruby-progressbar (1.11.0)
 | 
				
			||||||
    ruby-saml (1.11.0)
 | 
					    ruby-saml (1.11.0)
 | 
				
			||||||
      nokogiri (>= 1.5.10)
 | 
					      nokogiri (>= 1.5.10)
 | 
				
			||||||
 | 
					    ruby2_keywords (0.0.4)
 | 
				
			||||||
    rufus-scheduler (3.6.0)
 | 
					    rufus-scheduler (3.6.0)
 | 
				
			||||||
      fugit (~> 1.1, >= 1.1.6)
 | 
					      fugit (~> 1.1, >= 1.1.6)
 | 
				
			||||||
    safety_net_attestation (0.4.0)
 | 
					    safety_net_attestation (0.4.0)
 | 
				
			||||||
| 
						 | 
					@ -570,7 +574,7 @@ GEM
 | 
				
			||||||
      railties (>= 4.0.0)
 | 
					      railties (>= 4.0.0)
 | 
				
			||||||
    securecompare (1.0.0)
 | 
					    securecompare (1.0.0)
 | 
				
			||||||
    semantic_range (2.3.0)
 | 
					    semantic_range (2.3.0)
 | 
				
			||||||
    sidekiq (6.1.2)
 | 
					    sidekiq (6.1.3)
 | 
				
			||||||
      connection_pool (>= 2.2.2)
 | 
					      connection_pool (>= 2.2.2)
 | 
				
			||||||
      rack (~> 2.0)
 | 
					      rack (~> 2.0)
 | 
				
			||||||
      redis (>= 4.2.0)
 | 
					      redis (>= 4.2.0)
 | 
				
			||||||
| 
						 | 
					@ -620,7 +624,7 @@ GEM
 | 
				
			||||||
      unicode-display_width (~> 1.1, >= 1.1.1)
 | 
					      unicode-display_width (~> 1.1, >= 1.1.1)
 | 
				
			||||||
    terrapin (0.6.0)
 | 
					    terrapin (0.6.0)
 | 
				
			||||||
      climate_control (>= 0.0.3, < 1.0)
 | 
					      climate_control (>= 0.0.3, < 1.0)
 | 
				
			||||||
    thor (1.0.1)
 | 
					    thor (1.1.0)
 | 
				
			||||||
    thread_safe (0.3.6)
 | 
					    thread_safe (0.3.6)
 | 
				
			||||||
    thwait (0.2.0)
 | 
					    thwait (0.2.0)
 | 
				
			||||||
      e2mmap
 | 
					      e2mmap
 | 
				
			||||||
| 
						 | 
					@ -642,13 +646,13 @@ GEM
 | 
				
			||||||
      unf (~> 0.1.0)
 | 
					      unf (~> 0.1.0)
 | 
				
			||||||
    tzinfo (1.2.9)
 | 
					    tzinfo (1.2.9)
 | 
				
			||||||
      thread_safe (~> 0.1)
 | 
					      thread_safe (~> 0.1)
 | 
				
			||||||
    tzinfo-data (1.2020.6)
 | 
					    tzinfo-data (1.2021.1)
 | 
				
			||||||
      tzinfo (>= 1.0.0)
 | 
					      tzinfo (>= 1.0.0)
 | 
				
			||||||
    unf (0.1.4)
 | 
					    unf (0.1.4)
 | 
				
			||||||
      unf_ext
 | 
					      unf_ext
 | 
				
			||||||
    unf_ext (0.0.7.7)
 | 
					    unf_ext (0.0.7.7)
 | 
				
			||||||
    unicode-display_width (1.7.0)
 | 
					    unicode-display_width (1.7.0)
 | 
				
			||||||
    uniform_notifier (1.13.0)
 | 
					    uniform_notifier (1.13.2)
 | 
				
			||||||
    warden (1.2.9)
 | 
					    warden (1.2.9)
 | 
				
			||||||
      rack (>= 2.0.9)
 | 
					      rack (>= 2.0.9)
 | 
				
			||||||
    webauthn (3.0.0.alpha1)
 | 
					    webauthn (3.0.0.alpha1)
 | 
				
			||||||
| 
						 | 
					@ -661,7 +665,7 @@ GEM
 | 
				
			||||||
      safety_net_attestation (~> 0.4.0)
 | 
					      safety_net_attestation (~> 0.4.0)
 | 
				
			||||||
      securecompare (~> 1.0)
 | 
					      securecompare (~> 1.0)
 | 
				
			||||||
      tpm-key_attestation (~> 0.9.0)
 | 
					      tpm-key_attestation (~> 0.9.0)
 | 
				
			||||||
    webmock (3.11.1)
 | 
					    webmock (3.11.2)
 | 
				
			||||||
      addressable (>= 2.3.6)
 | 
					      addressable (>= 2.3.6)
 | 
				
			||||||
      crack (>= 0.3.2)
 | 
					      crack (>= 0.3.2)
 | 
				
			||||||
      hashdiff (>= 0.4.0, < 2.0.0)
 | 
					      hashdiff (>= 0.4.0, < 2.0.0)
 | 
				
			||||||
| 
						 | 
					@ -702,9 +706,9 @@ DEPENDENCIES
 | 
				
			||||||
  capistrano-rails (~> 1.6)
 | 
					  capistrano-rails (~> 1.6)
 | 
				
			||||||
  capistrano-rbenv (~> 2.2)
 | 
					  capistrano-rbenv (~> 2.2)
 | 
				
			||||||
  capistrano-yarn (~> 2.0)
 | 
					  capistrano-yarn (~> 2.0)
 | 
				
			||||||
  capybara (~> 3.34)
 | 
					  capybara (~> 3.35)
 | 
				
			||||||
  charlock_holmes (~> 0.7.7)
 | 
					  charlock_holmes (~> 0.7.7)
 | 
				
			||||||
  chewy (~> 5.1)
 | 
					  chewy (~> 5.2)
 | 
				
			||||||
  cld3 (~> 3.4.1)
 | 
					  cld3 (~> 3.4.1)
 | 
				
			||||||
  climate_control (~> 0.2)
 | 
					  climate_control (~> 0.2)
 | 
				
			||||||
  color_diff (~> 0.1)
 | 
					  color_diff (~> 0.1)
 | 
				
			||||||
| 
						 | 
					@ -773,7 +777,7 @@ DEPENDENCIES
 | 
				
			||||||
  puma (~> 5.1)
 | 
					  puma (~> 5.1)
 | 
				
			||||||
  pundit (~> 2.1)
 | 
					  pundit (~> 2.1)
 | 
				
			||||||
  rack (~> 2.2.3)
 | 
					  rack (~> 2.2.3)
 | 
				
			||||||
  rack-attack (~> 6.3)
 | 
					  rack-attack (~> 6.4)
 | 
				
			||||||
  rack-cors (~> 1.1)
 | 
					  rack-cors (~> 1.1)
 | 
				
			||||||
  rails (~> 5.2.4.4)
 | 
					  rails (~> 5.2.4.4)
 | 
				
			||||||
  rails-controller-testing (~> 1.0)
 | 
					  rails-controller-testing (~> 1.0)
 | 
				
			||||||
| 
						 | 
					@ -788,7 +792,7 @@ DEPENDENCIES
 | 
				
			||||||
  rspec-rails (~> 4.0)
 | 
					  rspec-rails (~> 4.0)
 | 
				
			||||||
  rspec-sidekiq (~> 3.1)
 | 
					  rspec-sidekiq (~> 3.1)
 | 
				
			||||||
  rspec_junit_formatter (~> 0.4)
 | 
					  rspec_junit_formatter (~> 0.4)
 | 
				
			||||||
  rubocop (~> 1.7)
 | 
					  rubocop (~> 1.8)
 | 
				
			||||||
  rubocop-rails (~> 2.9)
 | 
					  rubocop-rails (~> 2.9)
 | 
				
			||||||
  ruby-progressbar (~> 1.11)
 | 
					  ruby-progressbar (~> 1.11)
 | 
				
			||||||
  sanitize (~> 5.2)
 | 
					  sanitize (~> 5.2)
 | 
				
			||||||
| 
						 | 
					@ -806,10 +810,10 @@ DEPENDENCIES
 | 
				
			||||||
  stoplight (~> 2.2.1)
 | 
					  stoplight (~> 2.2.1)
 | 
				
			||||||
  streamio-ffmpeg (~> 3.0)
 | 
					  streamio-ffmpeg (~> 3.0)
 | 
				
			||||||
  strong_migrations (~> 0.7)
 | 
					  strong_migrations (~> 0.7)
 | 
				
			||||||
  thor (~> 1.0)
 | 
					  thor (~> 1.1)
 | 
				
			||||||
  tty-prompt (~> 0.23)
 | 
					  tty-prompt (~> 0.23)
 | 
				
			||||||
  twitter-text (~> 1.14)
 | 
					  twitter-text (~> 1.14)
 | 
				
			||||||
  tzinfo-data (~> 1.2020)
 | 
					  tzinfo-data (~> 1.2021)
 | 
				
			||||||
  webauthn (~> 3.0.0.alpha1)
 | 
					  webauthn (~> 3.0.0.alpha1)
 | 
				
			||||||
  webmock (~> 3.11)
 | 
					  webmock (~> 3.11)
 | 
				
			||||||
  webpacker (~> 5.2)
 | 
					  webpacker (~> 5.2)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,12 +40,13 @@ class Api::V1::NotificationsController < Api::BaseController
 | 
				
			||||||
  private
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def load_notifications
 | 
					  def load_notifications
 | 
				
			||||||
    cache_collection_paginated_by_id(
 | 
					    notifications = browserable_account_notifications.includes(from_account: :account_stat).to_a_paginated_by_id(
 | 
				
			||||||
      browserable_account_notifications,
 | 
					 | 
				
			||||||
      Notification,
 | 
					 | 
				
			||||||
      limit_param(DEFAULT_NOTIFICATIONS_LIMIT),
 | 
					      limit_param(DEFAULT_NOTIFICATIONS_LIMIT),
 | 
				
			||||||
      params_slice(:max_id, :since_id, :min_id)
 | 
					      params_slice(:max_id, :since_id, :min_id)
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					    Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses|
 | 
				
			||||||
 | 
					      cache_collection(target_statuses, Status)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def browserable_account_notifications
 | 
					  def browserable_account_notifications
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,45 +15,30 @@ export default class DisplayName extends React.PureComponent {
 | 
				
			||||||
    handleClick: PropTypes.func,
 | 
					    handleClick: PropTypes.func,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _updateEmojis () {
 | 
					  handleMouseEnter = ({ currentTarget }) => {
 | 
				
			||||||
    const node = this.node;
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!node || autoPlayGif) {
 | 
					 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const emojis = node.querySelectorAll('.custom-emoji');
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (var i = 0; i < emojis.length; i++) {
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
      let emoji = emojis[i];
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
      if (emoji.classList.contains('status-emoji')) {
 | 
					      emoji.src = emoji.getAttribute('data-original');
 | 
				
			||||||
        continue;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      emoji.classList.add('status-emoji');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false);
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidMount () {
 | 
					  handleMouseLeave = ({ currentTarget }) => {
 | 
				
			||||||
    this._updateEmojis();
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
  }
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidUpdate () {
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
    this._updateEmojis();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleEmojiMouseEnter = ({ target }) => {
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
    target.src = target.getAttribute('data-original');
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
  }
 | 
					      emoji.src = emoji.getAttribute('data-static');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  handleEmojiMouseLeave = ({ target }) => {
 | 
					 | 
				
			||||||
    target.src = target.getAttribute('data-static');
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  setRef = (c) => {
 | 
					 | 
				
			||||||
    this.node = c;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render() {
 | 
					  render() {
 | 
				
			||||||
| 
						 | 
					@ -101,7 +86,7 @@ export default class DisplayName extends React.PureComponent {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <span className={computedClass} ref={this.setRef}>
 | 
					      <span className={computedClass} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
 | 
				
			||||||
        {displayName}
 | 
					        {displayName}
 | 
				
			||||||
        {inline ? ' ' : null}
 | 
					        {inline ? ' ' : null}
 | 
				
			||||||
        {suffix}
 | 
					        {suffix}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -154,35 +154,38 @@ export default class StatusContent extends React.PureComponent {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _updateStatusEmojis () {
 | 
					  handleMouseEnter = ({ currentTarget }) => {
 | 
				
			||||||
    const node = this.node;
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!node || autoPlayGif) {
 | 
					 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const emojis = node.querySelectorAll('.custom-emoji');
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (var i = 0; i < emojis.length; i++) {
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
      let emoji = emojis[i];
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
      if (emoji.classList.contains('status-emoji')) {
 | 
					      emoji.src = emoji.getAttribute('data-original');
 | 
				
			||||||
        continue;
 | 
					    }
 | 
				
			||||||
      }
 | 
					  }
 | 
				
			||||||
      emoji.classList.add('status-emoji');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false);
 | 
					  handleMouseLeave = ({ currentTarget }) => {
 | 
				
			||||||
      emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false);
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
 | 
					      emoji.src = emoji.getAttribute('data-static');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidMount () {
 | 
					  componentDidMount () {
 | 
				
			||||||
    this._updateStatusLinks();
 | 
					    this._updateStatusLinks();
 | 
				
			||||||
    this._updateStatusEmojis();
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidUpdate () {
 | 
					  componentDidUpdate () {
 | 
				
			||||||
    this._updateStatusLinks();
 | 
					    this._updateStatusLinks();
 | 
				
			||||||
    this._updateStatusEmojis();
 | 
					 | 
				
			||||||
    if (this.props.onUpdate) this.props.onUpdate();
 | 
					    if (this.props.onUpdate) this.props.onUpdate();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -206,14 +209,6 @@ export default class StatusContent extends React.PureComponent {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleEmojiMouseEnter = ({ target }) => {
 | 
					 | 
				
			||||||
    target.src = target.getAttribute('data-original');
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleEmojiMouseLeave = ({ target }) => {
 | 
					 | 
				
			||||||
    target.src = target.getAttribute('data-static');
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleMouseDown = (e) => {
 | 
					  handleMouseDown = (e) => {
 | 
				
			||||||
    this.startXY = [e.clientX, e.clientY];
 | 
					    this.startXY = [e.clientX, e.clientY];
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -253,10 +248,6 @@ export default class StatusContent extends React.PureComponent {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  setRef = (c) => {
 | 
					 | 
				
			||||||
    this.node = c;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  setContentsRef = (c) => {
 | 
					  setContentsRef = (c) => {
 | 
				
			||||||
    this.contentsNode = c;
 | 
					    this.contentsNode = c;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -323,7 +314,7 @@ export default class StatusContent extends React.PureComponent {
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return (
 | 
					      return (
 | 
				
			||||||
        <div className={classNames} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} ref={this.setRef}>
 | 
					        <div className={classNames} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
 | 
				
			||||||
          <p
 | 
					          <p
 | 
				
			||||||
            style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}
 | 
					            style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}
 | 
				
			||||||
          >
 | 
					          >
 | 
				
			||||||
| 
						 | 
					@ -343,6 +334,8 @@ export default class StatusContent extends React.PureComponent {
 | 
				
			||||||
              tabIndex={!hidden ? 0 : null}
 | 
					              tabIndex={!hidden ? 0 : null}
 | 
				
			||||||
              dangerouslySetInnerHTML={content}
 | 
					              dangerouslySetInnerHTML={content}
 | 
				
			||||||
              className='status__content__text translate'
 | 
					              className='status__content__text translate'
 | 
				
			||||||
 | 
					              onMouseEnter={this.handleMouseEnter}
 | 
				
			||||||
 | 
					              onMouseLeave={this.handleMouseLeave}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
            {media}
 | 
					            {media}
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
| 
						 | 
					@ -356,7 +349,6 @@ export default class StatusContent extends React.PureComponent {
 | 
				
			||||||
          onMouseDown={this.handleMouseDown}
 | 
					          onMouseDown={this.handleMouseDown}
 | 
				
			||||||
          onMouseUp={this.handleMouseUp}
 | 
					          onMouseUp={this.handleMouseUp}
 | 
				
			||||||
          tabIndex='0'
 | 
					          tabIndex='0'
 | 
				
			||||||
          ref={this.setRef}
 | 
					 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <div
 | 
					          <div
 | 
				
			||||||
            ref={this.setContentsRef}
 | 
					            ref={this.setContentsRef}
 | 
				
			||||||
| 
						 | 
					@ -364,6 +356,8 @@ export default class StatusContent extends React.PureComponent {
 | 
				
			||||||
            dangerouslySetInnerHTML={content}
 | 
					            dangerouslySetInnerHTML={content}
 | 
				
			||||||
            className='status__content__text translate'
 | 
					            className='status__content__text translate'
 | 
				
			||||||
            tabIndex='0'
 | 
					            tabIndex='0'
 | 
				
			||||||
 | 
					            onMouseEnter={this.handleMouseEnter}
 | 
				
			||||||
 | 
					            onMouseLeave={this.handleMouseLeave}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
          {media}
 | 
					          {media}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
| 
						 | 
					@ -373,9 +367,16 @@ export default class StatusContent extends React.PureComponent {
 | 
				
			||||||
        <div
 | 
					        <div
 | 
				
			||||||
          className='status__content'
 | 
					          className='status__content'
 | 
				
			||||||
          tabIndex='0'
 | 
					          tabIndex='0'
 | 
				
			||||||
          ref={this.setRef}
 | 
					 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <div ref={this.setContentsRef} key={`contents-${tagLinks}`} className='status__content__text translate' dangerouslySetInnerHTML={content} tabIndex='0' />
 | 
					          <div
 | 
				
			||||||
 | 
					            ref={this.setContentsRef}
 | 
				
			||||||
 | 
					            key={`contents-${tagLinks}`}
 | 
				
			||||||
 | 
					            className='status__content__text translate'
 | 
				
			||||||
 | 
					            dangerouslySetInnerHTML={content}
 | 
				
			||||||
 | 
					            tabIndex='0'
 | 
				
			||||||
 | 
					            onMouseEnter={this.handleMouseEnter}
 | 
				
			||||||
 | 
					            onMouseLeave={this.handleMouseLeave}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
          {media}
 | 
					          {media}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -88,45 +88,30 @@ class Header extends ImmutablePureComponent {
 | 
				
			||||||
    window.open(profileLink, '_blank');
 | 
					    window.open(profileLink, '_blank');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _updateEmojis () {
 | 
					  handleMouseEnter = ({ currentTarget }) => {
 | 
				
			||||||
    const node = this.node;
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!node || autoPlayGif) {
 | 
					 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const emojis = node.querySelectorAll('.custom-emoji');
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (var i = 0; i < emojis.length; i++) {
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
      let emoji = emojis[i];
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
      if (emoji.classList.contains('status-emoji')) {
 | 
					      emoji.src = emoji.getAttribute('data-original');
 | 
				
			||||||
        continue;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      emoji.classList.add('status-emoji');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false);
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidMount () {
 | 
					  handleMouseLeave = ({ currentTarget }) => {
 | 
				
			||||||
    this._updateEmojis();
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
  }
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidUpdate () {
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
    this._updateEmojis();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleEmojiMouseEnter = ({ target }) => {
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
    target.src = target.getAttribute('data-original');
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
  }
 | 
					      emoji.src = emoji.getAttribute('data-static');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  handleEmojiMouseLeave = ({ target }) => {
 | 
					 | 
				
			||||||
    target.src = target.getAttribute('data-static');
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  setRef = (c) => {
 | 
					 | 
				
			||||||
    this.node = c;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
| 
						 | 
					@ -279,7 +264,7 @@ class Header extends ImmutablePureComponent {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <div className={classNames('account__header', { inactive: !!account.get('moved') })} ref={this.setRef}>
 | 
					      <div className={classNames('account__header', { inactive: !!account.get('moved') })} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
 | 
				
			||||||
        <div className='account__header__image'>
 | 
					        <div className='account__header__image'>
 | 
				
			||||||
          <div className='account__header__info'>
 | 
					          <div className='account__header__info'>
 | 
				
			||||||
            {info}
 | 
					            {info}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -67,41 +67,30 @@ class Conversation extends ImmutablePureComponent {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _updateEmojis () {
 | 
					  handleMouseEnter = ({ currentTarget }) => {
 | 
				
			||||||
    const node = this.namesNode;
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!node || autoPlayGif) {
 | 
					 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const emojis = node.querySelectorAll('.custom-emoji');
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (var i = 0; i < emojis.length; i++) {
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
      let emoji = emojis[i];
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
      if (emoji.classList.contains('status-emoji')) {
 | 
					      emoji.src = emoji.getAttribute('data-original');
 | 
				
			||||||
        continue;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      emoji.classList.add('status-emoji');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false);
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidMount () {
 | 
					  handleMouseLeave = ({ currentTarget }) => {
 | 
				
			||||||
    this._updateEmojis();
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
  }
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidUpdate () {
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
    this._updateEmojis();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleEmojiMouseEnter = ({ target }) => {
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
    target.src = target.getAttribute('data-original');
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
  }
 | 
					      emoji.src = emoji.getAttribute('data-static');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  handleEmojiMouseLeave = ({ target }) => {
 | 
					 | 
				
			||||||
    target.src = target.getAttribute('data-static');
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleClick = () => {
 | 
					  handleClick = () => {
 | 
				
			||||||
| 
						 | 
					@ -152,10 +141,6 @@ class Conversation extends ImmutablePureComponent {
 | 
				
			||||||
    this.setState({ isExpanded: value });
 | 
					    this.setState({ isExpanded: value });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  setNamesRef = (c) => {
 | 
					 | 
				
			||||||
    this.namesNode = c;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
    const { accounts, lastStatus, unread, scrollKey, intl } = this.props;
 | 
					    const { accounts, lastStatus, unread, scrollKey, intl } = this.props;
 | 
				
			||||||
    const { isExpanded } = this.state;
 | 
					    const { isExpanded } = this.state;
 | 
				
			||||||
| 
						 | 
					@ -206,7 +191,7 @@ class Conversation extends ImmutablePureComponent {
 | 
				
			||||||
                {unread && <span className='conversation__unread' />} <RelativeTimestamp timestamp={lastStatus.get('created_at')} />
 | 
					                {unread && <span className='conversation__unread' />} <RelativeTimestamp timestamp={lastStatus.get('created_at')} />
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              <div className='conversation__content__names' ref={this.setNamesRef}>
 | 
					              <div className='conversation__content__names' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
 | 
				
			||||||
                <FormattedMessage id='conversation.with' defaultMessage='With {names}' values={{ names: <span>{names}</span> }} />
 | 
					                <FormattedMessage id='conversation.with' defaultMessage='With {names}' values={{ names: <span>{names}</span> }} />
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -102,43 +102,32 @@ class AccountCard extends ImmutablePureComponent {
 | 
				
			||||||
    onMute: PropTypes.func.isRequired,
 | 
					    onMute: PropTypes.func.isRequired,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _updateEmojis() {
 | 
					  handleMouseEnter = ({ currentTarget }) => {
 | 
				
			||||||
    const node = this.node;
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!node || autoPlayGif) {
 | 
					 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const emojis = node.querySelectorAll('.custom-emoji');
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (var i = 0; i < emojis.length; i++) {
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
      let emoji = emojis[i];
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
      if (emoji.classList.contains('status-emoji')) {
 | 
					      emoji.src = emoji.getAttribute('data-original');
 | 
				
			||||||
        continue;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      emoji.classList.add('status-emoji');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false);
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidMount() {
 | 
					  handleMouseLeave = ({ currentTarget }) => {
 | 
				
			||||||
    this._updateEmojis();
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
 | 
					      emoji.src = emoji.getAttribute('data-static');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidUpdate() {
 | 
					 | 
				
			||||||
    this._updateEmojis();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleEmojiMouseEnter = ({ target }) => {
 | 
					 | 
				
			||||||
    target.src = target.getAttribute('data-original');
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleEmojiMouseLeave = ({ target }) => {
 | 
					 | 
				
			||||||
    target.src = target.getAttribute('data-static');
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleFollow = () => {
 | 
					  handleFollow = () => {
 | 
				
			||||||
    this.props.onFollow(this.props.account);
 | 
					    this.props.onFollow(this.props.account);
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
| 
						 | 
					@ -151,10 +140,6 @@ class AccountCard extends ImmutablePureComponent {
 | 
				
			||||||
    this.props.onMute(this.props.account);
 | 
					    this.props.onMute(this.props.account);
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  setRef = (c) => {
 | 
					 | 
				
			||||||
    this.node = c;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  render() {
 | 
					  render() {
 | 
				
			||||||
    const { account, intl } = this.props;
 | 
					    const { account, intl } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -239,7 +224,7 @@ class AccountCard extends ImmutablePureComponent {
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div className='directory__card__extra' ref={this.setRef}>
 | 
					        <div className='directory__card__extra' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
 | 
				
			||||||
          <div
 | 
					          <div
 | 
				
			||||||
            className='account__header__content translate'
 | 
					            className='account__header__content translate'
 | 
				
			||||||
            dangerouslySetInnerHTML={{ __html: account.get('note_emojified') }}
 | 
					            dangerouslySetInnerHTML={{ __html: account.get('note_emojified') }}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,35 +39,10 @@ class Content extends ImmutablePureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidMount () {
 | 
					  componentDidMount () {
 | 
				
			||||||
    this._updateLinks();
 | 
					    this._updateLinks();
 | 
				
			||||||
    this._updateEmojis();
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidUpdate () {
 | 
					  componentDidUpdate () {
 | 
				
			||||||
    this._updateLinks();
 | 
					    this._updateLinks();
 | 
				
			||||||
    this._updateEmojis();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  _updateEmojis () {
 | 
					 | 
				
			||||||
    const node = this.node;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!node || autoPlayGif) {
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const emojis = node.querySelectorAll('.custom-emoji');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (var i = 0; i < emojis.length; i++) {
 | 
					 | 
				
			||||||
      let emoji = emojis[i];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (emoji.classList.contains('status-emoji')) {
 | 
					 | 
				
			||||||
        continue;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      emoji.classList.add('status-emoji');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false);
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _updateLinks () {
 | 
					  _updateLinks () {
 | 
				
			||||||
| 
						 | 
					@ -132,12 +107,30 @@ class Content extends ImmutablePureComponent {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleEmojiMouseEnter = ({ target }) => {
 | 
					  handleMouseEnter = ({ currentTarget }) => {
 | 
				
			||||||
    target.src = target.getAttribute('data-original');
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
 | 
					      emoji.src = emoji.getAttribute('data-original');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleEmojiMouseLeave = ({ target }) => {
 | 
					  handleMouseLeave = ({ currentTarget }) => {
 | 
				
			||||||
    target.src = target.getAttribute('data-static');
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
 | 
					      emoji.src = emoji.getAttribute('data-static');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
| 
						 | 
					@ -148,6 +141,8 @@ class Content extends ImmutablePureComponent {
 | 
				
			||||||
        className='announcements__item__content translate'
 | 
					        className='announcements__item__content translate'
 | 
				
			||||||
        ref={this.setRef}
 | 
					        ref={this.setRef}
 | 
				
			||||||
        dangerouslySetInnerHTML={{ __html: announcement.get('contentHtml') }}
 | 
					        dangerouslySetInnerHTML={{ __html: announcement.get('contentHtml') }}
 | 
				
			||||||
 | 
					        onMouseEnter={this.handleMouseEnter}
 | 
				
			||||||
 | 
					        onMouseLeave={this.handleMouseLeave}
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,8 @@
 | 
				
			||||||
exports[`<DisplayName /> renders display name + account name 1`] = `
 | 
					exports[`<DisplayName /> renders display name + account name 1`] = `
 | 
				
			||||||
<span
 | 
					<span
 | 
				
			||||||
  className="display-name"
 | 
					  className="display-name"
 | 
				
			||||||
 | 
					  onMouseEnter={[Function]}
 | 
				
			||||||
 | 
					  onMouseLeave={[Function]}
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
  <bdi>
 | 
					  <bdi>
 | 
				
			||||||
    <strong
 | 
					    <strong
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,45 +11,30 @@ export default class DisplayName extends React.PureComponent {
 | 
				
			||||||
    localDomain: PropTypes.string,
 | 
					    localDomain: PropTypes.string,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _updateEmojis () {
 | 
					  handleMouseEnter = ({ currentTarget }) => {
 | 
				
			||||||
    const node = this.node;
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!node || autoPlayGif) {
 | 
					 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const emojis = node.querySelectorAll('.custom-emoji');
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (var i = 0; i < emojis.length; i++) {
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
      let emoji = emojis[i];
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
      if (emoji.classList.contains('status-emoji')) {
 | 
					      emoji.src = emoji.getAttribute('data-original');
 | 
				
			||||||
        continue;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      emoji.classList.add('status-emoji');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false);
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidMount () {
 | 
					  handleMouseLeave = ({ currentTarget }) => {
 | 
				
			||||||
    this._updateEmojis();
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
  }
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidUpdate () {
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
    this._updateEmojis();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleEmojiMouseEnter = ({ target }) => {
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
    target.src = target.getAttribute('data-original');
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
  }
 | 
					      emoji.src = emoji.getAttribute('data-static');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  handleEmojiMouseLeave = ({ target }) => {
 | 
					 | 
				
			||||||
    target.src = target.getAttribute('data-static');
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  setRef = (c) => {
 | 
					 | 
				
			||||||
    this.node = c;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
| 
						 | 
					@ -81,7 +66,7 @@ export default class DisplayName extends React.PureComponent {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <span className='display-name' ref={this.setRef}>
 | 
					      <span className='display-name' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
 | 
				
			||||||
        {displayName} {suffix}
 | 
					        {displayName} {suffix}
 | 
				
			||||||
      </span>
 | 
					      </span>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -75,35 +75,38 @@ export default class StatusContent extends React.PureComponent {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _updateStatusEmojis () {
 | 
					  handleMouseEnter = ({ currentTarget }) => {
 | 
				
			||||||
    const node = this.node;
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!node || autoPlayGif) {
 | 
					 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const emojis = node.querySelectorAll('.custom-emoji');
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (var i = 0; i < emojis.length; i++) {
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
      let emoji = emojis[i];
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
      if (emoji.classList.contains('status-emoji')) {
 | 
					      emoji.src = emoji.getAttribute('data-original');
 | 
				
			||||||
        continue;
 | 
					    }
 | 
				
			||||||
      }
 | 
					  }
 | 
				
			||||||
      emoji.classList.add('status-emoji');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false);
 | 
					  handleMouseLeave = ({ currentTarget }) => {
 | 
				
			||||||
      emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false);
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
 | 
					      emoji.src = emoji.getAttribute('data-static');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidMount () {
 | 
					  componentDidMount () {
 | 
				
			||||||
    this._updateStatusLinks();
 | 
					    this._updateStatusLinks();
 | 
				
			||||||
    this._updateStatusEmojis();
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidUpdate () {
 | 
					  componentDidUpdate () {
 | 
				
			||||||
    this._updateStatusLinks();
 | 
					    this._updateStatusLinks();
 | 
				
			||||||
    this._updateStatusEmojis();
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onMentionClick = (mention, e) => {
 | 
					  onMentionClick = (mention, e) => {
 | 
				
			||||||
| 
						 | 
					@ -122,14 +125,6 @@ export default class StatusContent extends React.PureComponent {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleEmojiMouseEnter = ({ target }) => {
 | 
					 | 
				
			||||||
    target.src = target.getAttribute('data-original');
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleEmojiMouseLeave = ({ target }) => {
 | 
					 | 
				
			||||||
    target.src = target.getAttribute('data-static');
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleMouseDown = (e) => {
 | 
					  handleMouseDown = (e) => {
 | 
				
			||||||
    this.startXY = [e.clientX, e.clientY];
 | 
					    this.startXY = [e.clientX, e.clientY];
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -219,7 +214,7 @@ export default class StatusContent extends React.PureComponent {
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return (
 | 
					      return (
 | 
				
			||||||
        <div className={classNames} ref={this.setRef} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
 | 
					        <div className={classNames} ref={this.setRef} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
 | 
				
			||||||
          <p style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}>
 | 
					          <p style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}>
 | 
				
			||||||
            <span dangerouslySetInnerHTML={spoilerContent} className='translate' />
 | 
					            <span dangerouslySetInnerHTML={spoilerContent} className='translate' />
 | 
				
			||||||
            {' '}
 | 
					            {' '}
 | 
				
			||||||
| 
						 | 
					@ -237,7 +232,7 @@ export default class StatusContent extends React.PureComponent {
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    } else if (this.props.onClick) {
 | 
					    } else if (this.props.onClick) {
 | 
				
			||||||
      const output = [
 | 
					      const output = [
 | 
				
			||||||
        <div className={classNames} ref={this.setRef} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} key='status-content'>
 | 
					        <div className={classNames} ref={this.setRef} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} key='status-content' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
 | 
				
			||||||
          <div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} />
 | 
					          <div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          {!!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
 | 
					          {!!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
 | 
				
			||||||
| 
						 | 
					@ -253,7 +248,7 @@ export default class StatusContent extends React.PureComponent {
 | 
				
			||||||
      return output;
 | 
					      return output;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      return (
 | 
					      return (
 | 
				
			||||||
        <div className={classNames} ref={this.setRef} tabIndex='0'>
 | 
					        <div className={classNames} ref={this.setRef} tabIndex='0' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
 | 
				
			||||||
          <div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} />
 | 
					          <div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          {!!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
 | 
					          {!!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -96,45 +96,30 @@ class Header extends ImmutablePureComponent {
 | 
				
			||||||
    return !location.pathname.match(/\/(followers|following)\/?$/);
 | 
					    return !location.pathname.match(/\/(followers|following)\/?$/);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _updateEmojis () {
 | 
					  handleMouseEnter = ({ currentTarget }) => {
 | 
				
			||||||
    const node = this.node;
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!node || autoPlayGif) {
 | 
					 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const emojis = node.querySelectorAll('.custom-emoji');
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (var i = 0; i < emojis.length; i++) {
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
      let emoji = emojis[i];
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
      if (emoji.classList.contains('status-emoji')) {
 | 
					      emoji.src = emoji.getAttribute('data-original');
 | 
				
			||||||
        continue;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      emoji.classList.add('status-emoji');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false);
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidMount () {
 | 
					  handleMouseLeave = ({ currentTarget }) => {
 | 
				
			||||||
    this._updateEmojis();
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
  }
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidUpdate () {
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
    this._updateEmojis();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleEmojiMouseEnter = ({ target }) => {
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
    target.src = target.getAttribute('data-original');
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
  }
 | 
					      emoji.src = emoji.getAttribute('data-static');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  handleEmojiMouseLeave = ({ target }) => {
 | 
					 | 
				
			||||||
    target.src = target.getAttribute('data-static');
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  setRef = (c) => {
 | 
					 | 
				
			||||||
    this.node = c;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
| 
						 | 
					@ -276,7 +261,7 @@ class Header extends ImmutablePureComponent {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <div className={classNames('account__header', { inactive: !!account.get('moved') })} ref={this.setRef}>
 | 
					      <div className={classNames('account__header', { inactive: !!account.get('moved') })} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
 | 
				
			||||||
        <div className='account__header__image'>
 | 
					        <div className='account__header__image'>
 | 
				
			||||||
          <div className='account__header__info'>
 | 
					          <div className='account__header__info'>
 | 
				
			||||||
            {!suspended && info}
 | 
					            {!suspended && info}
 | 
				
			||||||
| 
						 | 
					@ -330,7 +315,7 @@ class Header extends ImmutablePureComponent {
 | 
				
			||||||
                    <dl key={i}>
 | 
					                    <dl key={i}>
 | 
				
			||||||
                      <dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} className='translate' />
 | 
					                      <dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} className='translate' />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                      <dd className={pair.get('verified_at') && 'verified'} title={pair.get('value_plain')} className='translate'>
 | 
					                      <dd className={`${pair.get('verified_at') ? 'verified' : ''} translate`} title={pair.get('value_plain')}>
 | 
				
			||||||
                        {pair.get('verified_at') && <span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(pair.get('verified_at'), dateFormatOptions) })}><Icon id='check' className='verified__mark' /></span>} <span dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} />
 | 
					                        {pair.get('verified_at') && <span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(pair.get('verified_at'), dateFormatOptions) })}><Icon id='check' className='verified__mark' /></span>} <span dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} />
 | 
				
			||||||
                      </dd>
 | 
					                      </dd>
 | 
				
			||||||
                    </dl>
 | 
					                    </dl>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,41 +44,30 @@ class Conversation extends ImmutablePureComponent {
 | 
				
			||||||
    intl: PropTypes.object.isRequired,
 | 
					    intl: PropTypes.object.isRequired,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _updateEmojis () {
 | 
					  handleMouseEnter = ({ currentTarget }) => {
 | 
				
			||||||
    const node = this.namesNode;
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!node || autoPlayGif) {
 | 
					 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const emojis = node.querySelectorAll('.custom-emoji');
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (var i = 0; i < emojis.length; i++) {
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
      let emoji = emojis[i];
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
      if (emoji.classList.contains('status-emoji')) {
 | 
					      emoji.src = emoji.getAttribute('data-original');
 | 
				
			||||||
        continue;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      emoji.classList.add('status-emoji');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false);
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidMount () {
 | 
					  handleMouseLeave = ({ currentTarget }) => {
 | 
				
			||||||
    this._updateEmojis();
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
  }
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidUpdate () {
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
    this._updateEmojis();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleEmojiMouseEnter = ({ target }) => {
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
    target.src = target.getAttribute('data-original');
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
  }
 | 
					      emoji.src = emoji.getAttribute('data-static');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  handleEmojiMouseLeave = ({ target }) => {
 | 
					 | 
				
			||||||
    target.src = target.getAttribute('data-static');
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleClick = () => {
 | 
					  handleClick = () => {
 | 
				
			||||||
| 
						 | 
					@ -123,10 +112,6 @@ class Conversation extends ImmutablePureComponent {
 | 
				
			||||||
    this.props.onToggleHidden(this.props.lastStatus);
 | 
					    this.props.onToggleHidden(this.props.lastStatus);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  setNamesRef = (c) => {
 | 
					 | 
				
			||||||
    this.namesNode = c;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
    const { accounts, lastStatus, unread, scrollKey, intl } = this.props;
 | 
					    const { accounts, lastStatus, unread, scrollKey, intl } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -171,7 +156,7 @@ class Conversation extends ImmutablePureComponent {
 | 
				
			||||||
                {unread && <span className='conversation__unread' />} <RelativeTimestamp timestamp={lastStatus.get('created_at')} />
 | 
					                {unread && <span className='conversation__unread' />} <RelativeTimestamp timestamp={lastStatus.get('created_at')} />
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              <div className='conversation__content__names' ref={this.setNamesRef}>
 | 
					              <div className='conversation__content__names' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
 | 
				
			||||||
                <FormattedMessage id='conversation.with' defaultMessage='With {names}' values={{ names: <span>{names}</span> }} />
 | 
					                <FormattedMessage id='conversation.with' defaultMessage='With {names}' values={{ names: <span>{names}</span> }} />
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -102,43 +102,32 @@ class AccountCard extends ImmutablePureComponent {
 | 
				
			||||||
    onMute: PropTypes.func.isRequired,
 | 
					    onMute: PropTypes.func.isRequired,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _updateEmojis() {
 | 
					  handleMouseEnter = ({ currentTarget }) => {
 | 
				
			||||||
    const node = this.node;
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!node || autoPlayGif) {
 | 
					 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const emojis = node.querySelectorAll('.custom-emoji');
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (var i = 0; i < emojis.length; i++) {
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
      let emoji = emojis[i];
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
      if (emoji.classList.contains('status-emoji')) {
 | 
					      emoji.src = emoji.getAttribute('data-original');
 | 
				
			||||||
        continue;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      emoji.classList.add('status-emoji');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false);
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidMount() {
 | 
					  handleMouseLeave = ({ currentTarget }) => {
 | 
				
			||||||
    this._updateEmojis();
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
 | 
					      emoji.src = emoji.getAttribute('data-static');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidUpdate() {
 | 
					 | 
				
			||||||
    this._updateEmojis();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleEmojiMouseEnter = ({ target }) => {
 | 
					 | 
				
			||||||
    target.src = target.getAttribute('data-original');
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleEmojiMouseLeave = ({ target }) => {
 | 
					 | 
				
			||||||
    target.src = target.getAttribute('data-static');
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleFollow = () => {
 | 
					  handleFollow = () => {
 | 
				
			||||||
    this.props.onFollow(this.props.account);
 | 
					    this.props.onFollow(this.props.account);
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
| 
						 | 
					@ -151,10 +140,6 @@ class AccountCard extends ImmutablePureComponent {
 | 
				
			||||||
    this.props.onMute(this.props.account);
 | 
					    this.props.onMute(this.props.account);
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  setRef = (c) => {
 | 
					 | 
				
			||||||
    this.node = c;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  render() {
 | 
					  render() {
 | 
				
			||||||
    const { account, intl } = this.props;
 | 
					    const { account, intl } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -239,7 +224,7 @@ class AccountCard extends ImmutablePureComponent {
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div className='directory__card__extra' ref={this.setRef}>
 | 
					        <div className='directory__card__extra' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
 | 
				
			||||||
          <div
 | 
					          <div
 | 
				
			||||||
            className='account__header__content translate'
 | 
					            className='account__header__content translate'
 | 
				
			||||||
            dangerouslySetInnerHTML={{ __html: account.get('note_emojified') }}
 | 
					            dangerouslySetInnerHTML={{ __html: account.get('note_emojified') }}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,35 +39,10 @@ class Content extends ImmutablePureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidMount () {
 | 
					  componentDidMount () {
 | 
				
			||||||
    this._updateLinks();
 | 
					    this._updateLinks();
 | 
				
			||||||
    this._updateEmojis();
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidUpdate () {
 | 
					  componentDidUpdate () {
 | 
				
			||||||
    this._updateLinks();
 | 
					    this._updateLinks();
 | 
				
			||||||
    this._updateEmojis();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  _updateEmojis () {
 | 
					 | 
				
			||||||
    const node = this.node;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!node || autoPlayGif) {
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const emojis = node.querySelectorAll('.custom-emoji');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (var i = 0; i < emojis.length; i++) {
 | 
					 | 
				
			||||||
      let emoji = emojis[i];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (emoji.classList.contains('status-emoji')) {
 | 
					 | 
				
			||||||
        continue;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      emoji.classList.add('status-emoji');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false);
 | 
					 | 
				
			||||||
      emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _updateLinks () {
 | 
					  _updateLinks () {
 | 
				
			||||||
| 
						 | 
					@ -132,12 +107,30 @@ class Content extends ImmutablePureComponent {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleEmojiMouseEnter = ({ target }) => {
 | 
					  handleMouseEnter = ({ currentTarget }) => {
 | 
				
			||||||
    target.src = target.getAttribute('data-original');
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
 | 
					      emoji.src = emoji.getAttribute('data-original');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleEmojiMouseLeave = ({ target }) => {
 | 
					  handleMouseLeave = ({ currentTarget }) => {
 | 
				
			||||||
    target.src = target.getAttribute('data-static');
 | 
					    if (autoPlayGif) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const emojis = currentTarget.querySelectorAll('.custom-emoji');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (var i = 0; i < emojis.length; i++) {
 | 
				
			||||||
 | 
					      let emoji = emojis[i];
 | 
				
			||||||
 | 
					      emoji.src = emoji.getAttribute('data-static');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
| 
						 | 
					@ -148,6 +141,8 @@ class Content extends ImmutablePureComponent {
 | 
				
			||||||
        className='announcements__item__content translate'
 | 
					        className='announcements__item__content translate'
 | 
				
			||||||
        ref={this.setRef}
 | 
					        ref={this.setRef}
 | 
				
			||||||
        dangerouslySetInnerHTML={{ __html: announcement.get('contentHtml') }}
 | 
					        dangerouslySetInnerHTML={{ __html: announcement.get('contentHtml') }}
 | 
				
			||||||
 | 
					        onMouseEnter={this.handleMouseEnter}
 | 
				
			||||||
 | 
					        onMouseLeave={this.handleMouseLeave}
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,9 +4,8 @@ class ActivityPub::Activity::Move < ActivityPub::Activity
 | 
				
			||||||
  PROCESSING_COOLDOWN = 7.days.seconds
 | 
					  PROCESSING_COOLDOWN = 7.days.seconds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def perform
 | 
					  def perform
 | 
				
			||||||
    return if origin_account.uri != object_uri || processed?
 | 
					    return if origin_account.uri != object_uri
 | 
				
			||||||
 | 
					    return unless mark_as_processing!
 | 
				
			||||||
    mark_as_processing!
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    target_account = ActivityPub::FetchRemoteAccountService.new.call(target_uri)
 | 
					    target_account = ActivityPub::FetchRemoteAccountService.new.call(target_uri)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,12 +34,8 @@ class ActivityPub::Activity::Move < ActivityPub::Activity
 | 
				
			||||||
    value_or_id(@json['target'])
 | 
					    value_or_id(@json['target'])
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def processed?
 | 
					 | 
				
			||||||
    redis.exists?("move_in_progress:#{@account.id}")
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def mark_as_processing!
 | 
					  def mark_as_processing!
 | 
				
			||||||
    redis.setex("move_in_progress:#{@account.id}", PROCESSING_COOLDOWN, true)
 | 
					    redis.set("move_in_progress:#{@account.id}", true, nx: true, ex: PROCESSING_COOLDOWN)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def unmark_as_processing!
 | 
					  def unmark_as_processing!
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,8 @@
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AccountMigration < ApplicationRecord
 | 
					class AccountMigration < ApplicationRecord
 | 
				
			||||||
 | 
					  include Redisable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  COOLDOWN_PERIOD = 30.days.freeze
 | 
					  COOLDOWN_PERIOD = 30.days.freeze
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  belongs_to :account
 | 
					  belongs_to :account
 | 
				
			||||||
| 
						 | 
					@ -39,7 +41,13 @@ class AccountMigration < ApplicationRecord
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return false unless errors.empty?
 | 
					    return false unless errors.empty?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    save
 | 
					    RedisLock.acquire(lock_options) do |lock|
 | 
				
			||||||
 | 
					      if lock.acquired?
 | 
				
			||||||
 | 
					        save
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					        raise Mastodon::RaceConditionError
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def cooldown_at
 | 
					  def cooldown_at
 | 
				
			||||||
| 
						 | 
					@ -75,4 +83,8 @@ class AccountMigration < ApplicationRecord
 | 
				
			||||||
  def validate_migration_cooldown
 | 
					  def validate_migration_cooldown
 | 
				
			||||||
    errors.add(:base, I18n.t('migrations.errors.on_cooldown')) if account.migrations.within_cooldown.exists?
 | 
					    errors.add(:base, I18n.t('migrations.errors.on_cooldown')) if account.migrations.within_cooldown.exists?
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def lock_options
 | 
				
			||||||
 | 
					    { redis: redis, key: "account_migration:#{account.id}" }
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,6 @@ class Notification < ApplicationRecord
 | 
				
			||||||
  self.inheritance_column = nil
 | 
					  self.inheritance_column = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  include Paginable
 | 
					  include Paginable
 | 
				
			||||||
  include Cacheable
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  LEGACY_TYPE_CLASS_MAP = {
 | 
					  LEGACY_TYPE_CLASS_MAP = {
 | 
				
			||||||
    'Mention'       => :mention,
 | 
					    'Mention'       => :mention,
 | 
				
			||||||
| 
						 | 
					@ -38,7 +37,13 @@ class Notification < ApplicationRecord
 | 
				
			||||||
    poll
 | 
					    poll
 | 
				
			||||||
  ).freeze
 | 
					  ).freeze
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  STATUS_INCLUDES = [:account, :application, :preloadable_poll, :media_attachments, :tags, active_mentions: :account, reblog: [:account, :application, :preloadable_poll, :media_attachments, :tags, active_mentions: :account]].freeze
 | 
					  TARGET_STATUS_INCLUDES_BY_TYPE = {
 | 
				
			||||||
 | 
					    status: :status,
 | 
				
			||||||
 | 
					    reblog: [status: :reblog],
 | 
				
			||||||
 | 
					    mention: [mention: :status],
 | 
				
			||||||
 | 
					    favourite: [favourite: :status],
 | 
				
			||||||
 | 
					    poll: [poll: :status],
 | 
				
			||||||
 | 
					  }.freeze
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  belongs_to :account, optional: true
 | 
					  belongs_to :account, optional: true
 | 
				
			||||||
  belongs_to :from_account, class_name: 'Account', optional: true
 | 
					  belongs_to :from_account, class_name: 'Account', optional: true
 | 
				
			||||||
| 
						 | 
					@ -65,8 +70,6 @@ class Notification < ApplicationRecord
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  cache_associated :from_account, status: STATUS_INCLUDES, mention: [status: STATUS_INCLUDES], favourite: [:account, status: STATUS_INCLUDES], follow: :account, follow_request: :account, poll: [status: STATUS_INCLUDES]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def type
 | 
					  def type
 | 
				
			||||||
    @type ||= (super || LEGACY_TYPE_CLASS_MAP[activity_type]).to_sym
 | 
					    @type ||= (super || LEGACY_TYPE_CLASS_MAP[activity_type]).to_sym
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
| 
						 | 
					@ -87,21 +90,40 @@ class Notification < ApplicationRecord
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  class << self
 | 
					  class << self
 | 
				
			||||||
    def cache_ids
 | 
					    def preload_cache_collection_target_statuses(notifications, &_block)
 | 
				
			||||||
      select(:id, :updated_at, :activity_type, :activity_id)
 | 
					      notifications.group_by(&:type).each do |type, grouped_notifications|
 | 
				
			||||||
    end
 | 
					        associations = TARGET_STATUS_INCLUDES_BY_TYPE[type]
 | 
				
			||||||
 | 
					        next unless associations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def reload_stale_associations!(cached_items)
 | 
					        # Instead of using the usual `includes`, manually preload each type.
 | 
				
			||||||
      account_ids = (cached_items.map(&:from_account_id) + cached_items.filter_map { |item| item.target_status&.account_id }).uniq
 | 
					        # If polymorphic associations are loaded with the usual `includes`, other types of associations will be loaded more.
 | 
				
			||||||
 | 
					        ActiveRecord::Associations::Preloader.new.preload(grouped_notifications, associations)
 | 
				
			||||||
      return if account_ids.empty?
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      accounts = Account.where(id: account_ids).includes(:account_stat).index_by(&:id)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      cached_items.each do |item|
 | 
					 | 
				
			||||||
        item.from_account = accounts[item.from_account_id]
 | 
					 | 
				
			||||||
        item.target_status.account = accounts[item.target_status.account_id] if item.target_status
 | 
					 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      unique_target_statuses = notifications.map(&:target_status).compact.uniq
 | 
				
			||||||
 | 
					      # Call cache_collection in block
 | 
				
			||||||
 | 
					      cached_statuses_by_id = yield(unique_target_statuses).index_by(&:id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      notifications.each do |notification|
 | 
				
			||||||
 | 
					        next if notification.target_status.nil?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cached_status = cached_statuses_by_id[notification.target_status.id]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case notification.type
 | 
				
			||||||
 | 
					        when :status
 | 
				
			||||||
 | 
					          notification.status = cached_status
 | 
				
			||||||
 | 
					        when :reblog
 | 
				
			||||||
 | 
					          notification.status.reblog = cached_status
 | 
				
			||||||
 | 
					        when :favourite
 | 
				
			||||||
 | 
					          notification.favourite.status = cached_status
 | 
				
			||||||
 | 
					        when :mention
 | 
				
			||||||
 | 
					          notification.mention.status = cached_status
 | 
				
			||||||
 | 
					        when :poll
 | 
				
			||||||
 | 
					          notification.poll.status = cached_status
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      notifications
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -120,7 +120,7 @@ class Status < ApplicationRecord
 | 
				
			||||||
                   :tags,
 | 
					                   :tags,
 | 
				
			||||||
                   :preview_cards,
 | 
					                   :preview_cards,
 | 
				
			||||||
                   :preloadable_poll,
 | 
					                   :preloadable_poll,
 | 
				
			||||||
                   account: :account_stat,
 | 
					                   account: [:account_stat, :user],
 | 
				
			||||||
                   active_mentions: { account: :account_stat },
 | 
					                   active_mentions: { account: :account_stat },
 | 
				
			||||||
                   reblog: [
 | 
					                   reblog: [
 | 
				
			||||||
                     :application,
 | 
					                     :application,
 | 
				
			||||||
| 
						 | 
					@ -130,7 +130,7 @@ class Status < ApplicationRecord
 | 
				
			||||||
                     :conversation,
 | 
					                     :conversation,
 | 
				
			||||||
                     :status_stat,
 | 
					                     :status_stat,
 | 
				
			||||||
                     :preloadable_poll,
 | 
					                     :preloadable_poll,
 | 
				
			||||||
                     account: :account_stat,
 | 
					                     account: [:account_stat, :user],
 | 
				
			||||||
                     active_mentions: { account: :account_stat },
 | 
					                     active_mentions: { account: :account_stat },
 | 
				
			||||||
                   ],
 | 
					                   ],
 | 
				
			||||||
                   thread: { account: :account_stat }
 | 
					                   thread: { account: :account_stat }
 | 
				
			||||||
| 
						 | 
					@ -354,7 +354,7 @@ class Status < ApplicationRecord
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return if account_ids.empty?
 | 
					      return if account_ids.empty?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      accounts = Account.where(id: account_ids).includes(:account_stat).index_by(&:id)
 | 
					      accounts = Account.where(id: account_ids).includes(:account_stat, :user).index_by(&:id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      cached_items.each do |item|
 | 
					      cached_items.each do |item|
 | 
				
			||||||
        item.account = accounts[item.account_id]
 | 
					        item.account = accounts[item.account_id]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
if String.method_defined?(:blank_as?)
 | 
					if String.method_defined?(:blank_as?)
 | 
				
			||||||
  class String
 | 
					  class String
 | 
				
			||||||
    alias_method :blank?, :blank_as?
 | 
					    alias blank? blank_as?
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
# Use this setup block to configure all options available in SimpleForm.
 | 
					# Use this setup block to configure all options available in SimpleForm.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module AppendComponent
 | 
					module AppendComponent
 | 
				
			||||||
  def append(wrapper_options = nil)
 | 
					  def append(_wrapper_options = nil)
 | 
				
			||||||
    @append ||= begin
 | 
					    @append ||= begin
 | 
				
			||||||
      options[:append].to_s.html_safe if options[:append].present?
 | 
					      options[:append].to_s.html_safe if options[:append].present?
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ module AppendComponent
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module RecommendedComponent
 | 
					module RecommendedComponent
 | 
				
			||||||
  def recommended(wrapper_options = nil)
 | 
					  def recommended(_wrapper_options = nil)
 | 
				
			||||||
    return unless options[:recommended]
 | 
					    return unless options[:recommended]
 | 
				
			||||||
    options[:label_text] = ->(raw_label_text, _required_label_text, _label_present) { safe_join([raw_label_text, ' ', content_tag(:span, I18n.t('simple_form.recommended'), class: 'recommended')]) }
 | 
					    options[:label_text] = ->(raw_label_text, _required_label_text, _label_present) { safe_join([raw_label_text, ' ', content_tag(:span, I18n.t('simple_form.recommended'), class: 'recommended')]) }
 | 
				
			||||||
    nil
 | 
					    nil
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -75,7 +75,7 @@ module Twitter
 | 
				
			||||||
    # XMPP or magnet URIs an empty array will be returned.
 | 
					    # XMPP or magnet URIs an empty array will be returned.
 | 
				
			||||||
    #
 | 
					    #
 | 
				
			||||||
    # If a block is given then it will be called for each XMPP URI.
 | 
					    # If a block is given then it will be called for each XMPP URI.
 | 
				
			||||||
    def extract_extra_uris_with_indices(text, options = {}) # :yields: uri, start, end
 | 
					    def extract_extra_uris_with_indices(text, _options = {}) # :yields: uri, start, end
 | 
				
			||||||
      return [] unless text && text.index(":")
 | 
					      return [] unless text && text.index(":")
 | 
				
			||||||
      urls = []
 | 
					      urls = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -83,7 +83,10 @@ en:
 | 
				
			||||||
        invalid_client: Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method.
 | 
					        invalid_client: Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method.
 | 
				
			||||||
        invalid_grant: The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.
 | 
					        invalid_grant: The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.
 | 
				
			||||||
        invalid_redirect_uri: The redirect uri included is not valid.
 | 
					        invalid_redirect_uri: The redirect uri included is not valid.
 | 
				
			||||||
        invalid_request: The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.
 | 
					        invalid_request:
 | 
				
			||||||
 | 
					          missing_param: 'Missing required parameter: %{value}.'
 | 
				
			||||||
 | 
					          request_not_authorized: Request need to be authorized. Required parameter for authorizing request is missing or invalid.
 | 
				
			||||||
 | 
					          unknown: The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.
 | 
				
			||||||
        invalid_resource_owner: The provided resource owner credentials are not valid, or resource owner cannot be found
 | 
					        invalid_resource_owner: The provided resource owner credentials are not valid, or resource owner cannot be found
 | 
				
			||||||
        invalid_scope: The requested scope is invalid, unknown, or malformed.
 | 
					        invalid_scope: The requested scope is invalid, unknown, or malformed.
 | 
				
			||||||
        invalid_token:
 | 
					        invalid_token:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,3 @@
 | 
				
			||||||
# -*- encoding: utf-8 -*-
 | 
					 | 
				
			||||||
# frozen_string_literal: true
 | 
					# frozen_string_literal: true
 | 
				
			||||||
# This file generated automatically from http://w3id.org/identity/v1
 | 
					# This file generated automatically from http://w3id.org/identity/v1
 | 
				
			||||||
require 'json/ld'
 | 
					require 'json/ld'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,3 @@
 | 
				
			||||||
# -*- encoding: utf-8 -*-
 | 
					 | 
				
			||||||
# frozen_string_literal: true
 | 
					# frozen_string_literal: true
 | 
				
			||||||
# This file generated automatically from http://w3id.org/security/v1
 | 
					# This file generated automatically from http://w3id.org/security/v1
 | 
				
			||||||
require 'json/ld'
 | 
					require 'json/ld'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -402,7 +402,7 @@ module Mastodon
 | 
				
			||||||
        exit(1)
 | 
					        exit(1)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      parallelize_with_progress(target_account.followers.local) do |account|
 | 
					      processed, = parallelize_with_progress(target_account.followers.local) do |account|
 | 
				
			||||||
        UnfollowService.new.call(account, target_account)
 | 
					        UnfollowService.new.call(account, target_account)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -113,7 +113,7 @@ module Mastodon
 | 
				
			||||||
        result = entry.destroy
 | 
					        result = entry.destroy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if result
 | 
					        if result
 | 
				
			||||||
          processed += 1 + children_count
 | 
					          processed += children_count + 1
 | 
				
			||||||
        else
 | 
					        else
 | 
				
			||||||
          say("#{domain} could not be unblocked.", :red)
 | 
					          say("#{domain} could not be unblocked.", :red)
 | 
				
			||||||
          failed += 1
 | 
					          failed += 1
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,7 +55,7 @@ module Paperclip
 | 
				
			||||||
      # If we don't have enough colors for accent and foreground, generate
 | 
					      # If we don't have enough colors for accent and foreground, generate
 | 
				
			||||||
      # new ones by manipulating the background color
 | 
					      # new ones by manipulating the background color
 | 
				
			||||||
      (2 - foreground_colors.size).times do |i|
 | 
					      (2 - foreground_colors.size).times do |i|
 | 
				
			||||||
        foreground_colors << lighten_or_darken(background_color, 35 + (15 * i))
 | 
					        foreground_colors << lighten_or_darken(background_color, 35 + (i * 15))
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      # We want the color with the highest contrast to background to be the foreground one,
 | 
					      # We want the color with the highest contrast to background to be the foreground one,
 | 
				
			||||||
| 
						 | 
					@ -147,7 +147,7 @@ module Paperclip
 | 
				
			||||||
        g = l.to_f
 | 
					        g = l.to_f
 | 
				
			||||||
        b = l.to_f # achromatic
 | 
					        b = l.to_f # achromatic
 | 
				
			||||||
      else
 | 
					      else
 | 
				
			||||||
        q = l < 0.5 ? l * (1 + s) : l + s - l * s
 | 
					        q = l < 0.5 ? l * (s + 1) : l + s - l * s
 | 
				
			||||||
        p = 2 * l - q
 | 
					        p = 2 * l - q
 | 
				
			||||||
        r = hue_to_rgb(p, q, h + 1 / 3.0)
 | 
					        r = hue_to_rgb(p, q, h + 1 / 3.0)
 | 
				
			||||||
        g = hue_to_rgb(p, q, h)
 | 
					        g = hue_to_rgb(p, q, h)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -114,7 +114,7 @@
 | 
				
			||||||
    "lodash": "^4.17.19",
 | 
					    "lodash": "^4.17.19",
 | 
				
			||||||
    "mark-loader": "^0.1.6",
 | 
					    "mark-loader": "^0.1.6",
 | 
				
			||||||
    "marky": "^1.2.1",
 | 
					    "marky": "^1.2.1",
 | 
				
			||||||
    "mini-css-extract-plugin": "^1.3.4",
 | 
					    "mini-css-extract-plugin": "^1.3.5",
 | 
				
			||||||
    "mkdirp": "^1.0.4",
 | 
					    "mkdirp": "^1.0.4",
 | 
				
			||||||
    "npmlog": "^4.1.2",
 | 
					    "npmlog": "^4.1.2",
 | 
				
			||||||
    "object-assign": "^4.1.1",
 | 
					    "object-assign": "^4.1.1",
 | 
				
			||||||
| 
						 | 
					@ -168,7 +168,7 @@
 | 
				
			||||||
    "uuid": "^8.3.1",
 | 
					    "uuid": "^8.3.1",
 | 
				
			||||||
    "webpack": "^4.46.0",
 | 
					    "webpack": "^4.46.0",
 | 
				
			||||||
    "webpack-assets-manifest": "^4.0.1",
 | 
					    "webpack-assets-manifest": "^4.0.1",
 | 
				
			||||||
    "webpack-bundle-analyzer": "^4.3.0",
 | 
					    "webpack-bundle-analyzer": "^4.4.0",
 | 
				
			||||||
    "webpack-cli": "^3.3.12",
 | 
					    "webpack-cli": "^3.3.12",
 | 
				
			||||||
    "webpack-merge": "^5.7.3",
 | 
					    "webpack-merge": "^5.7.3",
 | 
				
			||||||
    "wicg-inert": "^3.1.0"
 | 
					    "wicg-inert": "^3.1.0"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -353,10 +353,6 @@ describe ApplicationController, type: :controller do
 | 
				
			||||||
      expect(C.new.cache_collection(raw, Object)).to eq raw
 | 
					      expect(C.new.cache_collection(raw, Object)).to eq raw
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    context 'Notification' do
 | 
					 | 
				
			||||||
      include_examples 'cacheable', :notification, Notification
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    context 'Status' do
 | 
					    context 'Status' do
 | 
				
			||||||
      include_examples 'cacheable', :status, Status
 | 
					      include_examples 'cacheable', :status, Status
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,7 +51,7 @@ describe Settings::MigrationsController do
 | 
				
			||||||
      it_behaves_like 'authenticate user'
 | 
					      it_behaves_like 'authenticate user'
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    context 'when user is sign in' do
 | 
					    context 'when user is signed in' do
 | 
				
			||||||
      subject { post :create, params: { account_migration: { acct: acct, current_password: '12345678' } } }
 | 
					      subject { post :create, params: { account_migration: { acct: acct, current_password: '12345678' } } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      let(:user) { Fabricate(:user, password: '12345678') }
 | 
					      let(:user) { Fabricate(:user, password: '12345678') }
 | 
				
			||||||
| 
						 | 
					@ -67,12 +67,45 @@ describe Settings::MigrationsController do
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      context 'when acct is a current account' do
 | 
					      context 'when acct is the current account' do
 | 
				
			||||||
        let(:acct) { user.account }
 | 
					        let(:acct) { user.account }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        it 'renders show' do
 | 
					        it 'renders show' do
 | 
				
			||||||
          is_expected.to render_template :show
 | 
					          is_expected.to render_template :show
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it 'does not update the moved account' do
 | 
				
			||||||
 | 
					          expect(user.account.reload.moved_to_account_id).to be_nil
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      context 'when target account does not reference the account being moved from' do
 | 
				
			||||||
 | 
					        let(:acct) { Fabricate(:account, also_known_as: []) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it 'renders show' do
 | 
				
			||||||
 | 
					          is_expected.to render_template :show
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it 'does not update the moved account' do
 | 
				
			||||||
 | 
					          expect(user.account.reload.moved_to_account_id).to be_nil
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      context 'when a recent migration already exists ' do
 | 
				
			||||||
 | 
					        let(:acct) { Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)]) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        before do
 | 
				
			||||||
 | 
					          moved_to = Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)])
 | 
				
			||||||
 | 
					          user.account.migrations.create!(acct: moved_to.acct)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it 'renders show' do
 | 
				
			||||||
 | 
					          is_expected.to render_template :show
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it 'does not update the moved account' do
 | 
				
			||||||
 | 
					          expect(user.account.reload.moved_to_account_id).to be_nil
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,23 +1,11 @@
 | 
				
			||||||
require 'rails_helper'
 | 
					require 'rails_helper'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RSpec.describe ActivityPub::Activity::Move do
 | 
					RSpec.describe ActivityPub::Activity::Move do
 | 
				
			||||||
  let(:follower)    { Fabricate(:account) }
 | 
					  let(:follower)         { Fabricate(:account) }
 | 
				
			||||||
  let(:old_account) { Fabricate(:account) }
 | 
					  let(:old_account)      { Fabricate(:account, uri: 'https://example.org/alice', domain: 'example.org', protocol: :activitypub, inbox_url: 'https://example.org/inbox') }
 | 
				
			||||||
  let(:new_account) { Fabricate(:account) }
 | 
					  let(:new_account)      { Fabricate(:account, uri: 'https://example.com/alice', domain: 'example.com', protocol: :activitypub, inbox_url: 'https://example.com/inbox', also_known_as: also_known_as) }
 | 
				
			||||||
 | 
					  let(:also_known_as)    { [old_account.uri] }
 | 
				
			||||||
  before do
 | 
					  let(:returned_account) { new_account }
 | 
				
			||||||
    follower.follow!(old_account)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    old_account.update!(uri: 'https://example.org/alice', domain: 'example.org', protocol: :activitypub, inbox_url: 'https://example.org/inbox')
 | 
					 | 
				
			||||||
    new_account.update!(uri: 'https://example.com/alice', domain: 'example.com', protocol: :activitypub, inbox_url: 'https://example.com/inbox', also_known_as: [old_account.uri])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    stub_request(:post, 'https://example.org/inbox').to_return(status: 200)
 | 
					 | 
				
			||||||
    stub_request(:post, 'https://example.com/inbox').to_return(status: 200)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    service_stub = double
 | 
					 | 
				
			||||||
    allow(ActivityPub::FetchRemoteAccountService).to receive(:new).and_return(service_stub)
 | 
					 | 
				
			||||||
    allow(service_stub).to receive(:call).and_return(new_account)
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let(:json) do
 | 
					  let(:json) do
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -30,6 +18,17 @@ RSpec.describe ActivityPub::Activity::Move do
 | 
				
			||||||
    }.with_indifferent_access
 | 
					    }.with_indifferent_access
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  before do
 | 
				
			||||||
 | 
					    follower.follow!(old_account)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    stub_request(:post, old_account.inbox_url).to_return(status: 200)
 | 
				
			||||||
 | 
					    stub_request(:post, new_account.inbox_url).to_return(status: 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    service_stub = double
 | 
				
			||||||
 | 
					    allow(ActivityPub::FetchRemoteAccountService).to receive(:new).and_return(service_stub)
 | 
				
			||||||
 | 
					    allow(service_stub).to receive(:call).and_return(returned_account)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe '#perform' do
 | 
					  describe '#perform' do
 | 
				
			||||||
    subject { described_class.new(json, old_account) }
 | 
					    subject { described_class.new(json, old_account) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,16 +36,70 @@ RSpec.describe ActivityPub::Activity::Move do
 | 
				
			||||||
      subject.perform
 | 
					      subject.perform
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it 'sets moved account on old account' do
 | 
					    context 'when all conditions are met' do
 | 
				
			||||||
      expect(old_account.reload.moved_to_account_id).to eq new_account.id
 | 
					      it 'sets moved account on old account' do
 | 
				
			||||||
 | 
					        expect(old_account.reload.moved_to_account_id).to eq new_account.id
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'makes followers unfollow old account' do
 | 
				
			||||||
 | 
					        expect(follower.following?(old_account)).to be false
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'makes followers follow-request the new account' do
 | 
				
			||||||
 | 
					        expect(follower.requested?(new_account)).to be true
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it 'makes followers unfollow old account' do
 | 
					    context "when the new account can't be resolved" do
 | 
				
			||||||
      expect(follower.following?(old_account)).to be false
 | 
					      let(:returned_account) { nil }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'does not set moved account on old account' do
 | 
				
			||||||
 | 
					        expect(old_account.reload.moved_to_account_id).to be_nil
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'does not make followers unfollow old account' do
 | 
				
			||||||
 | 
					        expect(follower.following?(old_account)).to be true
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'does not make followers follow-request the new account' do
 | 
				
			||||||
 | 
					        expect(follower.requested?(new_account)).to be false
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it 'makes followers follow-request the new account' do
 | 
					    context 'when the new account does not references the old account' do
 | 
				
			||||||
      expect(follower.requested?(new_account)).to be true
 | 
					      let(:also_known_as) { [] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'does not set moved account on old account' do
 | 
				
			||||||
 | 
					        expect(old_account.reload.moved_to_account_id).to be_nil
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'does not make followers unfollow old account' do
 | 
				
			||||||
 | 
					        expect(follower.following?(old_account)).to be true
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'does not make followers follow-request the new account' do
 | 
				
			||||||
 | 
					        expect(follower.requested?(new_account)).to be false
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context 'when a Move has been recently processed' do
 | 
				
			||||||
 | 
					      around do |example|
 | 
				
			||||||
 | 
					        Redis.current.set("move_in_progress:#{old_account.id}", true, nx: true, ex: 7.days.seconds)
 | 
				
			||||||
 | 
					        example.run
 | 
				
			||||||
 | 
					        Redis.current.del("move_in_progress:#{old_account.id}")
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'does not set moved account on old account' do
 | 
				
			||||||
 | 
					        expect(old_account.reload.moved_to_account_id).to be_nil
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'does not make followers unfollow old account' do
 | 
				
			||||||
 | 
					        expect(follower.following?(old_account)).to be true
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'does not make followers follow-request the new account' do
 | 
				
			||||||
 | 
					        expect(follower.requested?(new_account)).to be false
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -81,6 +81,6 @@ RSpec.describe ActivityPub::LinkedDataSignature do
 | 
				
			||||||
    options_hash   = Digest::SHA256.hexdigest(canonicalize(options.merge('@context' => ActivityPub::LinkedDataSignature::CONTEXT)))
 | 
					    options_hash   = Digest::SHA256.hexdigest(canonicalize(options.merge('@context' => ActivityPub::LinkedDataSignature::CONTEXT)))
 | 
				
			||||||
    document_hash  = Digest::SHA256.hexdigest(canonicalize(document))
 | 
					    document_hash  = Digest::SHA256.hexdigest(canonicalize(document))
 | 
				
			||||||
    to_be_verified = options_hash + document_hash
 | 
					    to_be_verified = options_hash + document_hash
 | 
				
			||||||
    Base64.strict_encode64(from_account.keypair.sign(OpenSSL::Digest::SHA256.new, to_be_verified))
 | 
					    Base64.strict_encode64(from_account.keypair.sign(OpenSSL::Digest.new('SHA256'), to_be_verified))
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,47 +56,114 @@ RSpec.describe Notification, type: :model do
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe '.reload_stale_associations!' do
 | 
					  describe '.preload_cache_collection_target_statuses' do
 | 
				
			||||||
    context 'account_ids are empty' do
 | 
					    subject do
 | 
				
			||||||
      let(:cached_items) { [] }
 | 
					      described_class.preload_cache_collection_target_statuses(notifications) do |target_statuses|
 | 
				
			||||||
 | 
					        # preload account for testing instead of using cache_collection
 | 
				
			||||||
      subject { described_class.reload_stale_associations!(cached_items) }
 | 
					        Status.preload(:account).where(id: target_statuses.map(&:id))
 | 
				
			||||||
 | 
					 | 
				
			||||||
      it 'returns nil' do
 | 
					 | 
				
			||||||
        is_expected.to be nil
 | 
					 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    context 'account_ids are present' do
 | 
					    context 'notifications are empty' do
 | 
				
			||||||
 | 
					      let(:notifications) { [] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'returns []' do
 | 
				
			||||||
 | 
					        is_expected.to eq []
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context 'notifications are present' do
 | 
				
			||||||
      before do
 | 
					      before do
 | 
				
			||||||
        allow(accounts_with_ids).to receive(:[]).with(stale_account1.id).and_return(account1)
 | 
					        notifications.each(&:reload)
 | 
				
			||||||
        allow(accounts_with_ids).to receive(:[]).with(stale_account2.id).and_return(account2)
 | 
					 | 
				
			||||||
        allow(Account).to receive_message_chain(:where, :includes, :index_by).and_return(accounts_with_ids)
 | 
					 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      let(:cached_items) do
 | 
					      let(:mention) { Fabricate(:mention) }
 | 
				
			||||||
 | 
					      let(:status) { Fabricate(:status) }
 | 
				
			||||||
 | 
					      let(:reblog) { Fabricate(:status, reblog: Fabricate(:status)) }
 | 
				
			||||||
 | 
					      let(:follow) { Fabricate(:follow) }
 | 
				
			||||||
 | 
					      let(:follow_request) { Fabricate(:follow_request) }
 | 
				
			||||||
 | 
					      let(:favourite) { Fabricate(:favourite) }
 | 
				
			||||||
 | 
					      let(:poll) { Fabricate(:poll) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let(:notifications) do
 | 
				
			||||||
        [
 | 
					        [
 | 
				
			||||||
          Fabricate(:notification, activity: Fabricate(:status)),
 | 
					          Fabricate(:notification, type: :mention, activity: mention),
 | 
				
			||||||
          Fabricate(:notification, activity: Fabricate(:follow)),
 | 
					          Fabricate(:notification, type: :status, activity: status),
 | 
				
			||||||
 | 
					          Fabricate(:notification, type: :reblog, activity: reblog),
 | 
				
			||||||
 | 
					          Fabricate(:notification, type: :follow, activity: follow),
 | 
				
			||||||
 | 
					          Fabricate(:notification, type: :follow_request, activity: follow_request),
 | 
				
			||||||
 | 
					          Fabricate(:notification, type: :favourite, activity: favourite),
 | 
				
			||||||
 | 
					          Fabricate(:notification, type: :poll, activity: poll),
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      let(:stale_account1) { cached_items[0].from_account }
 | 
					      it 'preloads target status' do
 | 
				
			||||||
      let(:stale_account2) { cached_items[1].from_account }
 | 
					        # mention
 | 
				
			||||||
 | 
					        expect(subject[0].type).to eq :mention
 | 
				
			||||||
 | 
					        expect(subject[0].association(:mention)).to be_loaded
 | 
				
			||||||
 | 
					        expect(subject[0].mention.association(:status)).to be_loaded
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      let(:account1) { Fabricate(:account) }
 | 
					        # status
 | 
				
			||||||
      let(:account2) { Fabricate(:account) }
 | 
					        expect(subject[1].type).to eq :status
 | 
				
			||||||
 | 
					        expect(subject[1].association(:status)).to be_loaded
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      let(:accounts_with_ids) { { account1.id => account1, account2.id => account2 } }
 | 
					        # reblog
 | 
				
			||||||
 | 
					        expect(subject[2].type).to eq :reblog
 | 
				
			||||||
 | 
					        expect(subject[2].association(:status)).to be_loaded
 | 
				
			||||||
 | 
					        expect(subject[2].status.association(:reblog)).to be_loaded
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it 'reloads associations' do
 | 
					        # follow: nothing
 | 
				
			||||||
        expect(cached_items[0].from_account).to be stale_account1
 | 
					        expect(subject[3].type).to eq :follow
 | 
				
			||||||
        expect(cached_items[1].from_account).to be stale_account2
 | 
					        expect(subject[3].target_status).to be_nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        described_class.reload_stale_associations!(cached_items)
 | 
					        # follow_request: nothing
 | 
				
			||||||
 | 
					        expect(subject[4].type).to eq :follow_request
 | 
				
			||||||
 | 
					        expect(subject[4].target_status).to be_nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        expect(cached_items[0].from_account).to be account1
 | 
					        # favourite
 | 
				
			||||||
        expect(cached_items[1].from_account).to be account2
 | 
					        expect(subject[5].type).to eq :favourite
 | 
				
			||||||
 | 
					        expect(subject[5].association(:favourite)).to be_loaded
 | 
				
			||||||
 | 
					        expect(subject[5].favourite.association(:status)).to be_loaded
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # poll
 | 
				
			||||||
 | 
					        expect(subject[6].type).to eq :poll
 | 
				
			||||||
 | 
					        expect(subject[6].association(:poll)).to be_loaded
 | 
				
			||||||
 | 
					        expect(subject[6].poll.association(:status)).to be_loaded
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'replaces to cached status' do
 | 
				
			||||||
 | 
					        # mention
 | 
				
			||||||
 | 
					        expect(subject[0].type).to eq :mention
 | 
				
			||||||
 | 
					        expect(subject[0].target_status.association(:account)).to be_loaded
 | 
				
			||||||
 | 
					        expect(subject[0].target_status).to eq mention.status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # status
 | 
				
			||||||
 | 
					        expect(subject[1].type).to eq :status
 | 
				
			||||||
 | 
					        expect(subject[1].target_status.association(:account)).to be_loaded
 | 
				
			||||||
 | 
					        expect(subject[1].target_status).to eq status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # reblog
 | 
				
			||||||
 | 
					        expect(subject[2].type).to eq :reblog
 | 
				
			||||||
 | 
					        expect(subject[2].target_status.association(:account)).to be_loaded
 | 
				
			||||||
 | 
					        expect(subject[2].target_status).to eq reblog.reblog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # follow: nothing
 | 
				
			||||||
 | 
					        expect(subject[3].type).to eq :follow
 | 
				
			||||||
 | 
					        expect(subject[3].target_status).to be_nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # follow_request: nothing
 | 
				
			||||||
 | 
					        expect(subject[4].type).to eq :follow_request
 | 
				
			||||||
 | 
					        expect(subject[4].target_status).to be_nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # favourite
 | 
				
			||||||
 | 
					        expect(subject[5].type).to eq :favourite
 | 
				
			||||||
 | 
					        expect(subject[5].target_status.association(:account)).to be_loaded
 | 
				
			||||||
 | 
					        expect(subject[5].target_status).to eq favourite.status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # poll
 | 
				
			||||||
 | 
					        expect(subject[6].type).to eq :poll
 | 
				
			||||||
 | 
					        expect(subject[6].target_status.association(:account)).to be_loaded
 | 
				
			||||||
 | 
					        expect(subject[6].target_status).to eq poll.status
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								yarn.lock
								
								
								
								
							
							
						
						
									
										16
									
								
								yarn.lock
								
								
								
								
							| 
						 | 
					@ -7115,10 +7115,10 @@ min-indent@^1.0.0:
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
 | 
					  resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
 | 
				
			||||||
  integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
 | 
					  integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mini-css-extract-plugin@^1.3.4:
 | 
					mini-css-extract-plugin@^1.3.5:
 | 
				
			||||||
  version "1.3.4"
 | 
					  version "1.3.5"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.4.tgz#706e69632cdcdb8b15bf8e638442a0dba304a9c8"
 | 
					  resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.5.tgz#252166e78879c106e0130f229d44e0cbdfcebed3"
 | 
				
			||||||
  integrity sha512-dNjqyeogUd8ucUgw5sxm1ahvSfSUgef7smbmATRSbDm4EmNx5kQA6VdUEhEeCKSjX6CTYjb5vxgMUvRjqP3uHg==
 | 
					  integrity sha512-tvmzcwqJJXau4OQE5vT72pRT18o2zF+tQJp8CWchqvfQnTlflkzS+dANYcRdyPRWUWRkfmeNTKltx0NZI/b5dQ==
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    loader-utils "^2.0.0"
 | 
					    loader-utils "^2.0.0"
 | 
				
			||||||
    schema-utils "^3.0.0"
 | 
					    schema-utils "^3.0.0"
 | 
				
			||||||
| 
						 | 
					@ -11079,10 +11079,10 @@ webpack-assets-manifest@^4.0.1:
 | 
				
			||||||
    tapable "^1.0"
 | 
					    tapable "^1.0"
 | 
				
			||||||
    webpack-sources "^1.0"
 | 
					    webpack-sources "^1.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
webpack-bundle-analyzer@^4.3.0:
 | 
					webpack-bundle-analyzer@^4.4.0:
 | 
				
			||||||
  version "4.3.0"
 | 
					  version "4.4.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.3.0.tgz#2f3c0ca9041d5ee47fa418693cf56b4a518b578b"
 | 
					  resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.4.0.tgz#74013106e7e2b07cbd64f3a5ae847f7e814802c7"
 | 
				
			||||||
  integrity sha512-J3TPm54bPARx6QG8z4cKBszahnUglcv70+N+8gUqv2I5KOFHJbzBiLx+pAp606so0X004fxM7hqRu10MLjJifA==
 | 
					  integrity sha512-9DhNa+aXpqdHk8LkLPTBU/dMfl84Y+WE2+KnfI6rSpNRNVKa0VGLjPd2pjFubDeqnWmulFggxmWBxhfJXZnR0g==
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    acorn "^8.0.4"
 | 
					    acorn "^8.0.4"
 | 
				
			||||||
    acorn-walk "^8.0.0"
 | 
					    acorn-walk "^8.0.0"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue