Compare commits

..

No commits in common. "6c28810b39ec55083db9f8f98d8a9c317380acbf" and "4f37b4873f4c6b60534a233dad4ee2b72cc46244" have entirely different histories.

223 changed files with 911 additions and 2281 deletions

View file

@ -4,8 +4,7 @@ NODE_ENV=production
LOCAL_DOMAIN=cb6e6126.ngrok.io LOCAL_DOMAIN=cb6e6126.ngrok.io
LOCAL_HTTPS=true LOCAL_HTTPS=true
# Secret values required by ActiveRecord encryption feature # Required by ActiveRecord encryption feature
# Use `bin/rails db:encryption:init` to generate fresh secrets ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=fkSxKD2bF396kdQbrP1EJ7WbU7ZgNokR
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=test_determinist_key_DO_NOT_USE_IN_PRODUCTION ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=r0hvVmzBVsjxC7AMlwhOzmtc36ZCOS1E
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=test_salt_DO_NOT_USE_IN_PRODUCTION ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=PhdFyyfy5xJ7WVd2lWBpcPScRQHzRTNr
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=test_primary_key_DO_NOT_USE_IN_PRODUCTION

View file

@ -264,8 +264,8 @@ jobs:
ports: ports:
- 6379:6379 - 6379:6379
elasticsearch: search:
image: ${{ contains(matrix.search-image, 'elasticsearch') && matrix.search-image || '' }} image: ${{ matrix.search-image }}
env: env:
discovery.type: single-node discovery.type: single-node
xpack.security.enabled: false xpack.security.enabled: false
@ -277,20 +277,6 @@ jobs:
ports: ports:
- 9200:9200 - 9200:9200
opensearch:
image: ${{ contains(matrix.search-image, 'opensearch') && matrix.search-image || '' }}
env:
discovery.type: single-node
DISABLE_INSTALL_DEMO_CONFIG: true
DISABLE_SECURITY_PLUGIN: true
options: >-
--health-cmd "curl http://localhost:9200/_cluster/health"
--health-interval 10s
--health-timeout 5s
--health-retries 10
ports:
- 9200:9200
env: env:
DB_HOST: localhost DB_HOST: localhost
DB_USER: postgres DB_USER: postgres
@ -314,8 +300,6 @@ jobs:
include: include:
- ruby-version: '.ruby-version' - ruby-version: '.ruby-version'
search-image: docker.elastic.co/elasticsearch/elasticsearch:8.10.2 search-image: docker.elastic.co/elasticsearch/elasticsearch:8.10.2
- ruby-version: '.ruby-version'
search-image: opensearchproject/opensearch:2
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

View file

@ -211,11 +211,6 @@ Style/PercentLiteralDelimiters:
Style/RedundantBegin: Style/RedundantBegin:
Enabled: false Enabled: false
# Reason: Prevailing style choice
# https://docs.rubocop.org/rubocop/cops_style.html#styleredundantfetchblock
Style/RedundantFetchBlock:
Enabled: false
# Reason: Overridden to reduce implicit StandardError rescues # Reason: Overridden to reduce implicit StandardError rescues
# https://docs.rubocop.org/rubocop/cops_style.html#stylerescuestandarderror # https://docs.rubocop.org/rubocop/cops_style.html#stylerescuestandarderror
Style/RescueStandardError: Style/RescueStandardError:

View file

@ -1,6 +1,6 @@
# This configuration was generated by # This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp` # `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
# using RuboCop version 1.63.5. # using RuboCop version 1.62.1.
# The point is for the user to remove these configuration records # The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base. # one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new # Note that changes in the inspected code, or installation of new
@ -122,6 +122,13 @@ Style/HashTransformValues:
- 'app/serializers/rest/web_push_subscription_serializer.rb' - 'app/serializers/rest/web_push_subscription_serializer.rb'
- 'app/services/import_service.rb' - 'app/services/import_service.rb'
# This cop supports safe autocorrection (--autocorrect).
Style/IfUnlessModifier:
Exclude:
- 'config/environments/production.rb'
- 'config/initializers/devise.rb'
- 'config/initializers/ffmpeg.rb'
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
Style/MapToHash: Style/MapToHash:
Exclude: Exclude:
@ -169,6 +176,16 @@ Style/RedundantConstantBase:
- 'config/environments/production.rb' - 'config/environments/production.rb'
- 'config/initializers/sidekiq.rb' - 'config/initializers/sidekiq.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: SafeForConstants.
Style/RedundantFetchBlock:
Exclude:
- 'config/initializers/1_hosts.rb'
- 'config/initializers/chewy.rb'
- 'config/initializers/devise.rb'
- 'config/initializers/paperclip.rb'
- 'config/puma.rb'
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
# AllowedMethods: present?, blank?, presence, try, try! # AllowedMethods: present?, blank?, presence, try, try!

22
.simplecov Normal file
View file

@ -0,0 +1,22 @@
# frozen_string_literal: true
if ENV['CI']
require 'simplecov-lcov'
SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter
else
SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
end
SimpleCov.start 'rails' do
enable_coverage :branch
add_filter 'lib/linter'
add_group 'Libraries', 'lib'
add_group 'Policies', 'app/policies'
add_group 'Presenters', 'app/presenters'
add_group 'Serializers', 'app/serializers'
add_group 'Services', 'app/services'
add_group 'Validators', 'app/validators'
end

View file

@ -28,7 +28,7 @@ gem 'active_model_serializers', '~> 0.10'
gem 'addressable', '~> 2.8' gem 'addressable', '~> 2.8'
gem 'bootsnap', '~> 1.18.0', require: false gem 'bootsnap', '~> 1.18.0', require: false
gem 'browser' gem 'browser'
gem 'charlock_holmes', github: 'TheEssem/charlock_holmes', ref: '226932af4b03eb60d2e31d58b6c3efd72a3ace68' gem 'charlock_holmes', '~> 0.7.7'
gem 'chewy', '~> 7.3' gem 'chewy', '~> 7.3'
gem 'devise', '~> 4.9' gem 'devise', '~> 4.9'
gem 'devise-two-factor' gem 'devise-two-factor'
@ -132,7 +132,7 @@ group :test do
gem 'email_spec' gem 'email_spec'
# Extra RSpec extension methods and helpers for sidekiq # Extra RSpec extension methods and helpers for sidekiq
gem 'rspec-sidekiq', '~> 5.0' gem 'rspec-sidekiq', '~> 4.0'
# Browser integration testing # Browser integration testing
gem 'capybara', '~> 3.39' gem 'capybara', '~> 3.39'
@ -178,7 +178,7 @@ group :development do
# Preview mail in the browser # Preview mail in the browser
gem 'letter_opener', '~> 1.8' gem 'letter_opener', '~> 1.8'
gem 'letter_opener_web', '~> 3.0' gem 'letter_opener_web', '~> 2.0'
# Security analysis CLI tools # Security analysis CLI tools
gem 'brakeman', '~> 6.0', require: false gem 'brakeman', '~> 6.0', require: false

View file

@ -7,45 +7,38 @@ GIT
hkdf (~> 0.2) hkdf (~> 0.2)
jwt (~> 2.0) jwt (~> 2.0)
GIT
remote: https://github.com/TheEssem/charlock_holmes.git
revision: 226932af4b03eb60d2e31d58b6c3efd72a3ace68
ref: 226932af4b03eb60d2e31d58b6c3efd72a3ace68
specs:
charlock_holmes (0.7.7)
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (7.1.3.3) actioncable (7.1.3.2)
actionpack (= 7.1.3.3) actionpack (= 7.1.3.2)
activesupport (= 7.1.3.3) activesupport (= 7.1.3.2)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6) zeitwerk (~> 2.6)
actionmailbox (7.1.3.3) actionmailbox (7.1.3.2)
actionpack (= 7.1.3.3) actionpack (= 7.1.3.2)
activejob (= 7.1.3.3) activejob (= 7.1.3.2)
activerecord (= 7.1.3.3) activerecord (= 7.1.3.2)
activestorage (= 7.1.3.3) activestorage (= 7.1.3.2)
activesupport (= 7.1.3.3) activesupport (= 7.1.3.2)
mail (>= 2.7.1) mail (>= 2.7.1)
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
actionmailer (7.1.3.3) actionmailer (7.1.3.2)
actionpack (= 7.1.3.3) actionpack (= 7.1.3.2)
actionview (= 7.1.3.3) actionview (= 7.1.3.2)
activejob (= 7.1.3.3) activejob (= 7.1.3.2)
activesupport (= 7.1.3.3) activesupport (= 7.1.3.2)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
actionpack (7.1.3.3) actionpack (7.1.3.2)
actionview (= 7.1.3.3) actionview (= 7.1.3.2)
activesupport (= 7.1.3.3) activesupport (= 7.1.3.2)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
racc racc
rack (>= 2.2.4) rack (>= 2.2.4)
@ -53,15 +46,15 @@ GEM
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6) rails-html-sanitizer (~> 1.6)
actiontext (7.1.3.3) actiontext (7.1.3.2)
actionpack (= 7.1.3.3) actionpack (= 7.1.3.2)
activerecord (= 7.1.3.3) activerecord (= 7.1.3.2)
activestorage (= 7.1.3.3) activestorage (= 7.1.3.2)
activesupport (= 7.1.3.3) activesupport (= 7.1.3.2)
globalid (>= 0.6.0) globalid (>= 0.6.0)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
actionview (7.1.3.3) actionview (7.1.3.2)
activesupport (= 7.1.3.3) activesupport (= 7.1.3.2)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.11) erubi (~> 1.11)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
@ -71,22 +64,22 @@ GEM
activemodel (>= 4.1) activemodel (>= 4.1)
case_transform (>= 0.2) case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3) jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
activejob (7.1.3.3) activejob (7.1.3.2)
activesupport (= 7.1.3.3) activesupport (= 7.1.3.2)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (7.1.3.3) activemodel (7.1.3.2)
activesupport (= 7.1.3.3) activesupport (= 7.1.3.2)
activerecord (7.1.3.3) activerecord (7.1.3.2)
activemodel (= 7.1.3.3) activemodel (= 7.1.3.2)
activesupport (= 7.1.3.3) activesupport (= 7.1.3.2)
timeout (>= 0.4.0) timeout (>= 0.4.0)
activestorage (7.1.3.3) activestorage (7.1.3.2)
actionpack (= 7.1.3.3) actionpack (= 7.1.3.2)
activejob (= 7.1.3.3) activejob (= 7.1.3.2)
activerecord (= 7.1.3.3) activerecord (= 7.1.3.2)
activesupport (= 7.1.3.3) activesupport (= 7.1.3.2)
marcel (~> 1.0) marcel (~> 1.0)
activesupport (7.1.3.3) activesupport (7.1.3.2)
base64 base64
bigdecimal bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
@ -107,16 +100,16 @@ GEM
attr_required (1.0.2) attr_required (1.0.2)
awrence (1.2.1) awrence (1.2.1)
aws-eventstream (1.3.0) aws-eventstream (1.3.0)
aws-partitions (1.929.0) aws-partitions (1.922.0)
aws-sdk-core (3.196.1) aws-sdk-core (3.194.1)
aws-eventstream (~> 1, >= 1.3.0) aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.651.0) aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.8) aws-sigv4 (~> 1.8)
jmespath (~> 1, >= 1.6.1) jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.81.0) aws-sdk-kms (1.80.0)
aws-sdk-core (~> 3, >= 3.193.0) aws-sdk-core (~> 3, >= 3.193.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.151.0) aws-sdk-s3 (1.149.1)
aws-sdk-core (~> 3, >= 3.194.0) aws-sdk-core (~> 3, >= 3.194.0)
aws-sdk-kms (~> 1) aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.8) aws-sigv4 (~> 1.8)
@ -166,6 +159,7 @@ GEM
case_transform (0.2) case_transform (0.2)
activesupport activesupport
cbor (0.5.9.8) cbor (0.5.9.8)
charlock_holmes (0.7.7)
chewy (7.6.0) chewy (7.6.0)
activesupport (>= 5.2) activesupport (>= 5.2)
elasticsearch (>= 7.14.0, < 8) elasticsearch (>= 7.14.0, < 8)
@ -278,7 +272,7 @@ GEM
fog-json (1.2.0) fog-json (1.2.0)
fog-core fog-core
multi_json (~> 1.10) multi_json (~> 1.10)
fog-openstack (1.1.1) fog-openstack (1.1.0)
fog-core (~> 2.1) fog-core (~> 2.1)
fog-json (>= 1.0) fog-json (>= 1.0)
formatador (1.1.0) formatador (1.1.0)
@ -395,10 +389,10 @@ GEM
addressable (~> 2.8) addressable (~> 2.8)
letter_opener (1.10.0) letter_opener (1.10.0)
launchy (>= 2.2, < 4) launchy (>= 2.2, < 4)
letter_opener_web (3.0.0) letter_opener_web (2.0.0)
actionmailer (>= 6.1) actionmailer (>= 5.2)
letter_opener (~> 1.9) letter_opener (~> 1.7)
railties (>= 6.1) railties (>= 5.2)
rexml rexml
link_header (0.0.8) link_header (0.0.8)
llhttp-ffi (0.5.0) llhttp-ffi (0.5.0)
@ -428,7 +422,7 @@ GEM
memory_profiler (1.0.1) memory_profiler (1.0.1)
mime-types (3.5.2) mime-types (3.5.2)
mime-types-data (~> 3.2015) mime-types-data (~> 3.2015)
mime-types-data (3.2024.0507) mime-types-data (3.2024.0305)
mini_mime (1.1.5) mini_mime (1.1.5)
mini_portile2 (2.8.6) mini_portile2 (2.8.6)
minitest (5.22.3) minitest (5.22.3)
@ -440,7 +434,7 @@ GEM
uri uri
net-http-persistent (4.0.2) net-http-persistent (4.0.2)
connection_pool (~> 2.2) connection_pool (~> 2.2)
net-imap (0.4.11) net-imap (0.4.10)
date date
net-protocol net-protocol
net-ldap (0.19.0) net-ldap (0.19.0)
@ -450,8 +444,8 @@ GEM
timeout timeout
net-smtp (0.5.0) net-smtp (0.5.0)
net-protocol net-protocol
nio4r (2.7.3) nio4r (2.7.1)
nokogiri (1.16.5) nokogiri (1.16.4)
mini_portile2 (~> 2.8.2) mini_portile2 (~> 2.8.2)
racc (~> 1.4) racc (~> 1.4)
nsa (0.3.0) nsa (0.3.0)
@ -549,7 +543,7 @@ GEM
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-common (~> 0.20.0) opentelemetry-common (~> 0.20.0)
opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-pg (0.27.3) opentelemetry-instrumentation-pg (0.27.2)
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-helpers-sql-obfuscation opentelemetry-helpers-sql-obfuscation
opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-base (~> 0.22.1)
@ -640,20 +634,20 @@ GEM
rackup (1.0.0) rackup (1.0.0)
rack (< 3) rack (< 3)
webrick webrick
rails (7.1.3.3) rails (7.1.3.2)
actioncable (= 7.1.3.3) actioncable (= 7.1.3.2)
actionmailbox (= 7.1.3.3) actionmailbox (= 7.1.3.2)
actionmailer (= 7.1.3.3) actionmailer (= 7.1.3.2)
actionpack (= 7.1.3.3) actionpack (= 7.1.3.2)
actiontext (= 7.1.3.3) actiontext (= 7.1.3.2)
actionview (= 7.1.3.3) actionview (= 7.1.3.2)
activejob (= 7.1.3.3) activejob (= 7.1.3.2)
activemodel (= 7.1.3.3) activemodel (= 7.1.3.2)
activerecord (= 7.1.3.3) activerecord (= 7.1.3.2)
activestorage (= 7.1.3.3) activestorage (= 7.1.3.2)
activesupport (= 7.1.3.3) activesupport (= 7.1.3.2)
bundler (>= 1.15.0) bundler (>= 1.15.0)
railties (= 7.1.3.3) railties (= 7.1.3.2)
rails-controller-testing (1.0.5) rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1) actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1)
@ -668,9 +662,9 @@ GEM
rails-i18n (7.0.9) rails-i18n (7.0.9)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8) railties (>= 6.0.0, < 8)
railties (7.1.3.3) railties (7.1.3.2)
actionpack (= 7.1.3.3) actionpack (= 7.1.3.2)
activesupport (= 7.1.3.3) activesupport (= 7.1.3.2)
irb irb
rackup (>= 1.0.0) rackup (>= 1.0.0)
rake (>= 12.2) rake (>= 12.2)
@ -692,15 +686,14 @@ GEM
redlock (1.3.2) redlock (1.3.2)
redis (>= 3.0.0, < 6.0) redis (>= 3.0.0, < 6.0)
regexp_parser (2.9.0) regexp_parser (2.9.0)
reline (0.5.7) reline (0.5.6)
io-console (~> 0.5) io-console (~> 0.5)
request_store (1.6.0) request_store (1.6.0)
rack (>= 1.4) rack (>= 1.4)
responders (3.1.1) responders (3.1.1)
actionpack (>= 5.2) actionpack (>= 5.2)
railties (>= 5.2) railties (>= 5.2)
rexml (3.2.8) rexml (3.2.6)
strscan (>= 3.0.9)
rotp (6.3.0) rotp (6.3.0)
rouge (4.2.1) rouge (4.2.1)
rpam2 (4.0.2) rpam2 (4.0.2)
@ -715,7 +708,7 @@ GEM
rspec-support (~> 3.13.0) rspec-support (~> 3.13.0)
rspec-github (2.4.0) rspec-github (2.4.0)
rspec-core (~> 3.0) rspec-core (~> 3.0)
rspec-mocks (3.13.1) rspec-mocks (3.13.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0) rspec-support (~> 3.13.0)
rspec-rails (6.1.2) rspec-rails (6.1.2)
@ -726,7 +719,7 @@ GEM
rspec-expectations (~> 3.13) rspec-expectations (~> 3.13)
rspec-mocks (~> 3.13) rspec-mocks (~> 3.13)
rspec-support (~> 3.13) rspec-support (~> 3.13)
rspec-sidekiq (5.0.0) rspec-sidekiq (4.2.0)
rspec-core (~> 3.0) rspec-core (~> 3.0)
rspec-expectations (~> 3.0) rspec-expectations (~> 3.0)
rspec-mocks (~> 3.0) rspec-mocks (~> 3.0)
@ -781,7 +774,7 @@ GEM
scenic (1.8.0) scenic (1.8.0)
activerecord (>= 4.0.0) activerecord (>= 4.0.0)
railties (>= 4.0.0) railties (>= 4.0.0)
selenium-webdriver (4.21.1) selenium-webdriver (4.20.1)
base64 (~> 0.2) base64 (~> 0.2)
rexml (~> 3.2, >= 3.2.5) rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0) rubyzip (>= 1.2.2, < 3.0)
@ -822,7 +815,6 @@ GEM
stringio (3.1.0) stringio (3.1.0)
strong_migrations (1.8.0) strong_migrations (1.8.0)
activerecord (>= 5.2) activerecord (>= 5.2)
strscan (3.1.0)
swd (1.3.0) swd (1.3.0)
activesupport (>= 3) activesupport (>= 3)
attr_required (>= 0.0.5) attr_required (>= 0.0.5)
@ -901,7 +893,7 @@ GEM
xorcist (1.1.3) xorcist (1.1.3)
xpath (3.2.0) xpath (3.2.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)
zeitwerk (2.6.14) zeitwerk (2.6.13)
PLATFORMS PLATFORMS
ruby ruby
@ -919,7 +911,7 @@ DEPENDENCIES
browser browser
bundler-audit (~> 0.9) bundler-audit (~> 0.9)
capybara (~> 3.39) capybara (~> 3.39)
charlock_holmes! charlock_holmes (~> 0.7.7)
chewy (~> 7.3) chewy (~> 7.3)
climate_control climate_control
cocoon (~> 1.2) cocoon (~> 1.2)
@ -963,7 +955,7 @@ DEPENDENCIES
kaminari (~> 1.2) kaminari (~> 1.2)
kt-paperclip (~> 7.2) kt-paperclip (~> 7.2)
letter_opener (~> 1.8) letter_opener (~> 1.8)
letter_opener_web (~> 3.0) letter_opener_web (~> 2.0)
link_header (~> 0.0) link_header (~> 0.0)
lograge (~> 0.12) lograge (~> 0.12)
mail (~> 2.8) mail (~> 2.8)
@ -1020,7 +1012,7 @@ DEPENDENCIES
rqrcode (~> 2.2) rqrcode (~> 2.2)
rspec-github (~> 2.4) rspec-github (~> 2.4)
rspec-rails (~> 6.0) rspec-rails (~> 6.0)
rspec-sidekiq (~> 5.0) rspec-sidekiq (~> 4.0)
rubocop rubocop
rubocop-capybara rubocop-capybara
rubocop-performance rubocop-performance

View file

@ -25,7 +25,7 @@ class AccountsController < ApplicationController
limit = params[:limit].present? ? [params[:limit].to_i, PAGE_SIZE_MAX].min : PAGE_SIZE limit = params[:limit].present? ? [params[:limit].to_i, PAGE_SIZE_MAX].min : PAGE_SIZE
@statuses = filtered_statuses.without_reblogs.limit(limit) @statuses = filtered_statuses.without_reblogs.limit(limit)
@statuses = preload_collection(@statuses, Status) @statuses = cache_collection(@statuses, Status)
end end
format.json do format.json do

View file

@ -18,7 +18,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
def set_items def set_items
case params[:id] case params[:id]
when 'featured' when 'featured'
@items = for_signed_account { preload_collection(@account.pinned_statuses.not_local_only, Status) } @items = for_signed_account { cache_collection(@account.pinned_statuses.not_local_only, Status) }
@items = @items.map { |item| item.distributable? ? item : ActivityPub::TagManager.instance.uri_for(item) } @items = @items.map { |item| item.distributable? ? item : ActivityPub::TagManager.instance.uri_for(item) }
when 'tags' when 'tags'
@items = for_signed_account { @account.featured_tags } @items = for_signed_account { @account.featured_tags }

View file

@ -60,7 +60,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
def set_statuses def set_statuses
return unless page_requested? return unless page_requested?
@statuses = preload_collection_paginated_by_id( @statuses = cache_collection_paginated_by_id(
AccountStatusesFilter.new(@account, signed_request_account).results, AccountStatusesFilter.new(@account, signed_request_account).results,
Status, Status,
LIMIT, LIMIT,

View file

@ -19,11 +19,11 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
end end
def load_statuses def load_statuses
@account.unavailable? ? [] : preloaded_account_statuses @account.unavailable? ? [] : cached_account_statuses
end end
def preloaded_account_statuses def cached_account_statuses
preload_collection_paginated_by_id( cache_collection_paginated_by_id(
AccountStatusesFilter.new(@account, current_account, params).results, AccountStatusesFilter.new(@account, current_account, params).results,
Status, Status,
limit_param(DEFAULT_STATUSES_LIMIT), limit_param(DEFAULT_STATUSES_LIMIT),

View file

@ -4,6 +4,6 @@ class Api::V1::Apps::CredentialsController < Api::BaseController
def show def show
return doorkeeper_render_error unless valid_doorkeeper_token? return doorkeeper_render_error unless valid_doorkeeper_token?
render json: doorkeeper_token.application, serializer: REST::ApplicationSerializer render json: doorkeeper_token.application, serializer: REST::ApplicationSerializer, fields: %i(name website vapid_key client_id scopes)
end end
end end

View file

@ -5,7 +5,7 @@ class Api::V1::AppsController < Api::BaseController
def create def create
@app = Doorkeeper::Application.create!(application_options) @app = Doorkeeper::Application.create!(application_options)
render json: @app, serializer: REST::CredentialApplicationSerializer render json: @app, serializer: REST::ApplicationSerializer
end end
private private
@ -24,6 +24,6 @@ class Api::V1::AppsController < Api::BaseController
end end
def app_params def app_params
params.permit(:client_name, :scopes, :website, :redirect_uris, redirect_uris: []) params.permit(:client_name, :redirect_uris, :scopes, :website)
end end
end end

View file

@ -13,11 +13,11 @@ class Api::V1::BookmarksController < Api::BaseController
private private
def load_statuses def load_statuses
preloaded_bookmarks cached_bookmarks
end end
def preloaded_bookmarks def cached_bookmarks
preload_collection(results.map(&:status), Status) cache_collection(results.map(&:status), Status)
end end
def results def results

View file

@ -13,11 +13,11 @@ class Api::V1::FavouritesController < Api::BaseController
private private
def load_statuses def load_statuses
preloaded_favourites cached_favourites
end end
def preloaded_favourites def cached_favourites
preload_collection(results.map(&:status), Status) cache_collection(results.map(&:status), Status)
end end
def results def results

View file

@ -41,7 +41,7 @@ class Api::V1::Notifications::RequestsController < Api::BaseController
) )
NotificationRequest.preload_cache_collection(requests) do |statuses| NotificationRequest.preload_cache_collection(requests) do |statuses|
preload_collection(statuses, Status) cache_collection(statuses, Status)
end end
end end

View file

@ -50,7 +50,7 @@ class Api::V1::NotificationsController < Api::BaseController
) )
Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses| Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses|
preload_collection(target_statuses, Status) cache_collection(target_statuses, Status)
end end
end end

View file

@ -26,13 +26,13 @@ class Api::V1::StatusesController < Api::BaseController
DESCENDANTS_DEPTH_LIMIT = 20 DESCENDANTS_DEPTH_LIMIT = 20
def index def index
@statuses = preload_collection(@statuses, Status) @statuses = cache_collection(@statuses, Status)
render json: @statuses, each_serializer: REST::StatusSerializer render json: @statuses, each_serializer: REST::StatusSerializer
end end
def show def show
cache_if_unauthenticated! cache_if_unauthenticated!
@status = preload_collection([@status], Status).first @status = cache_collection([@status], Status).first
render json: @status, serializer: REST::StatusSerializer render json: @status, serializer: REST::StatusSerializer
end end
@ -51,8 +51,8 @@ class Api::V1::StatusesController < Api::BaseController
ancestors_results = @status.in_reply_to_id.nil? ? [] : @status.ancestors(ancestors_limit, current_account) ancestors_results = @status.in_reply_to_id.nil? ? [] : @status.ancestors(ancestors_limit, current_account)
descendants_results = @status.descendants(descendants_limit, current_account, descendants_depth_limit) descendants_results = @status.descendants(descendants_limit, current_account, descendants_depth_limit)
loaded_ancestors = preload_collection(ancestors_results, Status) loaded_ancestors = cache_collection(ancestors_results, Status)
loaded_descendants = preload_collection(descendants_results, Status) loaded_descendants = cache_collection(descendants_results, Status)
@context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants) @context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
statuses = [@status] + @context.ancestors + @context.descendants statuses = [@status] + @context.ancestors + @context.descendants

View file

@ -15,11 +15,11 @@ class Api::V1::Timelines::DirectController < Api::BaseController
private private
def load_statuses def load_statuses
preloaded_direct_statuses cached_direct_statuses
end end
def preloaded_direct_statuses def cached_direct_statuses
preload_collection direct_statuses, Status cache_collection direct_statuses, Status
end end
def direct_statuses def direct_statuses

View file

@ -21,11 +21,11 @@ class Api::V1::Timelines::HomeController < Api::V1::Timelines::BaseController
private private
def load_statuses def load_statuses
preloaded_home_statuses cached_home_statuses
end end
def preloaded_home_statuses def cached_home_statuses
preload_collection home_statuses, Status cache_collection home_statuses, Status
end end
def home_statuses def home_statuses

View file

@ -21,11 +21,11 @@ class Api::V1::Timelines::ListController < Api::V1::Timelines::BaseController
end end
def set_statuses def set_statuses
@statuses = preloaded_list_statuses @statuses = cached_list_statuses
end end
def preloaded_list_statuses def cached_list_statuses
preload_collection list_statuses, Status cache_collection list_statuses, Status
end end
def list_statuses def list_statuses

View file

@ -18,11 +18,11 @@ class Api::V1::Timelines::PublicController < Api::V1::Timelines::BaseController
end end
def load_statuses def load_statuses
preloaded_public_statuses_page cached_public_statuses_page
end end
def preloaded_public_statuses_page def cached_public_statuses_page
preload_collection(public_statuses, Status) cache_collection(public_statuses, Status)
end end
def public_statuses def public_statuses

View file

@ -23,11 +23,11 @@ class Api::V1::Timelines::TagController < Api::V1::Timelines::BaseController
end end
def load_statuses def load_statuses
preloaded_tagged_statuses cached_tagged_statuses
end end
def preloaded_tagged_statuses def cached_tagged_statuses
@tag.nil? ? [] : preload_collection(tag_timeline_statuses, Status) @tag.nil? ? [] : cache_collection(tag_timeline_statuses, Status)
end end
def tag_timeline_statuses def tag_timeline_statuses

View file

@ -20,7 +20,7 @@ class Api::V1::Trends::StatusesController < Api::BaseController
def set_statuses def set_statuses
@statuses = if enabled? @statuses = if enabled?
preload_collection(statuses_from_trends.offset(offset_param).limit(limit_param(DEFAULT_STATUSES_LIMIT)), Status) cache_collection(statuses_from_trends.offset(offset_param).limit(limit_param(DEFAULT_STATUSES_LIMIT)), Status)
else else
[] []
end end

View file

@ -9,7 +9,6 @@ class ApplicationController < ActionController::Base
include UserTrackingConcern include UserTrackingConcern
include SessionTrackingConcern include SessionTrackingConcern
include CacheConcern include CacheConcern
include PreloadingConcern
include DomainControlHelper include DomainControlHelper
include ThemingConcern include ThemingConcern
include DatabaseHelper include DatabaseHelper

View file

@ -45,4 +45,20 @@ module CacheConcern
Rails.cache.write(key, response.body, expires_in: expires_in, raw: true) Rails.cache.write(key, response.body, expires_in: expires_in, raw: true)
end end
end end
# TODO: Rename this method, as it does not perform any caching anymore.
def cache_collection(raw, klass)
return raw unless klass.respond_to?(:preload_cacheable_associations)
records = raw.to_a
klass.preload_cacheable_associations(records)
records
end
# TODO: Rename this method, as it does not perform any caching anymore.
def cache_collection_paginated_by_id(raw, klass, limit, options)
cache_collection raw.to_a_paginated_by_id(limit, options), klass
end
end end

View file

@ -1,17 +0,0 @@
# frozen_string_literal: true
module PreloadingConcern
extend ActiveSupport::Concern
def preload_collection(scope, klass)
return scope unless klass.respond_to?(:preload_cacheable_associations)
scope.to_a.tap do |records|
klass.preload_cacheable_associations(records)
end
end
def preload_collection_paginated_by_id(scope, klass, limit, options)
preload_collection scope.to_a_paginated_by_id(limit, options), klass
end
end

View file

@ -13,7 +13,7 @@ class Settings::ApplicationsController < Settings::BaseController
def new def new
@application = Doorkeeper::Application.new( @application = Doorkeeper::Application.new(
redirect_uri: Doorkeeper.configuration.native_redirect_uri, redirect_uri: Doorkeeper.configuration.native_redirect_uri,
scopes: 'read:me' scopes: 'read write follow'
) )
end end

View file

@ -45,7 +45,7 @@ class TagsController < ApplicationController
end end
def set_statuses def set_statuses
@statuses = preload_collection(TagFeed.new(@tag, nil, local: @local).get(limit_param), Status) @statuses = cache_collection(TagFeed.new(@tag, nil, local: @local).get(limit_param), Status)
end end
def limit_param def limit_param

View file

@ -241,20 +241,11 @@ module ApplicationHelper
EmojiFormatter.new(html, custom_emojis, other_options.merge(animate: prefers_autoplay?)).to_s EmojiFormatter.new(html, custom_emojis, other_options.merge(animate: prefers_autoplay?)).to_s
end end
def mascot_url def site_icon_path(type, size = '48')
full_asset_url(instance_presenter.mascot&.file&.url || frontend_asset_path('images/elephant_ui_plane.svg')) icon = SiteUpload.find_by(var: type)
end return nil unless icon
def instance_presenter icon.file.url(size)
@instance_presenter ||= InstancePresenter.new
end
def favicon_path(size = '48')
instance_presenter.favicon&.file&.url(size)
end
def app_icon_path(size = '48')
instance_presenter.app_icon&.file&.url(size)
end end
# glitch-soc addition to handle the multiple flavors # glitch-soc addition to handle the multiple flavors

View file

@ -0,0 +1,11 @@
# frozen_string_literal: true
module MascotHelper
def mascot_url
full_asset_url(instance_presenter.mascot&.file&.url || frontend_asset_path('images/elephant_ui_plane.svg'))
end
def instance_presenter
@instance_presenter ||= InstancePresenter.new
end
end

View file

@ -20,7 +20,7 @@ export function changeSetting(path, value) {
} }
const debouncedSave = debounce((dispatch, getState) => { const debouncedSave = debounce((dispatch, getState) => {
if (getState().getIn(['settings', 'saved']) || !getState().getIn(['meta', 'me'])) { if (getState().getIn(['settings', 'saved'])) {
return; return;
} }

View file

@ -174,6 +174,7 @@ Account.propTypes = {
onBlock: PropTypes.func, onBlock: PropTypes.func,
onMute: PropTypes.func, onMute: PropTypes.func,
onMuteNotifications: PropTypes.func, onMuteNotifications: PropTypes.func,
intl: PropTypes.object.isRequired,
hidden: PropTypes.bool, hidden: PropTypes.bool,
minimal: PropTypes.bool, minimal: PropTypes.bool,
defaultAction: PropTypes.string, defaultAction: PropTypes.string,

View file

@ -14,10 +14,8 @@ import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import SettingsIcon from '@/material-icons/400-24px/settings.svg?react'; import SettingsIcon from '@/material-icons/400-24px/settings.svg?react';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { ButtonInTabsBar } from 'flavours/glitch/features/ui/util/columns_context'; import { ButtonInTabsBar } from 'flavours/glitch/features/ui/util/columns_context';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
import { useAppHistory } from './router'; import { useAppHistory } from './router';
const messages = defineMessages({ const messages = defineMessages({
@ -53,8 +51,12 @@ BackButton.propTypes = {
}; };
class ColumnHeader extends PureComponent { class ColumnHeader extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
title: PropTypes.node, title: PropTypes.node,
icon: PropTypes.string, icon: PropTypes.string,
@ -169,7 +171,7 @@ class ColumnHeader extends PureComponent {
); );
} }
if (this.props.identity.signedIn && (children || (multiColumn && this.props.onPin))) { if (this.context.identity.signedIn && (children || (multiColumn && this.props.onPin))) {
collapseButton = ( collapseButton = (
<button <button
className={collapsibleButtonClassName} className={collapsibleButtonClassName}
@ -230,4 +232,4 @@ class ColumnHeader extends PureComponent {
} }
export default injectIntl(withIdentity(withRouter(ColumnHeader))); export default injectIntl(withRouter(ColumnHeader));

View file

@ -14,7 +14,6 @@ import CheckIcon from '@/material-icons/400-24px/check.svg?react';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import emojify from 'flavours/glitch/features/emoji/emoji'; import emojify from 'flavours/glitch/features/emoji/emoji';
import Motion from 'flavours/glitch/features/ui/util/optional_motion'; import Motion from 'flavours/glitch/features/ui/util/optional_motion';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { RelativeTimestamp } from './relative_timestamp'; import { RelativeTimestamp } from './relative_timestamp';
@ -39,8 +38,12 @@ const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => {
}, {}); }, {});
class Poll extends ImmutablePureComponent { class Poll extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
poll: ImmutablePropTypes.map, poll: ImmutablePropTypes.map,
lang: PropTypes.string, lang: PropTypes.string,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
@ -232,7 +235,7 @@ class Poll extends ImmutablePureComponent {
</ul> </ul>
<div className='poll__footer'> <div className='poll__footer'>
{!showResults && <button className='button button-secondary' disabled={disabled || !this.props.identity.signedIn} onClick={this.handleVote}><FormattedMessage id='poll.vote' defaultMessage='Vote' /></button>} {!showResults && <button className='button button-secondary' disabled={disabled || !this.context.identity.signedIn} onClick={this.handleVote}><FormattedMessage id='poll.vote' defaultMessage='Vote' /></button>}
{!showResults && <><button className='poll__link' onClick={this.handleReveal}><FormattedMessage id='poll.reveal' defaultMessage='See results' /></button> · </>} {!showResults && <><button className='poll__link' onClick={this.handleReveal}><FormattedMessage id='poll.reveal' defaultMessage='See results' /></button> · </>}
{showResults && !this.props.disabled && <><button className='poll__link' onClick={this.handleRefresh}><FormattedMessage id='poll.refresh' defaultMessage='Refresh' /></button> · </>} {showResults && !this.props.disabled && <><button className='poll__link' onClick={this.handleRefresh}><FormattedMessage id='poll.refresh' defaultMessage='Refresh' /></button> · </>}
{votesCount} {votesCount}
@ -244,4 +247,4 @@ class Poll extends ImmutablePureComponent {
} }
export default injectIntl(withIdentity(Poll)); export default injectIntl(Poll);

View file

@ -12,7 +12,6 @@ import { HotKeys } from 'react-hotkeys';
import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder'; import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder';
import PollContainer from 'flavours/glitch/containers/poll_container'; import PollContainer from 'flavours/glitch/containers/poll_container';
import NotificationOverlayContainer from 'flavours/glitch/features/notifications/containers/overlay_container'; import NotificationOverlayContainer from 'flavours/glitch/features/notifications/containers/overlay_container';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { autoUnfoldCW } from 'flavours/glitch/utils/content_warning'; import { autoUnfoldCW } from 'flavours/glitch/utils/content_warning';
import { withOptionalRouter, WithOptionalRouterPropTypes } from 'flavours/glitch/utils/react_router'; import { withOptionalRouter, WithOptionalRouterPropTypes } from 'flavours/glitch/utils/react_router';
@ -21,6 +20,7 @@ import Card from '../features/status/components/card';
// to use the progress bar to show download progress // to use the progress bar to show download progress
import Bundle from '../features/ui/components/bundle'; import Bundle from '../features/ui/components/bundle';
import { MediaGallery, Video, Audio } from '../features/ui/util/async-components'; import { MediaGallery, Video, Audio } from '../features/ui/util/async-components';
import { IdentityConsumer } from '../features/ui/util/identity_consumer';
import { SensitiveMediaContext } from '../features/ui/util/sensitive_media_context'; import { SensitiveMediaContext } from '../features/ui/util/sensitive_media_context';
import { displayMedia, visibleReactions } from '../initial_state'; import { displayMedia, visibleReactions } from '../initial_state';
@ -78,7 +78,6 @@ class Status extends ImmutablePureComponent {
static contextType = SensitiveMediaContext; static contextType = SensitiveMediaContext;
static propTypes = { static propTypes = {
identity: identityContextPropShape,
containerId: PropTypes.string, containerId: PropTypes.string,
id: PropTypes.string, id: PropTypes.string,
status: ImmutablePropTypes.map, status: ImmutablePropTypes.map,
@ -546,7 +545,6 @@ class Status extends ImmutablePureComponent {
nextInReplyToId, nextInReplyToId,
rootId, rootId,
history, history,
identity,
...other ...other
} = this.props; } = this.props;
const { isCollapsed } = this.state; const { isCollapsed } = this.state;
@ -848,14 +846,18 @@ class Status extends ImmutablePureComponent {
{...statusContentProps} {...statusContentProps}
/> />
<IdentityConsumer>
{identity => (
<StatusReactions <StatusReactions
statusId={status.get('id')} statusId={status.get('id')}
reactions={status.get('reactions')} reactions={status.get('reactions')}
numVisible={visibleReactions} numVisible={visibleReactions}
addReaction={this.props.onReactionAdd} addReaction={this.props.onReactionAdd}
removeReaction={this.props.onReactionRemove} removeReaction={this.props.onReactionRemove}
canReact={this.props.identity.signedIn} canReact={identity.signedIn}
/> />
)}
</IdentityConsumer>
{(!isCollapsed || !(muted || !settings.getIn(['collapsed', 'show_action_bar']))) && ( {(!isCollapsed || !(muted || !settings.getIn(['collapsed', 'show_action_bar']))) && (
<StatusActionBar <StatusActionBar
@ -879,4 +881,4 @@ class Status extends ImmutablePureComponent {
} }
export default withOptionalRouter(injectIntl((withIdentity(Status)))); export default withOptionalRouter(injectIntl(Status));

View file

@ -22,7 +22,6 @@ import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg'; import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg';
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg'; import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg';
import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react'; import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links'; import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
@ -71,8 +70,12 @@ const messages = defineMessages({
}); });
class StatusActionBar extends ImmutablePureComponent { class StatusActionBar extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
status: ImmutablePropTypes.map.isRequired, status: ImmutablePropTypes.map.isRequired,
onReply: PropTypes.func, onReply: PropTypes.func,
onFavourite: PropTypes.func, onFavourite: PropTypes.func,
@ -109,7 +112,7 @@ class StatusActionBar extends ImmutablePureComponent {
]; ];
handleReplyClick = () => { handleReplyClick = () => {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
this.props.onReply(this.props.status, this.props.history); this.props.onReply(this.props.status, this.props.history);
@ -125,7 +128,7 @@ class StatusActionBar extends ImmutablePureComponent {
}; };
handleFavouriteClick = (e) => { handleFavouriteClick = (e) => {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
this.props.onFavourite(this.props.status, e); this.props.onFavourite(this.props.status, e);
@ -139,7 +142,7 @@ class StatusActionBar extends ImmutablePureComponent {
}; };
handleReblogClick = e => { handleReblogClick = e => {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
this.props.onReblog(this.props.status, e); this.props.onReblog(this.props.status, e);
@ -215,7 +218,7 @@ class StatusActionBar extends ImmutablePureComponent {
render () { render () {
const { status, intl, withDismiss, withCounters, showReplyCount, scrollKey } = this.props; const { status, intl, withDismiss, withCounters, showReplyCount, scrollKey } = this.props;
const { permissions, signedIn } = this.props.identity; const { permissions, signedIn } = this.context.identity;
const mutingConversation = status.get('muted'); const mutingConversation = status.get('muted');
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility')); const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
@ -367,4 +370,4 @@ class StatusActionBar extends ImmutablePureComponent {
} }
export default withRouter(withIdentity(injectIntl(StatusActionBar))); export default withRouter(injectIntl(StatusActionBar));

View file

@ -15,7 +15,6 @@ import LinkIcon from '@/material-icons/400-24px/link.svg?react';
import MovieIcon from '@/material-icons/400-24px/movie.svg?react'; import MovieIcon from '@/material-icons/400-24px/movie.svg?react';
import MusicNoteIcon from '@/material-icons/400-24px/music_note.svg?react'; import MusicNoteIcon from '@/material-icons/400-24px/music_note.svg?react';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state'; import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state';
import { decode as decodeIDNA } from 'flavours/glitch/utils/idna'; import { decode as decodeIDNA } from 'flavours/glitch/utils/idna';
@ -127,8 +126,12 @@ const mapStateToProps = state => ({
}); });
class StatusContent extends PureComponent { class StatusContent extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
status: ImmutablePropTypes.map.isRequired, status: ImmutablePropTypes.map.isRequired,
statusContent: PropTypes.string, statusContent: PropTypes.string,
expanded: PropTypes.bool, expanded: PropTypes.bool,
@ -346,7 +349,7 @@ class StatusContent extends PureComponent {
const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden; const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
const contentLocale = intl.locale.replace(/[_-].*/, ''); const contentLocale = intl.locale.replace(/[_-].*/, '');
const targetLanguages = this.props.languages?.get(status.get('language') || 'und'); const targetLanguages = this.props.languages?.get(status.get('language') || 'und');
const renderTranslate = this.props.onTranslate && this.props.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('search_index').trim().length > 0 && targetLanguages?.includes(contentLocale); const renderTranslate = this.props.onTranslate && this.context.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('search_index').trim().length > 0 && targetLanguages?.includes(contentLocale);
const content = { __html: statusContent ?? getStatusContent(status) }; const content = { __html: statusContent ?? getStatusContent(status) };
const spoilerContent = { __html: status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml') }; const spoilerContent = { __html: status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml') };
@ -500,4 +503,4 @@ class StatusContent extends PureComponent {
} }
export default withRouter(withIdentity(connect(mapStateToProps)(injectIntl(StatusContent)))); export default withRouter(connect(mapStateToProps)(injectIntl(StatusContent)));

View file

@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react'; import { PureComponent } from 'react';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
@ -14,7 +15,6 @@ import { connectUserStream } from 'flavours/glitch/actions/streaming';
import ErrorBoundary from 'flavours/glitch/components/error_boundary'; import ErrorBoundary from 'flavours/glitch/components/error_boundary';
import { Router } from 'flavours/glitch/components/router'; import { Router } from 'flavours/glitch/components/router';
import UI from 'flavours/glitch/features/ui'; import UI from 'flavours/glitch/features/ui';
import { IdentityContext, createIdentityContext } from 'flavours/glitch/identity_context';
import initialState, { title as siteTitle } from 'flavours/glitch/initial_state'; import initialState, { title as siteTitle } from 'flavours/glitch/initial_state';
import { IntlProvider } from 'flavours/glitch/locales'; import { IntlProvider } from 'flavours/glitch/locales';
import { store } from 'flavours/glitch/store'; import { store } from 'flavours/glitch/store';
@ -33,9 +33,33 @@ if (initialState.meta.me) {
store.dispatch(fetchCustomEmojis()); store.dispatch(fetchCustomEmojis());
} }
const createIdentityContext = state => ({
signedIn: !!state.meta.me,
accountId: state.meta.me,
disabledAccountId: state.meta.disabled_account_id,
accessToken: state.meta.access_token,
permissions: state.role ? state.role.permissions : 0,
});
export default class Mastodon extends PureComponent { export default class Mastodon extends PureComponent {
static childContextTypes = {
identity: PropTypes.shape({
signedIn: PropTypes.bool.isRequired,
accountId: PropTypes.string,
disabledAccountId: PropTypes.string,
accessToken: PropTypes.string,
}).isRequired,
};
identity = createIdentityContext(initialState); identity = createIdentityContext(initialState);
getChildContext() {
return {
identity: this.identity,
};
}
componentDidMount() { componentDidMount() {
if (this.identity.signedIn) { if (this.identity.signedIn) {
this.disconnect = store.dispatch(connectUserStream()); this.disconnect = store.dispatch(connectUserStream());
@ -55,7 +79,6 @@ export default class Mastodon extends PureComponent {
render () { render () {
return ( return (
<IdentityContext.Provider value={this.identity}>
<IntlProvider> <IntlProvider>
<ReduxProvider store={store}> <ReduxProvider store={store}>
<ErrorBoundary> <ErrorBoundary>
@ -69,7 +92,6 @@ export default class Mastodon extends PureComponent {
</ErrorBoundary> </ErrorBoundary>
</ReduxProvider> </ReduxProvider>
</IntlProvider> </IntlProvider>
</IdentityContext.Provider>
); );
} }

View file

@ -23,7 +23,6 @@ import { Icon } from 'flavours/glitch/components/icon';
import { IconButton } from 'flavours/glitch/components/icon_button'; import { IconButton } from 'flavours/glitch/components/icon_button';
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator'; import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container'; import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { autoPlayGif, me, domain as localDomain } from 'flavours/glitch/initial_state'; import { autoPlayGif, me, domain as localDomain } from 'flavours/glitch/initial_state';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
import { preferencesLink, profileLink, accountAdminLink } from 'flavours/glitch/utils/backend_links'; import { preferencesLink, profileLink, accountAdminLink } from 'flavours/glitch/utils/backend_links';
@ -95,7 +94,6 @@ const dateFormatOptions = {
class Header extends ImmutablePureComponent { class Header extends ImmutablePureComponent {
static propTypes = { static propTypes = {
identity: identityContextPropShape,
account: ImmutablePropTypes.record, account: ImmutablePropTypes.record,
identity_props: ImmutablePropTypes.list, identity_props: ImmutablePropTypes.list,
onFollow: PropTypes.func.isRequired, onFollow: PropTypes.func.isRequired,
@ -119,6 +117,10 @@ class Header extends ImmutablePureComponent {
...WithRouterPropTypes, ...WithRouterPropTypes,
}; };
static contextTypes = {
identity: PropTypes.object,
};
openEditProfile = () => { openEditProfile = () => {
window.open(profileLink, '_blank'); window.open(profileLink, '_blank');
}; };
@ -168,7 +170,7 @@ class Header extends ImmutablePureComponent {
render () { render () {
const { account, hidden, intl } = this.props; const { account, hidden, intl } = this.props;
const { signedIn, permissions } = this.props.identity; const { signedIn, permissions } = this.context.identity;
if (!account) { if (!account) {
return null; return null;
@ -412,4 +414,4 @@ class Header extends ImmutablePureComponent {
} }
export default withRouter(withIdentity(injectIntl(Header))); export default withRouter(injectIntl(Header));

View file

@ -9,7 +9,6 @@ import { connect } from 'react-redux';
import PeopleIcon from '@/material-icons/400-24px/group.svg?react'; import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner'; import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { domain } from 'flavours/glitch/initial_state'; import { domain } from 'flavours/glitch/initial_state';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
@ -41,12 +40,16 @@ const mapStateToProps = (state, { columnId }) => {
}; };
class CommunityTimeline extends PureComponent { class CommunityTimeline extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static defaultProps = { static defaultProps = {
onlyMedia: false, onlyMedia: false,
}; };
static propTypes = { static propTypes = {
identity: identityContextPropShape,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
columnId: PropTypes.string, columnId: PropTypes.string,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
@ -77,7 +80,7 @@ class CommunityTimeline extends PureComponent {
componentDidMount () { componentDidMount () {
const { dispatch, onlyMedia } = this.props; const { dispatch, onlyMedia } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
dispatch(expandCommunityTimeline({ onlyMedia })); dispatch(expandCommunityTimeline({ onlyMedia }));
@ -87,7 +90,7 @@ class CommunityTimeline extends PureComponent {
} }
componentDidUpdate (prevProps) { componentDidUpdate (prevProps) {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (prevProps.onlyMedia !== this.props.onlyMedia) { if (prevProps.onlyMedia !== this.props.onlyMedia) {
const { dispatch, onlyMedia } = this.props; const { dispatch, onlyMedia } = this.props;
@ -162,4 +165,4 @@ class CommunityTimeline extends PureComponent {
} }
export default withIdentity(connect(mapStateToProps)(injectIntl(CommunityTimeline))); export default connect(mapStateToProps)(injectIntl(CommunityTimeline));

View file

@ -12,7 +12,6 @@ import CancelIcon from '@/material-icons/400-24px/cancel-fill.svg?react';
import CloseIcon from '@/material-icons/400-24px/close.svg?react'; import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import SearchIcon from '@/material-icons/400-24px/search.svg?react'; import SearchIcon from '@/material-icons/400-24px/search.svg?react';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { domain, searchEnabled } from 'flavours/glitch/initial_state'; import { domain, searchEnabled } from 'flavours/glitch/initial_state';
import { HASHTAG_REGEX } from 'flavours/glitch/utils/hashtags'; import { HASHTAG_REGEX } from 'flavours/glitch/utils/hashtags';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
@ -34,8 +33,12 @@ const labelForRecentSearch = search => {
}; };
class Search extends PureComponent { class Search extends PureComponent {
static contextTypes = {
identity: PropTypes.object.isRequired,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
value: PropTypes.string.isRequired, value: PropTypes.string.isRequired,
recent: ImmutablePropTypes.orderedSet, recent: ImmutablePropTypes.orderedSet,
submitted: PropTypes.bool, submitted: PropTypes.bool,
@ -273,7 +276,7 @@ class Search extends PureComponent {
} }
_calculateOptions (value) { _calculateOptions (value) {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
const trimmedValue = value.trim(); const trimmedValue = value.trim();
const options = []; const options = [];
@ -315,7 +318,7 @@ class Search extends PureComponent {
render () { render () {
const { intl, value, submitted, recent } = this.props; const { intl, value, submitted, recent } = this.props;
const { expanded, options, selectedOption } = this.state; const { expanded, options, selectedOption } = this.state;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
const hasValue = value.length > 0 || submitted; const hasValue = value.length > 0 || submitted;
@ -399,4 +402,4 @@ class Search extends PureComponent {
} }
export default withRouter(withIdentity(injectIntl(Search))); export default withRouter(injectIntl(Search));

View file

@ -13,7 +13,6 @@ import SearchIcon from '@/material-icons/400-24px/search.svg?react';
import Column from 'flavours/glitch/components/column'; import Column from 'flavours/glitch/components/column';
import ColumnHeader from 'flavours/glitch/components/column_header'; import ColumnHeader from 'flavours/glitch/components/column_header';
import Search from 'flavours/glitch/features/compose/containers/search_container'; import Search from 'flavours/glitch/features/compose/containers/search_container';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { trendsEnabled } from 'flavours/glitch/initial_state'; import { trendsEnabled } from 'flavours/glitch/initial_state';
import Links from './links'; import Links from './links';
@ -33,8 +32,12 @@ const mapStateToProps = state => ({
}); });
class Explore extends PureComponent { class Explore extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
multiColumn: PropTypes.bool, multiColumn: PropTypes.bool,
isSearching: PropTypes.bool, isSearching: PropTypes.bool,
@ -50,7 +53,7 @@ class Explore extends PureComponent {
render() { render() {
const { intl, multiColumn, isSearching } = this.props; const { intl, multiColumn, isSearching } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
return ( return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
@ -111,4 +114,4 @@ class Explore extends PureComponent {
} }
export default withIdentity(connect(mapStateToProps)(injectIntl(Explore))); export default connect(mapStateToProps)(injectIntl(Explore));

View file

@ -6,7 +6,6 @@ import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
import { useIdentity } from '@/flavours/glitch/identity_context';
import PublicIcon from '@/material-icons/400-24px/public.svg?react'; import PublicIcon from '@/material-icons/400-24px/public.svg?react';
import { addColumn } from 'flavours/glitch/actions/columns'; import { addColumn } from 'flavours/glitch/actions/columns';
import { changeSetting } from 'flavours/glitch/actions/settings'; import { changeSetting } from 'flavours/glitch/actions/settings';
@ -14,7 +13,7 @@ import { connectPublicStream, connectCommunityStream } from 'flavours/glitch/act
import { expandPublicTimeline, expandCommunityTimeline } from 'flavours/glitch/actions/timelines'; import { expandPublicTimeline, expandCommunityTimeline } from 'flavours/glitch/actions/timelines';
import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner'; import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner';
import SettingText from 'flavours/glitch/components/setting_text'; import SettingText from 'flavours/glitch/components/setting_text';
import { domain } from 'flavours/glitch/initial_state'; import initialState, { domain } from 'flavours/glitch/initial_state';
import { useAppDispatch, useAppSelector } from 'flavours/glitch/store'; import { useAppDispatch, useAppSelector } from 'flavours/glitch/store';
import Column from '../../components/column'; import Column from '../../components/column';
@ -27,6 +26,15 @@ const messages = defineMessages({
filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter out by regular expressions' }, filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter out by regular expressions' },
}); });
// TODO: use a proper React context later on
const useIdentity = () => ({
signedIn: !!initialState.meta.me,
accountId: initialState.meta.me,
disabledAccountId: initialState.meta.disabled_account_id,
accessToken: initialState.meta.access_token,
permissions: initialState.role ? initialState.role.permissions : 0,
});
const ColumnSettings = () => { const ColumnSettings = () => {
const intl = useIntl(); const intl = useIntl();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();

View file

@ -28,7 +28,6 @@ import { fetchLists } from 'flavours/glitch/actions/lists';
import { openModal } from 'flavours/glitch/actions/modal'; import { openModal } from 'flavours/glitch/actions/modal';
import Column from 'flavours/glitch/features/ui/components/column'; import Column from 'flavours/glitch/features/ui/components/column';
import LinkFooter from 'flavours/glitch/features/ui/components/link_footer'; import LinkFooter from 'flavours/glitch/features/ui/components/link_footer';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { preferencesLink } from 'flavours/glitch/utils/backend_links'; import { preferencesLink } from 'flavours/glitch/utils/backend_links';
@ -100,8 +99,12 @@ const badgeDisplay = (number, limit) => {
}; };
class GettingStarted extends ImmutablePureComponent { class GettingStarted extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
myAccount: ImmutablePropTypes.record, myAccount: ImmutablePropTypes.record,
columns: ImmutablePropTypes.list, columns: ImmutablePropTypes.list,
@ -120,7 +123,7 @@ class GettingStarted extends ImmutablePureComponent {
componentDidMount () { componentDidMount () {
const { fetchFollowRequests } = this.props; const { fetchFollowRequests } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (!signedIn) { if (!signedIn) {
return; return;
@ -131,7 +134,7 @@ class GettingStarted extends ImmutablePureComponent {
render () { render () {
const { intl, myAccount, columns, multiColumn, unreadFollowRequests, unreadNotifications, lists, openSettings } = this.props; const { intl, myAccount, columns, multiColumn, unreadFollowRequests, unreadNotifications, lists, openSettings } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
const navItems = []; const navItems = [];
let listItems = []; let listItems = [];
@ -216,4 +219,4 @@ class GettingStarted extends ImmutablePureComponent {
} }
export default withIdentity(connect(makeMapStateToProps, mapDispatchToProps)(injectIntl(GettingStarted))); export default connect(makeMapStateToProps, mapDispatchToProps)(injectIntl(GettingStarted));

View file

@ -16,7 +16,7 @@ import { openModal } from 'flavours/glitch/actions/modal';
import Column from 'flavours/glitch/features/ui/components/column'; import Column from 'flavours/glitch/features/ui/components/column';
import ColumnLink from 'flavours/glitch/features/ui/components/column_link'; import ColumnLink from 'flavours/glitch/features/ui/components/column_link';
import ColumnSubheading from 'flavours/glitch/features/ui/components/column_subheading'; import ColumnSubheading from 'flavours/glitch/features/ui/components/column_subheading';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
const messages = defineMessages({ const messages = defineMessages({
heading: { id: 'column.heading', defaultMessage: 'Misc' }, heading: { id: 'column.heading', defaultMessage: 'Misc' },
@ -32,8 +32,11 @@ const messages = defineMessages({
class GettingStartedMisc extends ImmutablePureComponent { class GettingStartedMisc extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
}; };
@ -46,7 +49,7 @@ class GettingStartedMisc extends ImmutablePureComponent {
render () { render () {
const { intl } = this.props; const { intl } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
return ( return (
<Column icon='ellipsis-h' iconComponent={MoreHorizIcon} heading={intl.formatMessage(messages.heading)} alwaysShowBackButton> <Column icon='ellipsis-h' iconComponent={MoreHorizIcon} heading={intl.formatMessage(messages.heading)} alwaysShowBackButton>
@ -66,4 +69,4 @@ class GettingStartedMisc extends ImmutablePureComponent {
} }
export default connect()(withIdentity(injectIntl(GettingStartedMisc))); export default connect()(injectIntl(GettingStartedMisc));

View file

@ -17,7 +17,6 @@ import { fetchHashtag, followHashtag, unfollowHashtag } from 'flavours/glitch/ac
import { expandHashtagTimeline, clearTimeline } from 'flavours/glitch/actions/timelines'; import { expandHashtagTimeline, clearTimeline } from 'flavours/glitch/actions/timelines';
import Column from 'flavours/glitch/components/column'; import Column from 'flavours/glitch/components/column';
import ColumnHeader from 'flavours/glitch/components/column_header'; import ColumnHeader from 'flavours/glitch/components/column_header';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import StatusListContainer from '../ui/containers/status_list_container'; import StatusListContainer from '../ui/containers/status_list_container';
@ -30,10 +29,14 @@ const mapStateToProps = (state, props) => ({
}); });
class HashtagTimeline extends PureComponent { class HashtagTimeline extends PureComponent {
disconnects = []; disconnects = [];
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
params: PropTypes.object.isRequired, params: PropTypes.object.isRequired,
columnId: PropTypes.string, columnId: PropTypes.string,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
@ -91,7 +94,7 @@ class HashtagTimeline extends PureComponent {
}; };
_subscribe (dispatch, id, tags = {}, local) { _subscribe (dispatch, id, tags = {}, local) {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (!signedIn) { if (!signedIn) {
return; return;
@ -165,7 +168,7 @@ class HashtagTimeline extends PureComponent {
handleFollow = () => { handleFollow = () => {
const { dispatch, params, tag } = this.props; const { dispatch, params, tag } = this.props;
const { id } = params; const { id } = params;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (!signedIn) { if (!signedIn) {
return; return;
@ -182,7 +185,7 @@ class HashtagTimeline extends PureComponent {
const { hasUnread, columnId, multiColumn, tag } = this.props; const { hasUnread, columnId, multiColumn, tag } = this.props;
const { id, local } = this.props.params; const { id, local } = this.props.params;
const pinned = !!columnId; const pinned = !!columnId;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
return ( return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={`#${id}`}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={`#${id}`}>
@ -222,4 +225,4 @@ class HashtagTimeline extends PureComponent {
} }
export default connect(mapStateToProps)(withIdentity(HashtagTimeline)); export default connect(mapStateToProps)(HashtagTimeline);

View file

@ -14,7 +14,6 @@ import { fetchAnnouncements, toggleShowAnnouncements } from 'flavours/glitch/act
import { IconWithBadge } from 'flavours/glitch/components/icon_with_badge'; import { IconWithBadge } from 'flavours/glitch/components/icon_with_badge';
import { NotSignedInIndicator } from 'flavours/glitch/components/not_signed_in_indicator'; import { NotSignedInIndicator } from 'flavours/glitch/components/not_signed_in_indicator';
import AnnouncementsContainer from 'flavours/glitch/features/getting_started/containers/announcements_container'; import AnnouncementsContainer from 'flavours/glitch/features/getting_started/containers/announcements_container';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { criticalUpdatesPending } from 'flavours/glitch/initial_state'; import { criticalUpdatesPending } from 'flavours/glitch/initial_state';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
@ -42,8 +41,12 @@ const mapStateToProps = state => ({
}); });
class HomeTimeline extends PureComponent { class HomeTimeline extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
hasUnread: PropTypes.bool, hasUnread: PropTypes.bool,
@ -125,7 +128,7 @@ class HomeTimeline extends PureComponent {
render () { render () {
const { intl, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props; const { intl, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props;
const pinned = !!columnId; const pinned = !!columnId;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
const banners = []; const banners = [];
let announcementsButton; let announcementsButton;
@ -189,4 +192,4 @@ class HomeTimeline extends PureComponent {
} }
export default connect(mapStateToProps)(withIdentity(injectIntl(HomeTimeline))); export default connect(mapStateToProps)(injectIntl(HomeTimeline));

View file

@ -5,7 +5,6 @@ import { FormattedMessage } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_REPORTS } from 'flavours/glitch/permissions'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_REPORTS } from 'flavours/glitch/permissions';
import { CheckboxWithLabel } from './checkbox_with_label'; import { CheckboxWithLabel } from './checkbox_with_label';
@ -14,9 +13,13 @@ import GrantPermissionButton from './grant_permission_button';
import PillBarButton from './pill_bar_button'; import PillBarButton from './pill_bar_button';
import SettingToggle from './setting_toggle'; import SettingToggle from './setting_toggle';
class ColumnSettings extends PureComponent { export default class ColumnSettings extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
settings: ImmutablePropTypes.map.isRequired, settings: ImmutablePropTypes.map.isRequired,
pushSettings: ImmutablePropTypes.map.isRequired, pushSettings: ImmutablePropTypes.map.isRequired,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
@ -224,7 +227,7 @@ class ColumnSettings extends PureComponent {
</div> </div>
</section> </section>
{((this.props.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) && ( {((this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) && (
<section role='group' aria-labelledby='notifications-admin-sign-up'> <section role='group' aria-labelledby='notifications-admin-sign-up'>
<h3 id='notifications-status'><FormattedMessage id='notifications.column_settings.admin.sign_up' defaultMessage='New sign-ups:' /></h3> <h3 id='notifications-status'><FormattedMessage id='notifications.column_settings.admin.sign_up' defaultMessage='New sign-ups:' /></h3>
@ -237,7 +240,7 @@ class ColumnSettings extends PureComponent {
</section> </section>
)} )}
{((this.props.identity.permissions & PERMISSION_MANAGE_REPORTS) === PERMISSION_MANAGE_REPORTS) && ( {((this.context.identity.permissions & PERMISSION_MANAGE_REPORTS) === PERMISSION_MANAGE_REPORTS) && (
<section role='group' aria-labelledby='notifications-admin-report'> <section role='group' aria-labelledby='notifications-admin-report'>
<h3 id='notifications-status'><FormattedMessage id='notifications.column_settings.admin.report' defaultMessage='New reports:' /></h3> <h3 id='notifications-status'><FormattedMessage id='notifications.column_settings.admin.report' defaultMessage='New reports:' /></h3>
@ -254,5 +257,3 @@ class ColumnSettings extends PureComponent {
} }
} }
export default withIdentity(ColumnSettings);

View file

@ -19,7 +19,6 @@ import NotificationsIcon from '@/material-icons/400-24px/notifications-fill.svg?
import { compareId } from 'flavours/glitch/compare_id'; import { compareId } from 'flavours/glitch/compare_id';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { NotSignedInIndicator } from 'flavours/glitch/components/not_signed_in_indicator'; import { NotSignedInIndicator } from 'flavours/glitch/components/not_signed_in_indicator';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { submitMarkers } from '../../actions/markers'; import { submitMarkers } from '../../actions/markers';
@ -94,8 +93,12 @@ const mapDispatchToProps = dispatch => ({
}); });
class Notifications extends PureComponent { class Notifications extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
columnId: PropTypes.string, columnId: PropTypes.string,
notifications: ImmutablePropTypes.list.isRequired, notifications: ImmutablePropTypes.list.isRequired,
showFilterBar: PropTypes.bool.isRequired, showFilterBar: PropTypes.bool.isRequired,
@ -222,7 +225,7 @@ class Notifications extends PureComponent {
const { animatingNCD } = this.state; const { animatingNCD } = this.state;
const pinned = !!columnId; const pinned = !!columnId;
const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. When other people interact with you, you will see it here." />; const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. When other people interact with you, you will see it here." />;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
let scrollableContent = null; let scrollableContent = null;
@ -370,4 +373,4 @@ class Notifications extends PureComponent {
} }
export default connect(mapStateToProps, mapDispatchToProps)(withIdentity(injectIntl(Notifications))); export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(Notifications));

View file

@ -18,7 +18,6 @@ import { replyCompose } from 'flavours/glitch/actions/compose';
import { reblog, favourite, unreblog, unfavourite } from 'flavours/glitch/actions/interactions'; import { reblog, favourite, unreblog, unfavourite } from 'flavours/glitch/actions/interactions';
import { openModal } from 'flavours/glitch/actions/modal'; import { openModal } from 'flavours/glitch/actions/modal';
import { IconButton } from 'flavours/glitch/components/icon_button'; import { IconButton } from 'flavours/glitch/components/icon_button';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { me, boostModal } from 'flavours/glitch/initial_state'; import { me, boostModal } from 'flavours/glitch/initial_state';
import { makeGetStatus } from 'flavours/glitch/selectors'; import { makeGetStatus } from 'flavours/glitch/selectors';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
@ -49,8 +48,12 @@ const makeMapStateToProps = () => {
}; };
class Footer extends ImmutablePureComponent { class Footer extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
statusId: PropTypes.string.isRequired, statusId: PropTypes.string.isRequired,
status: ImmutablePropTypes.map.isRequired, status: ImmutablePropTypes.map.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
@ -74,7 +77,7 @@ class Footer extends ImmutablePureComponent {
handleReplyClick = () => { handleReplyClick = () => {
const { dispatch, askReplyConfirmation, status, intl } = this.props; const { dispatch, askReplyConfirmation, status, intl } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
if (askReplyConfirmation) { if (askReplyConfirmation) {
@ -103,7 +106,7 @@ class Footer extends ImmutablePureComponent {
handleFavouriteClick = () => { handleFavouriteClick = () => {
const { dispatch, status } = this.props; const { dispatch, status } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
if (status.get('favourited')) { if (status.get('favourited')) {
@ -130,7 +133,7 @@ class Footer extends ImmutablePureComponent {
handleReblogClick = e => { handleReblogClick = e => {
const { dispatch, status } = this.props; const { dispatch, status } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
@ -233,4 +236,4 @@ class Footer extends ImmutablePureComponent {
} }
export default connect(makeMapStateToProps)(withIdentity(withRouter(injectIntl(Footer)))); export default connect(makeMapStateToProps)(withRouter(injectIntl(Footer)));

View file

@ -9,7 +9,6 @@ import { connect } from 'react-redux';
import PublicIcon from '@/material-icons/400-24px/public.svg?react'; import PublicIcon from '@/material-icons/400-24px/public.svg?react';
import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner'; import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { domain } from 'flavours/glitch/initial_state'; import { domain } from 'flavours/glitch/initial_state';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
@ -45,12 +44,16 @@ const mapStateToProps = (state, { columnId }) => {
}; };
class PublicTimeline extends PureComponent { class PublicTimeline extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static defaultProps = { static defaultProps = {
onlyMedia: false, onlyMedia: false,
}; };
static propTypes = { static propTypes = {
identity: identityContextPropShape,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
columnId: PropTypes.string, columnId: PropTypes.string,
@ -83,7 +86,7 @@ class PublicTimeline extends PureComponent {
componentDidMount () { componentDidMount () {
const { dispatch, onlyMedia, onlyRemote, allowLocalOnly } = this.props; const { dispatch, onlyMedia, onlyRemote, allowLocalOnly } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
dispatch(expandPublicTimeline({ onlyMedia, onlyRemote, allowLocalOnly })); dispatch(expandPublicTimeline({ onlyMedia, onlyRemote, allowLocalOnly }));
if (signedIn) { if (signedIn) {
@ -92,7 +95,7 @@ class PublicTimeline extends PureComponent {
} }
componentDidUpdate (prevProps) { componentDidUpdate (prevProps) {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (prevProps.onlyMedia !== this.props.onlyMedia || prevProps.onlyRemote !== this.props.onlyRemote || prevProps.allowLocalOnly !== this.props.allowLocalOnly) { if (prevProps.onlyMedia !== this.props.onlyMedia || prevProps.onlyRemote !== this.props.onlyRemote || prevProps.allowLocalOnly !== this.props.allowLocalOnly) {
const { dispatch, onlyMedia, onlyRemote, allowLocalOnly } = this.props; const { dispatch, onlyMedia, onlyRemote, allowLocalOnly } = this.props;
@ -167,4 +170,4 @@ class PublicTimeline extends PureComponent {
} }
export default connect(mapStateToProps)(withIdentity(injectIntl(PublicTimeline))); export default connect(mapStateToProps)(injectIntl(PublicTimeline));

View file

@ -21,7 +21,6 @@ import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react'; import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react'; import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react'; import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links'; import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
@ -63,8 +62,12 @@ const messages = defineMessages({
}); });
class ActionBar extends PureComponent { class ActionBar extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
status: ImmutablePropTypes.map.isRequired, status: ImmutablePropTypes.map.isRequired,
onReply: PropTypes.func.isRequired, onReply: PropTypes.func.isRequired,
onReblog: PropTypes.func.isRequired, onReblog: PropTypes.func.isRequired,
@ -162,7 +165,7 @@ class ActionBar extends PureComponent {
render () { render () {
const { status, intl } = this.props; const { status, intl } = this.props;
const { signedIn, permissions } = this.props.identity; const { signedIn, permissions } = this.context.identity;
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility')); const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
const pinnableStatus = ['public', 'unlisted', 'private'].includes(status.get('visibility')); const pinnableStatus = ['public', 'unlisted', 'private'].includes(status.get('visibility'));
@ -273,4 +276,4 @@ class ActionBar extends PureComponent {
} }
export default withRouter(withIdentity(injectIntl(ActionBar))); export default withRouter(injectIntl(ActionBar));

View file

@ -15,7 +15,6 @@ import { getHashtagBarForStatus } from 'flavours/glitch/components/hashtag_bar';
import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder'; import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder';
import { VisibilityIcon } from 'flavours/glitch/components/visibility_icon'; import { VisibilityIcon } from 'flavours/glitch/components/visibility_icon';
import PollContainer from 'flavours/glitch/containers/poll_container'; import PollContainer from 'flavours/glitch/containers/poll_container';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
import { Avatar } from '../../../components/avatar'; import { Avatar } from '../../../components/avatar';
@ -30,8 +29,12 @@ import Video from '../../video';
import Card from './card'; import Card from './card';
class DetailedStatus extends ImmutablePureComponent { class DetailedStatus extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
status: ImmutablePropTypes.map, status: ImmutablePropTypes.map,
settings: ImmutablePropTypes.map.isRequired, settings: ImmutablePropTypes.map.isRequired,
onOpenMedia: PropTypes.func.isRequired, onOpenMedia: PropTypes.func.isRequired,
@ -316,7 +319,7 @@ class DetailedStatus extends ImmutablePureComponent {
reactions={status.get('reactions')} reactions={status.get('reactions')}
addReaction={this.props.onReactionAdd} addReaction={this.props.onReactionAdd}
removeReaction={this.props.onReactionRemove} removeReaction={this.props.onReactionRemove}
canReact={this.props.identity.signedIn} canReact={this.context.identity.signedIn}
/> />
<div className='detailed-status__meta'> <div className='detailed-status__meta'>
@ -345,4 +348,4 @@ class DetailedStatus extends ImmutablePureComponent {
} }
export default withRouter(withIdentity(DetailedStatus)); export default withRouter(DetailedStatus);

View file

@ -21,7 +21,6 @@ import { Icon } from 'flavours/glitch/components/icon';
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator'; import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
import ScrollContainer from 'flavours/glitch/containers/scroll_container'; import ScrollContainer from 'flavours/glitch/containers/scroll_container';
import BundleColumnError from 'flavours/glitch/features/ui/components/bundle_column_error'; import BundleColumnError from 'flavours/glitch/features/ui/components/bundle_column_error';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { autoUnfoldCW } from 'flavours/glitch/utils/content_warning'; import { autoUnfoldCW } from 'flavours/glitch/utils/content_warning';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
@ -188,8 +187,12 @@ const titleFromStatus = (intl, status) => {
}; };
class Status extends ImmutablePureComponent { class Status extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
params: PropTypes.object.isRequired, params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
status: ImmutablePropTypes.map, status: ImmutablePropTypes.map,
@ -276,7 +279,7 @@ class Status extends ImmutablePureComponent {
handleFavouriteClick = (status, e) => { handleFavouriteClick = (status, e) => {
const { dispatch } = this.props; const { dispatch } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
if (status.get('favourited')) { if (status.get('favourited')) {
@ -307,8 +310,8 @@ class Status extends ImmutablePureComponent {
}; };
handleReactionAdd = (statusId, name, url) => { handleReactionAdd = (statusId, name, url) => {
const { dispatch, identity } = this.props; const { dispatch } = this.props;
const { signedIn } = identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
dispatch(addReaction(statusId, name, url)); dispatch(addReaction(statusId, name, url));
@ -329,7 +332,7 @@ class Status extends ImmutablePureComponent {
handleReplyClick = (status) => { handleReplyClick = (status) => {
const { askReplyConfirmation, dispatch, intl } = this.props; const { askReplyConfirmation, dispatch, intl } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
if (askReplyConfirmation) { if (askReplyConfirmation) {
@ -369,7 +372,7 @@ class Status extends ImmutablePureComponent {
handleReblogClick = (status, e) => { handleReblogClick = (status, e) => {
const { settings, dispatch } = this.props; const { settings, dispatch } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
if (settings.get('confirm_boost_missing_media_description') && status.get('media_attachments').some(item => !item.get('description')) && !status.get('reblogged')) { if (settings.get('confirm_boost_missing_media_description') && status.get('media_attachments').some(item => !item.get('description')) && !status.get('reblogged')) {
@ -807,4 +810,4 @@ class Status extends ImmutablePureComponent {
} }
export default withRouter(injectIntl(connect(makeMapStateToProps)(withIdentity(Status)))); export default withRouter(injectIntl(connect(makeMapStateToProps)(Status)));

View file

@ -7,13 +7,16 @@ import { mountCompose, unmountCompose } from 'flavours/glitch/actions/compose';
import ServerBanner from 'flavours/glitch/components/server_banner'; import ServerBanner from 'flavours/glitch/components/server_banner';
import ComposeFormContainer from 'flavours/glitch/features/compose/containers/compose_form_container'; import ComposeFormContainer from 'flavours/glitch/features/compose/containers/compose_form_container';
import SearchContainer from 'flavours/glitch/features/compose/containers/search_container'; import SearchContainer from 'flavours/glitch/features/compose/containers/search_container';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import LinkFooter from './link_footer'; import LinkFooter from './link_footer';
class ComposePanel extends PureComponent { class ComposePanel extends PureComponent {
static contextTypes = {
identity: PropTypes.object.isRequired,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
}; };
@ -28,7 +31,7 @@ class ComposePanel extends PureComponent {
} }
render() { render() {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
return ( return (
<div className='compose-panel'> <div className='compose-panel'>
@ -52,4 +55,4 @@ class ComposePanel extends PureComponent {
} }
export default connect()(withIdentity(ComposePanel)); export default connect()(ComposePanel);

View file

@ -14,7 +14,6 @@ import { Avatar } from 'flavours/glitch/components/avatar';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { WordmarkLogo, SymbolLogo } from 'flavours/glitch/components/logo'; import { WordmarkLogo, SymbolLogo } from 'flavours/glitch/components/logo';
import { Permalink } from 'flavours/glitch/components/permalink'; import { Permalink } from 'flavours/glitch/components/permalink';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { registrationsOpen, me, sso_redirect } from 'flavours/glitch/initial_state'; import { registrationsOpen, me, sso_redirect } from 'flavours/glitch/initial_state';
const Account = connect(state => ({ const Account = connect(state => ({
@ -43,8 +42,12 @@ const mapDispatchToProps = (dispatch) => ({
}); });
class Header extends PureComponent { class Header extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
openClosedRegistrationsModal: PropTypes.func, openClosedRegistrationsModal: PropTypes.func,
location: PropTypes.object, location: PropTypes.object,
signupUrl: PropTypes.string.isRequired, signupUrl: PropTypes.string.isRequired,
@ -58,7 +61,7 @@ class Header extends PureComponent {
} }
render () { render () {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
const { location, openClosedRegistrationsModal, signupUrl, intl } = this.props; const { location, openClosedRegistrationsModal, signupUrl, intl } = this.props;
let content; let content;
@ -119,4 +122,4 @@ class Header extends PureComponent {
} }
export default injectIntl(withRouter(withIdentity(connect(mapStateToProps, mapDispatchToProps)(Header)))); export default injectIntl(withRouter(connect(mapStateToProps, mapDispatchToProps)(Header)));

View file

@ -8,7 +8,6 @@ import { Link } from 'react-router-dom';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { openModal } from 'flavours/glitch/actions/modal'; import { openModal } from 'flavours/glitch/actions/modal';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { domain, version, source_url, statusPageUrl, profile_directory as profileDirectory } from 'flavours/glitch/initial_state'; import { domain, version, source_url, statusPageUrl, profile_directory as profileDirectory } from 'flavours/glitch/initial_state';
import { PERMISSION_INVITE_USERS } from 'flavours/glitch/permissions'; import { PERMISSION_INVITE_USERS } from 'flavours/glitch/permissions';
import { logOut } from 'flavours/glitch/utils/log_out'; import { logOut } from 'flavours/glitch/utils/log_out';
@ -33,8 +32,12 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
}); });
class LinkFooter extends PureComponent { class LinkFooter extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
multiColumn: PropTypes.bool, multiColumn: PropTypes.bool,
onLogout: PropTypes.func.isRequired, onLogout: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
@ -50,7 +53,7 @@ class LinkFooter extends PureComponent {
}; };
render () { render () {
const { signedIn, permissions } = this.props.identity; const { signedIn, permissions } = this.context.identity;
const { multiColumn } = this.props; const { multiColumn } = this.props;
const canInvite = signedIn && ((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS); const canInvite = signedIn && ((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS);
@ -105,4 +108,4 @@ class LinkFooter extends PureComponent {
} }
export default injectIntl(withIdentity(connect(null, mapDispatchToProps)(LinkFooter))); export default injectIntl(connect(null, mapDispatchToProps)(LinkFooter));

View file

@ -30,7 +30,6 @@ import StarIcon from '@/material-icons/400-24px/star.svg?react';
import { fetchFollowRequests } from 'flavours/glitch/actions/accounts'; import { fetchFollowRequests } from 'flavours/glitch/actions/accounts';
import { IconWithBadge } from 'flavours/glitch/components/icon_with_badge'; import { IconWithBadge } from 'flavours/glitch/components/icon_with_badge';
import { NavigationPortal } from 'flavours/glitch/components/navigation_portal'; import { NavigationPortal } from 'flavours/glitch/components/navigation_portal';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { timelinePreview, trendsEnabled } from 'flavours/glitch/initial_state'; import { timelinePreview, trendsEnabled } from 'flavours/glitch/initial_state';
import { transientSingleColumn } from 'flavours/glitch/is_mobile'; import { transientSingleColumn } from 'flavours/glitch/is_mobile';
import { preferencesLink } from 'flavours/glitch/utils/backend_links'; import { preferencesLink } from 'flavours/glitch/utils/backend_links';
@ -99,8 +98,12 @@ const FollowRequestsLink = () => {
}; };
class NavigationPanel extends Component { class NavigationPanel extends Component {
static contextTypes = {
identity: PropTypes.object.isRequired,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
onOpenSettings: PropTypes.func, onOpenSettings: PropTypes.func,
}; };
@ -111,7 +114,7 @@ class NavigationPanel extends Component {
render () { render () {
const { intl, onOpenSettings } = this.props; const { intl, onOpenSettings } = this.props;
const { signedIn, disabledAccountId } = this.props.identity; const { signedIn, disabledAccountId } = this.context.identity;
let banner = undefined; let banner = undefined;
@ -185,4 +188,4 @@ class NavigationPanel extends Component {
} }
export default injectIntl(withIdentity(NavigationPanel)); export default injectIntl(NavigationPanel);

View file

@ -17,7 +17,6 @@ import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'flavour
import { INTRODUCTION_VERSION } from 'flavours/glitch/actions/onboarding'; import { INTRODUCTION_VERSION } from 'flavours/glitch/actions/onboarding';
import { Permalink } from 'flavours/glitch/components/permalink'; import { Permalink } from 'flavours/glitch/components/permalink';
import { PictureInPicture } from 'flavours/glitch/features/picture_in_picture'; import { PictureInPicture } from 'flavours/glitch/features/picture_in_picture';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { layoutFromWindow } from 'flavours/glitch/is_mobile'; import { layoutFromWindow } from 'flavours/glitch/is_mobile';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
@ -130,8 +129,12 @@ const keyMap = {
}; };
class SwitchingColumnsArea extends PureComponent { class SwitchingColumnsArea extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
children: PropTypes.node, children: PropTypes.node,
location: PropTypes.object, location: PropTypes.object,
singleColumn: PropTypes.bool, singleColumn: PropTypes.bool,
@ -166,7 +169,7 @@ class SwitchingColumnsArea extends PureComponent {
render () { render () {
const { children, singleColumn } = this.props; const { children, singleColumn } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
const pathName = this.props.location.pathname; const pathName = this.props.location.pathname;
let redirect; let redirect;
@ -259,8 +262,12 @@ class SwitchingColumnsArea extends PureComponent {
} }
class UI extends PureComponent { class UI extends PureComponent {
static contextTypes = {
identity: PropTypes.object.isRequired,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
children: PropTypes.node, children: PropTypes.node,
isWide: PropTypes.bool, isWide: PropTypes.bool,
@ -316,7 +323,7 @@ class UI extends PureComponent {
this.dragTargets.push(e.target); this.dragTargets.push(e.target);
} }
if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore && this.props.identity.signedIn) { if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore && this.context.identity.signedIn) {
this.setState({ draggingOver: true }); this.setState({ draggingOver: true });
} }
}; };
@ -344,7 +351,7 @@ class UI extends PureComponent {
this.setState({ draggingOver: false }); this.setState({ draggingOver: false });
this.dragTargets = []; this.dragTargets = [];
if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore && this.props.identity.signedIn) { if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore && this.context.identity.signedIn) {
this.props.dispatch(uploadCompose(e.dataTransfer.files)); this.props.dispatch(uploadCompose(e.dataTransfer.files));
} }
}; };
@ -396,7 +403,7 @@ class UI extends PureComponent {
}; };
componentDidMount () { componentDidMount () {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
window.addEventListener('beforeunload', this.handleBeforeUnload, false); window.addEventListener('beforeunload', this.handleBeforeUnload, false);
window.addEventListener('resize', this.handleResize, { passive: true }); window.addEventListener('resize', this.handleResize, { passive: true });
@ -642,7 +649,7 @@ class UI extends PureComponent {
<Header /> <Header />
<SwitchingColumnsArea identity={this.props.identity} location={location} singleColumn={layout === 'mobile' || layout === 'single-column'}> <SwitchingColumnsArea location={location} singleColumn={layout === 'mobile' || layout === 'single-column'}>
{children} {children}
</SwitchingColumnsArea> </SwitchingColumnsArea>
@ -658,4 +665,4 @@ class UI extends PureComponent {
} }
export default connect(mapStateToProps)(injectIntl(withRouter(withIdentity(UI)))); export default connect(mapStateToProps)(injectIntl(withRouter(UI)));

View file

@ -0,0 +1,16 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
export class IdentityConsumer extends PureComponent {
static contextTypes = {
identity: PropTypes.object
};
static propTypes = {
children: PropTypes.func.isRequired
};
render() {
return this.props.children(this.context.identity);
}
}

View file

@ -1,74 +0,0 @@
import PropTypes from 'prop-types';
import { createContext, useContext } from 'react';
import hoistStatics from 'hoist-non-react-statics';
import type { InitialState } from 'flavours/glitch/initial_state';
export interface IdentityContextType {
signedIn: boolean;
accountId: string | undefined;
disabledAccountId: string | undefined;
accessToken: string | undefined;
permissions: number;
}
export const identityContextPropShape = PropTypes.shape({
signedIn: PropTypes.bool.isRequired,
accountId: PropTypes.string,
disabledAccountId: PropTypes.string,
accessToken: PropTypes.string,
}).isRequired;
export const createIdentityContext = (state: InitialState) => ({
signedIn: !!state.meta.me,
accountId: state.meta.me,
disabledAccountId: state.meta.disabled_account_id,
accessToken: state.meta.access_token,
permissions: state.role?.permissions ?? 0,
});
export const IdentityContext = createContext<IdentityContextType>({
signedIn: false,
permissions: 0,
accountId: undefined,
disabledAccountId: undefined,
accessToken: undefined,
});
export const useIdentity = () => useContext(IdentityContext);
export interface IdentityProps {
ref?: unknown;
wrappedComponentRef?: unknown;
}
/* Injects an `identity` props into the wrapped component to be able to use the new context in class components */
export function withIdentity<
ComponentType extends React.ComponentType<IdentityProps>,
>(Component: ComponentType) {
const displayName = `withIdentity(${Component.displayName ?? Component.name})`;
const C = (props: React.ComponentProps<ComponentType>) => {
const { wrappedComponentRef, ...remainingProps } = props;
return (
<IdentityContext.Consumer>
{(context) => {
return (
// @ts-expect-error - Dynamic covariant generic components are tough to type.
<Component
{...remainingProps}
identity={context}
ref={wrappedComponentRef}
/>
);
}}
</IdentityContext.Consumer>
);
};
C.displayName = displayName;
C.WrappedComponent = Component;
return hoistStatics(C, Component);
}

View file

@ -52,22 +52,12 @@
* @property {string} default_content_type * @property {string} default_content_type
*/ */
/**
* @typedef Role
* @property {string} id
* @property {string} name
* @property {string} permissions
* @property {string} color
* @property {boolean} highlighted
*/
/** /**
* @typedef InitialState * @typedef InitialState
* @property {Record<string, import("./api_types/accounts").ApiAccountJSON>} accounts * @property {Record<string, import("./api_types/accounts").ApiAccountJSON>} accounts
* @property {InitialStateLanguage[]} languages * @property {InitialStateLanguage[]} languages
* @property {boolean=} critical_updates_pending * @property {boolean=} critical_updates_pending
* @property {InitialStateMeta} meta * @property {InitialStateMeta} meta
* @property {Role?} role
* @property {object} local_settings * @property {object} local_settings
* @property {number} max_feed_hashtags * @property {number} max_feed_hashtags
* @property {number} poll_limits * @property {number} poll_limits

View file

@ -4621,10 +4621,6 @@ a.status-card {
&:hover { &:hover {
color: $primary-text-color; color: $primary-text-color;
} }
.icon {
transform: rotate(60deg);
}
} }
&:disabled { &:disabled {
@ -4672,10 +4668,6 @@ a.status-card {
padding: 0; padding: 0;
} }
.no-reduce-motion .column-header__button .icon {
transition: transform 150ms ease-in-out;
}
.column-header__collapsible { .column-header__collapsible {
max-height: 70vh; max-height: 70vh;
overflow: hidden; overflow: hidden;

View file

@ -20,7 +20,7 @@ export function changeSetting(path, value) {
} }
const debouncedSave = debounce((dispatch, getState) => { const debouncedSave = debounce((dispatch, getState) => {
if (getState().getIn(['settings', 'saved']) || !getState().getIn(['meta', 'me'])) { if (getState().getIn(['settings', 'saved'])) {
return; return;
} }

View file

@ -172,6 +172,7 @@ Account.propTypes = {
onBlock: PropTypes.func, onBlock: PropTypes.func,
onMute: PropTypes.func, onMute: PropTypes.func,
onMuteNotifications: PropTypes.func, onMuteNotifications: PropTypes.func,
intl: PropTypes.object.isRequired,
hidden: PropTypes.bool, hidden: PropTypes.bool,
minimal: PropTypes.bool, minimal: PropTypes.bool,
defaultAction: PropTypes.string, defaultAction: PropTypes.string,

View file

@ -14,10 +14,8 @@ import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import SettingsIcon from '@/material-icons/400-24px/settings.svg?react'; import SettingsIcon from '@/material-icons/400-24px/settings.svg?react';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { ButtonInTabsBar } from 'mastodon/features/ui/util/columns_context'; import { ButtonInTabsBar } from 'mastodon/features/ui/util/columns_context';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
import { useAppHistory } from './router'; import { useAppHistory } from './router';
const messages = defineMessages({ const messages = defineMessages({
@ -53,8 +51,12 @@ BackButton.propTypes = {
}; };
class ColumnHeader extends PureComponent { class ColumnHeader extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
title: PropTypes.node, title: PropTypes.node,
icon: PropTypes.string, icon: PropTypes.string,
@ -169,7 +171,7 @@ class ColumnHeader extends PureComponent {
); );
} }
if (this.props.identity.signedIn && (children || (multiColumn && this.props.onPin))) { if (this.context.identity.signedIn && (children || (multiColumn && this.props.onPin))) {
collapseButton = ( collapseButton = (
<button <button
className={collapsibleButtonClassName} className={collapsibleButtonClassName}
@ -230,4 +232,4 @@ class ColumnHeader extends PureComponent {
} }
export default injectIntl(withIdentity(withRouter(ColumnHeader))); export default injectIntl(withRouter(ColumnHeader));

View file

@ -14,7 +14,6 @@ import CheckIcon from '@/material-icons/400-24px/check.svg?react';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import emojify from 'mastodon/features/emoji/emoji'; import emojify from 'mastodon/features/emoji/emoji';
import Motion from 'mastodon/features/ui/util/optional_motion'; import Motion from 'mastodon/features/ui/util/optional_motion';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { RelativeTimestamp } from './relative_timestamp'; import { RelativeTimestamp } from './relative_timestamp';
@ -39,8 +38,12 @@ const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => {
}, {}); }, {});
class Poll extends ImmutablePureComponent { class Poll extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
poll: ImmutablePropTypes.map, poll: ImmutablePropTypes.map,
lang: PropTypes.string, lang: PropTypes.string,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
@ -232,7 +235,7 @@ class Poll extends ImmutablePureComponent {
</ul> </ul>
<div className='poll__footer'> <div className='poll__footer'>
{!showResults && <button className='button button-secondary' disabled={disabled || !this.props.identity.signedIn} onClick={this.handleVote}><FormattedMessage id='poll.vote' defaultMessage='Vote' /></button>} {!showResults && <button className='button button-secondary' disabled={disabled || !this.context.identity.signedIn} onClick={this.handleVote}><FormattedMessage id='poll.vote' defaultMessage='Vote' /></button>}
{!showResults && <><button className='poll__link' onClick={this.handleReveal}><FormattedMessage id='poll.reveal' defaultMessage='See results' /></button> · </>} {!showResults && <><button className='poll__link' onClick={this.handleReveal}><FormattedMessage id='poll.reveal' defaultMessage='See results' /></button> · </>}
{showResults && !this.props.disabled && <><button className='poll__link' onClick={this.handleRefresh}><FormattedMessage id='poll.refresh' defaultMessage='Refresh' /></button> · </>} {showResults && !this.props.disabled && <><button className='poll__link' onClick={this.handleRefresh}><FormattedMessage id='poll.refresh' defaultMessage='Refresh' /></button> · </>}
{votesCount} {votesCount}
@ -244,4 +247,4 @@ class Poll extends ImmutablePureComponent {
} }
export default injectIntl(withIdentity(Poll)); export default injectIntl(Poll);

View file

@ -22,7 +22,6 @@ import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react'; import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react'; import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react'; import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
@ -75,8 +74,12 @@ const mapStateToProps = (state, { status }) => ({
}); });
class StatusActionBar extends ImmutablePureComponent { class StatusActionBar extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
status: ImmutablePropTypes.map.isRequired, status: ImmutablePropTypes.map.isRequired,
relationship: ImmutablePropTypes.record, relationship: ImmutablePropTypes.record,
onReply: PropTypes.func, onReply: PropTypes.func,
@ -115,7 +118,7 @@ class StatusActionBar extends ImmutablePureComponent {
]; ];
handleReplyClick = () => { handleReplyClick = () => {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
this.props.onReply(this.props.status, this.props.history); this.props.onReply(this.props.status, this.props.history);
@ -133,7 +136,7 @@ class StatusActionBar extends ImmutablePureComponent {
}; };
handleFavouriteClick = () => { handleFavouriteClick = () => {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
this.props.onFavourite(this.props.status); this.props.onFavourite(this.props.status);
@ -143,7 +146,7 @@ class StatusActionBar extends ImmutablePureComponent {
}; };
handleReblogClick = e => { handleReblogClick = e => {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
this.props.onReblog(this.props.status, e); this.props.onReblog(this.props.status, e);
@ -247,7 +250,7 @@ class StatusActionBar extends ImmutablePureComponent {
render () { render () {
const { status, relationship, intl, withDismiss, withCounters, scrollKey } = this.props; const { status, relationship, intl, withDismiss, withCounters, scrollKey } = this.props;
const { signedIn, permissions } = this.props.identity; const { signedIn, permissions } = this.context.identity;
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility')); const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
const pinnableStatus = ['public', 'unlisted', 'private'].includes(status.get('visibility')); const pinnableStatus = ['public', 'unlisted', 'private'].includes(status.get('visibility'));
@ -407,4 +410,4 @@ class StatusActionBar extends ImmutablePureComponent {
} }
export default withRouter(withIdentity(connect(mapStateToProps)(injectIntl(StatusActionBar)))); export default withRouter(connect(mapStateToProps)(injectIntl(StatusActionBar)));

View file

@ -12,10 +12,8 @@ import { connect } from 'react-redux';
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react'; import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import PollContainer from 'mastodon/containers/poll_container'; import PollContainer from 'mastodon/containers/poll_container';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state'; import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state';
const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top) const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
/** /**
@ -69,8 +67,12 @@ const mapStateToProps = state => ({
}); });
class StatusContent extends PureComponent { class StatusContent extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
status: ImmutablePropTypes.map.isRequired, status: ImmutablePropTypes.map.isRequired,
statusContent: PropTypes.string, statusContent: PropTypes.string,
expanded: PropTypes.bool, expanded: PropTypes.bool,
@ -243,7 +245,7 @@ class StatusContent extends PureComponent {
const renderReadMore = this.props.onClick && status.get('collapsed'); const renderReadMore = this.props.onClick && status.get('collapsed');
const contentLocale = intl.locale.replace(/[_-].*/, ''); const contentLocale = intl.locale.replace(/[_-].*/, '');
const targetLanguages = this.props.languages?.get(status.get('language') || 'und'); const targetLanguages = this.props.languages?.get(status.get('language') || 'und');
const renderTranslate = this.props.onTranslate && this.props.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('search_index').trim().length > 0 && targetLanguages?.includes(contentLocale); const renderTranslate = this.props.onTranslate && this.context.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('search_index').trim().length > 0 && targetLanguages?.includes(contentLocale);
const content = { __html: statusContent ?? getStatusContent(status) }; const content = { __html: statusContent ?? getStatusContent(status) };
const spoilerContent = { __html: status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml') }; const spoilerContent = { __html: status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml') };
@ -326,4 +328,4 @@ class StatusContent extends PureComponent {
} }
export default withRouter(withIdentity(connect(mapStateToProps)(injectIntl(StatusContent)))); export default withRouter(connect(mapStateToProps)(injectIntl(StatusContent)));

View file

@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react'; import { PureComponent } from 'react';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
@ -13,7 +14,6 @@ import { connectUserStream } from 'mastodon/actions/streaming';
import ErrorBoundary from 'mastodon/components/error_boundary'; import ErrorBoundary from 'mastodon/components/error_boundary';
import { Router } from 'mastodon/components/router'; import { Router } from 'mastodon/components/router';
import UI from 'mastodon/features/ui'; import UI from 'mastodon/features/ui';
import { IdentityContext, createIdentityContext } from 'mastodon/identity_context';
import initialState, { title as siteTitle } from 'mastodon/initial_state'; import initialState, { title as siteTitle } from 'mastodon/initial_state';
import { IntlProvider } from 'mastodon/locales'; import { IntlProvider } from 'mastodon/locales';
import { store } from 'mastodon/store'; import { store } from 'mastodon/store';
@ -28,9 +28,33 @@ if (initialState.meta.me) {
store.dispatch(fetchCustomEmojis()); store.dispatch(fetchCustomEmojis());
} }
const createIdentityContext = state => ({
signedIn: !!state.meta.me,
accountId: state.meta.me,
disabledAccountId: state.meta.disabled_account_id,
accessToken: state.meta.access_token,
permissions: state.role ? state.role.permissions : 0,
});
export default class Mastodon extends PureComponent { export default class Mastodon extends PureComponent {
static childContextTypes = {
identity: PropTypes.shape({
signedIn: PropTypes.bool.isRequired,
accountId: PropTypes.string,
disabledAccountId: PropTypes.string,
accessToken: PropTypes.string,
}).isRequired,
};
identity = createIdentityContext(initialState); identity = createIdentityContext(initialState);
getChildContext() {
return {
identity: this.identity,
};
}
componentDidMount() { componentDidMount() {
if (this.identity.signedIn) { if (this.identity.signedIn) {
this.disconnect = store.dispatch(connectUserStream()); this.disconnect = store.dispatch(connectUserStream());
@ -50,7 +74,6 @@ export default class Mastodon extends PureComponent {
render () { render () {
return ( return (
<IdentityContext.Provider value={this.identity}>
<IntlProvider> <IntlProvider>
<ReduxProvider store={store}> <ReduxProvider store={store}>
<ErrorBoundary> <ErrorBoundary>
@ -64,7 +87,6 @@ export default class Mastodon extends PureComponent {
</ErrorBoundary> </ErrorBoundary>
</ReduxProvider> </ReduxProvider>
</IntlProvider> </IntlProvider>
</IdentityContext.Provider>
); );
} }

View file

@ -25,7 +25,6 @@ import { IconButton } from 'mastodon/components/icon_button';
import { LoadingIndicator } from 'mastodon/components/loading_indicator'; import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import { ShortNumber } from 'mastodon/components/short_number'; import { ShortNumber } from 'mastodon/components/short_number';
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container'; import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { autoPlayGif, me, domain as localDomain } from 'mastodon/initial_state'; import { autoPlayGif, me, domain as localDomain } from 'mastodon/initial_state';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
@ -112,7 +111,6 @@ const dateFormatOptions = {
class Header extends ImmutablePureComponent { class Header extends ImmutablePureComponent {
static propTypes = { static propTypes = {
identity: identityContextPropShape,
account: ImmutablePropTypes.record, account: ImmutablePropTypes.record,
identity_props: ImmutablePropTypes.list, identity_props: ImmutablePropTypes.list,
onFollow: PropTypes.func.isRequired, onFollow: PropTypes.func.isRequired,
@ -138,6 +136,10 @@ class Header extends ImmutablePureComponent {
...WithRouterPropTypes, ...WithRouterPropTypes,
}; };
static contextTypes = {
identity: PropTypes.object,
};
setRef = c => { setRef = c => {
this.node = c; this.node = c;
}; };
@ -253,7 +255,7 @@ class Header extends ImmutablePureComponent {
render () { render () {
const { account, hidden, intl } = this.props; const { account, hidden, intl } = this.props;
const { signedIn, permissions } = this.props.identity; const { signedIn, permissions } = this.context.identity;
if (!account) { if (!account) {
return null; return null;
@ -514,4 +516,4 @@ class Header extends ImmutablePureComponent {
} }
export default withRouter(withIdentity(injectIntl(Header))); export default withRouter(injectIntl(Header));

View file

@ -9,7 +9,6 @@ import { connect } from 'react-redux';
import PeopleIcon from '@/material-icons/400-24px/group.svg?react'; import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
import { DismissableBanner } from 'mastodon/components/dismissable_banner'; import { DismissableBanner } from 'mastodon/components/dismissable_banner';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { domain } from 'mastodon/initial_state'; import { domain } from 'mastodon/initial_state';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
@ -39,12 +38,16 @@ const mapStateToProps = (state, { columnId }) => {
}; };
class CommunityTimeline extends PureComponent { class CommunityTimeline extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static defaultProps = { static defaultProps = {
onlyMedia: false, onlyMedia: false,
}; };
static propTypes = { static propTypes = {
identity: identityContextPropShape,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
columnId: PropTypes.string, columnId: PropTypes.string,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
@ -74,7 +77,7 @@ class CommunityTimeline extends PureComponent {
componentDidMount () { componentDidMount () {
const { dispatch, onlyMedia } = this.props; const { dispatch, onlyMedia } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
dispatch(expandCommunityTimeline({ onlyMedia })); dispatch(expandCommunityTimeline({ onlyMedia }));
@ -84,7 +87,7 @@ class CommunityTimeline extends PureComponent {
} }
componentDidUpdate (prevProps) { componentDidUpdate (prevProps) {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (prevProps.onlyMedia !== this.props.onlyMedia) { if (prevProps.onlyMedia !== this.props.onlyMedia) {
const { dispatch, onlyMedia } = this.props; const { dispatch, onlyMedia } = this.props;
@ -158,4 +161,4 @@ class CommunityTimeline extends PureComponent {
} }
export default withIdentity(connect(mapStateToProps)(injectIntl(CommunityTimeline))); export default connect(mapStateToProps)(injectIntl(CommunityTimeline));

View file

@ -12,7 +12,6 @@ import CancelIcon from '@/material-icons/400-24px/cancel-fill.svg?react';
import CloseIcon from '@/material-icons/400-24px/close.svg?react'; import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import SearchIcon from '@/material-icons/400-24px/search.svg?react'; import SearchIcon from '@/material-icons/400-24px/search.svg?react';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { domain, searchEnabled } from 'mastodon/initial_state'; import { domain, searchEnabled } from 'mastodon/initial_state';
import { HASHTAG_REGEX } from 'mastodon/utils/hashtags'; import { HASHTAG_REGEX } from 'mastodon/utils/hashtags';
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
@ -34,8 +33,12 @@ const labelForRecentSearch = search => {
}; };
class Search extends PureComponent { class Search extends PureComponent {
static contextTypes = {
identity: PropTypes.object.isRequired,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
value: PropTypes.string.isRequired, value: PropTypes.string.isRequired,
recent: ImmutablePropTypes.orderedSet, recent: ImmutablePropTypes.orderedSet,
submitted: PropTypes.bool, submitted: PropTypes.bool,
@ -273,7 +276,7 @@ class Search extends PureComponent {
} }
_calculateOptions (value) { _calculateOptions (value) {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
const trimmedValue = value.trim(); const trimmedValue = value.trim();
const options = []; const options = [];
@ -315,7 +318,7 @@ class Search extends PureComponent {
render () { render () {
const { intl, value, submitted, recent } = this.props; const { intl, value, submitted, recent } = this.props;
const { expanded, options, selectedOption } = this.state; const { expanded, options, selectedOption } = this.state;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
const hasValue = value.length > 0 || submitted; const hasValue = value.length > 0 || submitted;
@ -399,4 +402,4 @@ class Search extends PureComponent {
} }
export default withRouter(withIdentity(injectIntl(Search))); export default withRouter(injectIntl(Search));

View file

@ -13,7 +13,6 @@ import SearchIcon from '@/material-icons/400-24px/search.svg?react';
import Column from 'mastodon/components/column'; import Column from 'mastodon/components/column';
import ColumnHeader from 'mastodon/components/column_header'; import ColumnHeader from 'mastodon/components/column_header';
import Search from 'mastodon/features/compose/containers/search_container'; import Search from 'mastodon/features/compose/containers/search_container';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { trendsEnabled } from 'mastodon/initial_state'; import { trendsEnabled } from 'mastodon/initial_state';
import Links from './links'; import Links from './links';
@ -33,8 +32,12 @@ const mapStateToProps = state => ({
}); });
class Explore extends PureComponent { class Explore extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
multiColumn: PropTypes.bool, multiColumn: PropTypes.bool,
isSearching: PropTypes.bool, isSearching: PropTypes.bool,
@ -50,7 +53,7 @@ class Explore extends PureComponent {
render() { render() {
const { intl, multiColumn, isSearching } = this.props; const { intl, multiColumn, isSearching } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
return ( return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
@ -111,4 +114,4 @@ class Explore extends PureComponent {
} }
export default withIdentity(connect(mapStateToProps)(injectIntl(Explore))); export default connect(mapStateToProps)(injectIntl(Explore));

View file

@ -6,14 +6,13 @@ import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
import { useIdentity } from '@/mastodon/identity_context';
import PublicIcon from '@/material-icons/400-24px/public.svg?react'; import PublicIcon from '@/material-icons/400-24px/public.svg?react';
import { addColumn } from 'mastodon/actions/columns'; import { addColumn } from 'mastodon/actions/columns';
import { changeSetting } from 'mastodon/actions/settings'; import { changeSetting } from 'mastodon/actions/settings';
import { connectPublicStream, connectCommunityStream } from 'mastodon/actions/streaming'; import { connectPublicStream, connectCommunityStream } from 'mastodon/actions/streaming';
import { expandPublicTimeline, expandCommunityTimeline } from 'mastodon/actions/timelines'; import { expandPublicTimeline, expandCommunityTimeline } from 'mastodon/actions/timelines';
import { DismissableBanner } from 'mastodon/components/dismissable_banner'; import { DismissableBanner } from 'mastodon/components/dismissable_banner';
import { domain } from 'mastodon/initial_state'; import initialState, { domain } from 'mastodon/initial_state';
import { useAppDispatch, useAppSelector } from 'mastodon/store'; import { useAppDispatch, useAppSelector } from 'mastodon/store';
import Column from '../../components/column'; import Column from '../../components/column';
@ -25,6 +24,15 @@ const messages = defineMessages({
title: { id: 'column.firehose', defaultMessage: 'Live feeds' }, title: { id: 'column.firehose', defaultMessage: 'Live feeds' },
}); });
// TODO: use a proper React context later on
const useIdentity = () => ({
signedIn: !!initialState.meta.me,
accountId: initialState.meta.me,
disabledAccountId: initialState.meta.disabled_account_id,
accessToken: initialState.meta.access_token,
permissions: initialState.role ? initialState.role.permissions : 0,
});
const ColumnSettings = () => { const ColumnSettings = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const settings = useAppSelector((state) => state.getIn(['settings', 'firehose'])); const settings = useAppSelector((state) => state.getIn(['settings', 'firehose']));

View file

@ -24,7 +24,6 @@ import { fetchFollowRequests } from 'mastodon/actions/accounts';
import Column from 'mastodon/components/column'; import Column from 'mastodon/components/column';
import ColumnHeader from 'mastodon/components/column_header'; import ColumnHeader from 'mastodon/components/column_header';
import LinkFooter from 'mastodon/features/ui/components/link_footer'; import LinkFooter from 'mastodon/features/ui/components/link_footer';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { me, showTrends } from '../../initial_state'; import { me, showTrends } from '../../initial_state';
import { NavigationBar } from '../compose/components/navigation_bar'; import { NavigationBar } from '../compose/components/navigation_bar';
@ -76,8 +75,12 @@ const badgeDisplay = (number, limit) => {
}; };
class GettingStarted extends ImmutablePureComponent { class GettingStarted extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
myAccount: ImmutablePropTypes.record, myAccount: ImmutablePropTypes.record,
multiColumn: PropTypes.bool, multiColumn: PropTypes.bool,
@ -88,7 +91,7 @@ class GettingStarted extends ImmutablePureComponent {
componentDidMount () { componentDidMount () {
const { fetchFollowRequests } = this.props; const { fetchFollowRequests } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (!signedIn) { if (!signedIn) {
return; return;
@ -99,7 +102,7 @@ class GettingStarted extends ImmutablePureComponent {
render () { render () {
const { intl, myAccount, multiColumn, unreadFollowRequests } = this.props; const { intl, myAccount, multiColumn, unreadFollowRequests } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
const navItems = []; const navItems = [];
@ -164,4 +167,4 @@ class GettingStarted extends ImmutablePureComponent {
} }
export default withIdentity(connect(mapStateToProps, mapDispatchToProps)(injectIntl(GettingStarted))); export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(GettingStarted));

View file

@ -17,7 +17,6 @@ import { fetchHashtag, followHashtag, unfollowHashtag } from 'mastodon/actions/t
import { expandHashtagTimeline, clearTimeline } from 'mastodon/actions/timelines'; import { expandHashtagTimeline, clearTimeline } from 'mastodon/actions/timelines';
import Column from 'mastodon/components/column'; import Column from 'mastodon/components/column';
import ColumnHeader from 'mastodon/components/column_header'; import ColumnHeader from 'mastodon/components/column_header';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import StatusListContainer from '../ui/containers/status_list_container'; import StatusListContainer from '../ui/containers/status_list_container';
@ -30,10 +29,14 @@ const mapStateToProps = (state, props) => ({
}); });
class HashtagTimeline extends PureComponent { class HashtagTimeline extends PureComponent {
disconnects = []; disconnects = [];
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
params: PropTypes.object.isRequired, params: PropTypes.object.isRequired,
columnId: PropTypes.string, columnId: PropTypes.string,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
@ -91,7 +94,7 @@ class HashtagTimeline extends PureComponent {
}; };
_subscribe (dispatch, id, tags = {}, local) { _subscribe (dispatch, id, tags = {}, local) {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (!signedIn) { if (!signedIn) {
return; return;
@ -165,7 +168,7 @@ class HashtagTimeline extends PureComponent {
handleFollow = () => { handleFollow = () => {
const { dispatch, params, tag } = this.props; const { dispatch, params, tag } = this.props;
const { id } = params; const { id } = params;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (!signedIn) { if (!signedIn) {
return; return;
@ -182,7 +185,7 @@ class HashtagTimeline extends PureComponent {
const { hasUnread, columnId, multiColumn, tag } = this.props; const { hasUnread, columnId, multiColumn, tag } = this.props;
const { id, local } = this.props.params; const { id, local } = this.props.params;
const pinned = !!columnId; const pinned = !!columnId;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
return ( return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={`#${id}`}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={`#${id}`}>
@ -222,4 +225,4 @@ class HashtagTimeline extends PureComponent {
} }
export default connect(mapStateToProps)(withIdentity(HashtagTimeline)); export default connect(mapStateToProps)(HashtagTimeline);

View file

@ -14,7 +14,6 @@ import { fetchAnnouncements, toggleShowAnnouncements } from 'mastodon/actions/an
import { IconWithBadge } from 'mastodon/components/icon_with_badge'; import { IconWithBadge } from 'mastodon/components/icon_with_badge';
import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator'; import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator';
import AnnouncementsContainer from 'mastodon/features/getting_started/containers/announcements_container'; import AnnouncementsContainer from 'mastodon/features/getting_started/containers/announcements_container';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { criticalUpdatesPending } from 'mastodon/initial_state'; import { criticalUpdatesPending } from 'mastodon/initial_state';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
@ -41,8 +40,12 @@ const mapStateToProps = state => ({
}); });
class HomeTimeline extends PureComponent { class HomeTimeline extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
hasUnread: PropTypes.bool, hasUnread: PropTypes.bool,
@ -123,7 +126,7 @@ class HomeTimeline extends PureComponent {
render () { render () {
const { intl, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props; const { intl, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props;
const pinned = !!columnId; const pinned = !!columnId;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
const banners = []; const banners = [];
let announcementsButton; let announcementsButton;
@ -187,4 +190,4 @@ class HomeTimeline extends PureComponent {
} }
export default connect(mapStateToProps)(withIdentity(injectIntl(HomeTimeline))); export default connect(mapStateToProps)(injectIntl(HomeTimeline));

View file

@ -5,7 +5,6 @@ import { FormattedMessage } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_REPORTS } from 'mastodon/permissions'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_REPORTS } from 'mastodon/permissions';
import { CheckboxWithLabel } from './checkbox_with_label'; import { CheckboxWithLabel } from './checkbox_with_label';
@ -13,9 +12,13 @@ import ClearColumnButton from './clear_column_button';
import GrantPermissionButton from './grant_permission_button'; import GrantPermissionButton from './grant_permission_button';
import SettingToggle from './setting_toggle'; import SettingToggle from './setting_toggle';
class ColumnSettings extends PureComponent { export default class ColumnSettings extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
settings: ImmutablePropTypes.map.isRequired, settings: ImmutablePropTypes.map.isRequired,
pushSettings: ImmutablePropTypes.map.isRequired, pushSettings: ImmutablePropTypes.map.isRequired,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
@ -212,7 +215,7 @@ class ColumnSettings extends PureComponent {
</div> </div>
</section> </section>
{((this.props.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) && ( {((this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) && (
<section role='group' aria-labelledby='notifications-admin-sign-up'> <section role='group' aria-labelledby='notifications-admin-sign-up'>
<h3 id='notifications-status'><FormattedMessage id='notifications.column_settings.admin.sign_up' defaultMessage='New sign-ups:' /></h3> <h3 id='notifications-status'><FormattedMessage id='notifications.column_settings.admin.sign_up' defaultMessage='New sign-ups:' /></h3>
@ -225,7 +228,7 @@ class ColumnSettings extends PureComponent {
</section> </section>
)} )}
{((this.props.identity.permissions & PERMISSION_MANAGE_REPORTS) === PERMISSION_MANAGE_REPORTS) && ( {((this.context.identity.permissions & PERMISSION_MANAGE_REPORTS) === PERMISSION_MANAGE_REPORTS) && (
<section role='group' aria-labelledby='notifications-admin-report'> <section role='group' aria-labelledby='notifications-admin-report'>
<h3 id='notifications-status'><FormattedMessage id='notifications.column_settings.admin.report' defaultMessage='New reports:' /></h3> <h3 id='notifications-status'><FormattedMessage id='notifications.column_settings.admin.report' defaultMessage='New reports:' /></h3>
@ -242,5 +245,3 @@ class ColumnSettings extends PureComponent {
} }
} }
export default withIdentity(ColumnSettings);

View file

@ -17,7 +17,6 @@ import NotificationsIcon from '@/material-icons/400-24px/notifications-fill.svg?
import { compareId } from 'mastodon/compare_id'; import { compareId } from 'mastodon/compare_id';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator'; import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { submitMarkers } from '../../actions/markers'; import { submitMarkers } from '../../actions/markers';
@ -78,8 +77,12 @@ const mapStateToProps = state => ({
}); });
class Notifications extends PureComponent { class Notifications extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
columnId: PropTypes.string, columnId: PropTypes.string,
notifications: ImmutablePropTypes.list.isRequired, notifications: ImmutablePropTypes.list.isRequired,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
@ -187,7 +190,7 @@ class Notifications extends PureComponent {
const { intl, notifications, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, lastReadId, canMarkAsRead, needsNotificationPermission } = this.props; const { intl, notifications, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, lastReadId, canMarkAsRead, needsNotificationPermission } = this.props;
const pinned = !!columnId; const pinned = !!columnId;
const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. When other people interact with you, you will see it here." />; const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. When other people interact with you, you will see it here." />;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
let scrollableContent = null; let scrollableContent = null;
@ -296,4 +299,4 @@ class Notifications extends PureComponent {
} }
export default connect(mapStateToProps)(withIdentity(injectIntl(Notifications))); export default connect(mapStateToProps)(injectIntl(Notifications));

View file

@ -18,7 +18,6 @@ import { replyCompose } from 'mastodon/actions/compose';
import { reblog, favourite, unreblog, unfavourite } from 'mastodon/actions/interactions'; import { reblog, favourite, unreblog, unfavourite } from 'mastodon/actions/interactions';
import { openModal } from 'mastodon/actions/modal'; import { openModal } from 'mastodon/actions/modal';
import { IconButton } from 'mastodon/components/icon_button'; import { IconButton } from 'mastodon/components/icon_button';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { me, boostModal } from 'mastodon/initial_state'; import { me, boostModal } from 'mastodon/initial_state';
import { makeGetStatus } from 'mastodon/selectors'; import { makeGetStatus } from 'mastodon/selectors';
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
@ -48,8 +47,12 @@ const makeMapStateToProps = () => {
}; };
class Footer extends ImmutablePureComponent { class Footer extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
statusId: PropTypes.string.isRequired, statusId: PropTypes.string.isRequired,
status: ImmutablePropTypes.map.isRequired, status: ImmutablePropTypes.map.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
@ -72,7 +75,7 @@ class Footer extends ImmutablePureComponent {
handleReplyClick = () => { handleReplyClick = () => {
const { dispatch, askReplyConfirmation, status, intl } = this.props; const { dispatch, askReplyConfirmation, status, intl } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
if (askReplyConfirmation) { if (askReplyConfirmation) {
@ -101,7 +104,7 @@ class Footer extends ImmutablePureComponent {
handleFavouriteClick = () => { handleFavouriteClick = () => {
const { dispatch, status } = this.props; const { dispatch, status } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
if (status.get('favourited')) { if (status.get('favourited')) {
@ -128,7 +131,7 @@ class Footer extends ImmutablePureComponent {
handleReblogClick = e => { handleReblogClick = e => {
const { dispatch, status } = this.props; const { dispatch, status } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
@ -206,4 +209,4 @@ class Footer extends ImmutablePureComponent {
} }
export default connect(makeMapStateToProps)(withIdentity(withRouter(injectIntl(Footer)))); export default connect(makeMapStateToProps)(withRouter(injectIntl(Footer)));

View file

@ -9,7 +9,6 @@ import { connect } from 'react-redux';
import PublicIcon from '@/material-icons/400-24px/public.svg?react'; import PublicIcon from '@/material-icons/400-24px/public.svg?react';
import { DismissableBanner } from 'mastodon/components/dismissable_banner'; import { DismissableBanner } from 'mastodon/components/dismissable_banner';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { domain } from 'mastodon/initial_state'; import { domain } from 'mastodon/initial_state';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
@ -41,12 +40,16 @@ const mapStateToProps = (state, { columnId }) => {
}; };
class PublicTimeline extends PureComponent { class PublicTimeline extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static defaultProps = { static defaultProps = {
onlyMedia: false, onlyMedia: false,
}; };
static propTypes = { static propTypes = {
identity: identityContextPropShape,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
columnId: PropTypes.string, columnId: PropTypes.string,
@ -77,7 +80,7 @@ class PublicTimeline extends PureComponent {
componentDidMount () { componentDidMount () {
const { dispatch, onlyMedia, onlyRemote } = this.props; const { dispatch, onlyMedia, onlyRemote } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
dispatch(expandPublicTimeline({ onlyMedia, onlyRemote })); dispatch(expandPublicTimeline({ onlyMedia, onlyRemote }));
@ -87,7 +90,7 @@ class PublicTimeline extends PureComponent {
} }
componentDidUpdate (prevProps) { componentDidUpdate (prevProps) {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (prevProps.onlyMedia !== this.props.onlyMedia || prevProps.onlyRemote !== this.props.onlyRemote) { if (prevProps.onlyMedia !== this.props.onlyMedia || prevProps.onlyRemote !== this.props.onlyRemote) {
const { dispatch, onlyMedia, onlyRemote } = this.props; const { dispatch, onlyMedia, onlyRemote } = this.props;
@ -161,4 +164,4 @@ class PublicTimeline extends PureComponent {
} }
export default connect(mapStateToProps)(withIdentity(injectIntl(PublicTimeline))); export default connect(mapStateToProps)(injectIntl(PublicTimeline));

View file

@ -21,7 +21,6 @@ import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react'; import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react'; import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react'; import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
@ -68,8 +67,12 @@ const mapStateToProps = (state, { status }) => ({
}); });
class ActionBar extends PureComponent { class ActionBar extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
status: ImmutablePropTypes.map.isRequired, status: ImmutablePropTypes.map.isRequired,
relationship: ImmutablePropTypes.record, relationship: ImmutablePropTypes.record,
onReply: PropTypes.func.isRequired, onReply: PropTypes.func.isRequired,
@ -195,7 +198,7 @@ class ActionBar extends PureComponent {
render () { render () {
const { status, relationship, intl } = this.props; const { status, relationship, intl } = this.props;
const { signedIn, permissions } = this.props.identity; const { signedIn, permissions } = this.context.identity;
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility')); const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
const pinnableStatus = ['public', 'unlisted', 'private'].includes(status.get('visibility')); const pinnableStatus = ['public', 'unlisted', 'private'].includes(status.get('visibility'));
@ -323,4 +326,4 @@ class ActionBar extends PureComponent {
} }
export default withRouter(connect(mapStateToProps)(withIdentity(injectIntl(ActionBar)))); export default withRouter(connect(mapStateToProps)(injectIntl(ActionBar)));

View file

@ -20,7 +20,6 @@ import { Icon } from 'mastodon/components/icon';
import { LoadingIndicator } from 'mastodon/components/loading_indicator'; import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import ScrollContainer from 'mastodon/containers/scroll_container'; import ScrollContainer from 'mastodon/containers/scroll_container';
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error'; import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
import { import {
@ -190,8 +189,12 @@ const titleFromStatus = (intl, status) => {
}; };
class Status extends ImmutablePureComponent { class Status extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
params: PropTypes.object.isRequired, params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
status: ImmutablePropTypes.map, status: ImmutablePropTypes.map,
@ -241,7 +244,7 @@ class Status extends ImmutablePureComponent {
handleFavouriteClick = (status) => { handleFavouriteClick = (status) => {
const { dispatch } = this.props; const { dispatch } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
if (status.get('favourited')) { if (status.get('favourited')) {
@ -271,7 +274,7 @@ class Status extends ImmutablePureComponent {
handleReplyClick = (status) => { handleReplyClick = (status) => {
const { askReplyConfirmation, dispatch, intl } = this.props; const { askReplyConfirmation, dispatch, intl } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
if (askReplyConfirmation) { if (askReplyConfirmation) {
@ -304,7 +307,7 @@ class Status extends ImmutablePureComponent {
handleReblogClick = (status, e) => { handleReblogClick = (status, e) => {
const { dispatch } = this.props; const { dispatch } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
if (signedIn) { if (signedIn) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
@ -742,4 +745,4 @@ class Status extends ImmutablePureComponent {
} }
export default withRouter(injectIntl(connect(makeMapStateToProps)(withIdentity(Status)))); export default withRouter(injectIntl(connect(makeMapStateToProps)(Status)));

View file

@ -7,13 +7,16 @@ import { changeComposing, mountCompose, unmountCompose } from 'mastodon/actions/
import ServerBanner from 'mastodon/components/server_banner'; import ServerBanner from 'mastodon/components/server_banner';
import ComposeFormContainer from 'mastodon/features/compose/containers/compose_form_container'; import ComposeFormContainer from 'mastodon/features/compose/containers/compose_form_container';
import SearchContainer from 'mastodon/features/compose/containers/search_container'; import SearchContainer from 'mastodon/features/compose/containers/search_container';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import LinkFooter from './link_footer'; import LinkFooter from './link_footer';
class ComposePanel extends PureComponent { class ComposePanel extends PureComponent {
static contextTypes = {
identity: PropTypes.object.isRequired,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
}; };
@ -38,7 +41,7 @@ class ComposePanel extends PureComponent {
} }
render() { render() {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
return ( return (
<div className='compose-panel' onFocus={this.onFocus}> <div className='compose-panel' onFocus={this.onFocus}>
@ -62,4 +65,4 @@ class ComposePanel extends PureComponent {
} }
export default connect()(withIdentity(ComposePanel)); export default connect()(ComposePanel);

View file

@ -13,7 +13,6 @@ import { fetchServer } from 'mastodon/actions/server';
import { Avatar } from 'mastodon/components/avatar'; import { Avatar } from 'mastodon/components/avatar';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { WordmarkLogo, SymbolLogo } from 'mastodon/components/logo'; import { WordmarkLogo, SymbolLogo } from 'mastodon/components/logo';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { registrationsOpen, me, sso_redirect } from 'mastodon/initial_state'; import { registrationsOpen, me, sso_redirect } from 'mastodon/initial_state';
const Account = connect(state => ({ const Account = connect(state => ({
@ -42,8 +41,12 @@ const mapDispatchToProps = (dispatch) => ({
}); });
class Header extends PureComponent { class Header extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
openClosedRegistrationsModal: PropTypes.func, openClosedRegistrationsModal: PropTypes.func,
location: PropTypes.object, location: PropTypes.object,
signupUrl: PropTypes.string.isRequired, signupUrl: PropTypes.string.isRequired,
@ -57,7 +60,7 @@ class Header extends PureComponent {
} }
render () { render () {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
const { location, openClosedRegistrationsModal, signupUrl, intl } = this.props; const { location, openClosedRegistrationsModal, signupUrl, intl } = this.props;
let content; let content;
@ -118,4 +121,4 @@ class Header extends PureComponent {
} }
export default injectIntl(withRouter(withIdentity(connect(mapStateToProps, mapDispatchToProps)(Header)))); export default injectIntl(withRouter(connect(mapStateToProps, mapDispatchToProps)(Header)));

View file

@ -8,7 +8,6 @@ import { Link } from 'react-router-dom';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { openModal } from 'mastodon/actions/modal'; import { openModal } from 'mastodon/actions/modal';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { domain, version, source_url, statusPageUrl, profile_directory as profileDirectory } from 'mastodon/initial_state'; import { domain, version, source_url, statusPageUrl, profile_directory as profileDirectory } from 'mastodon/initial_state';
import { PERMISSION_INVITE_USERS } from 'mastodon/permissions'; import { PERMISSION_INVITE_USERS } from 'mastodon/permissions';
import { logOut } from 'mastodon/utils/log_out'; import { logOut } from 'mastodon/utils/log_out';
@ -33,8 +32,12 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
}); });
class LinkFooter extends PureComponent { class LinkFooter extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
multiColumn: PropTypes.bool, multiColumn: PropTypes.bool,
onLogout: PropTypes.func.isRequired, onLogout: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
@ -50,7 +53,7 @@ class LinkFooter extends PureComponent {
}; };
render () { render () {
const { signedIn, permissions } = this.props.identity; const { signedIn, permissions } = this.context.identity;
const { multiColumn } = this.props; const { multiColumn } = this.props;
const canInvite = signedIn && ((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS); const canInvite = signedIn && ((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS);
@ -105,4 +108,4 @@ class LinkFooter extends PureComponent {
} }
export default injectIntl(withIdentity(connect(null, mapDispatchToProps)(LinkFooter))); export default injectIntl(connect(null, mapDispatchToProps)(LinkFooter));

View file

@ -31,7 +31,6 @@ import { fetchFollowRequests } from 'mastodon/actions/accounts';
import { IconWithBadge } from 'mastodon/components/icon_with_badge'; import { IconWithBadge } from 'mastodon/components/icon_with_badge';
import { WordmarkLogo } from 'mastodon/components/logo'; import { WordmarkLogo } from 'mastodon/components/logo';
import { NavigationPortal } from 'mastodon/components/navigation_portal'; import { NavigationPortal } from 'mastodon/components/navigation_portal';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { timelinePreview, trendsEnabled } from 'mastodon/initial_state'; import { timelinePreview, trendsEnabled } from 'mastodon/initial_state';
import { transientSingleColumn } from 'mastodon/is_mobile'; import { transientSingleColumn } from 'mastodon/is_mobile';
@ -98,8 +97,12 @@ const FollowRequestsLink = () => {
}; };
class NavigationPanel extends Component { class NavigationPanel extends Component {
static contextTypes = {
identity: PropTypes.object.isRequired,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
}; };
@ -109,7 +112,7 @@ class NavigationPanel extends Component {
render () { render () {
const { intl } = this.props; const { intl } = this.props;
const { signedIn, disabledAccountId } = this.props.identity; const { signedIn, disabledAccountId } = this.context.identity;
let banner = undefined; let banner = undefined;
@ -186,4 +189,4 @@ class NavigationPanel extends Component {
} }
export default injectIntl(withIdentity(NavigationPanel)); export default injectIntl(NavigationPanel);

View file

@ -15,7 +15,6 @@ import { focusApp, unfocusApp, changeLayout } from 'mastodon/actions/app';
import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'mastodon/actions/markers'; import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'mastodon/actions/markers';
import { INTRODUCTION_VERSION } from 'mastodon/actions/onboarding'; import { INTRODUCTION_VERSION } from 'mastodon/actions/onboarding';
import { PictureInPicture } from 'mastodon/features/picture_in_picture'; import { PictureInPicture } from 'mastodon/features/picture_in_picture';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { layoutFromWindow } from 'mastodon/is_mobile'; import { layoutFromWindow } from 'mastodon/is_mobile';
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
@ -121,8 +120,12 @@ const keyMap = {
}; };
class SwitchingColumnsArea extends PureComponent { class SwitchingColumnsArea extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
children: PropTypes.node, children: PropTypes.node,
location: PropTypes.object, location: PropTypes.object,
singleColumn: PropTypes.bool, singleColumn: PropTypes.bool,
@ -157,7 +160,7 @@ class SwitchingColumnsArea extends PureComponent {
render () { render () {
const { children, singleColumn } = this.props; const { children, singleColumn } = this.props;
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
const pathName = this.props.location.pathname; const pathName = this.props.location.pathname;
let redirect; let redirect;
@ -249,8 +252,12 @@ class SwitchingColumnsArea extends PureComponent {
} }
class UI extends PureComponent { class UI extends PureComponent {
static contextTypes = {
identity: PropTypes.object.isRequired,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
children: PropTypes.node, children: PropTypes.node,
isComposing: PropTypes.bool, isComposing: PropTypes.bool,
@ -302,7 +309,7 @@ class UI extends PureComponent {
this.dragTargets.push(e.target); this.dragTargets.push(e.target);
} }
if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore && this.props.identity.signedIn) { if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore && this.context.identity.signedIn) {
this.setState({ draggingOver: true }); this.setState({ draggingOver: true });
} }
}; };
@ -330,7 +337,7 @@ class UI extends PureComponent {
this.setState({ draggingOver: false }); this.setState({ draggingOver: false });
this.dragTargets = []; this.dragTargets = [];
if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore && this.props.identity.signedIn) { if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore && this.context.identity.signedIn) {
this.props.dispatch(uploadCompose(e.dataTransfer.files)); this.props.dispatch(uploadCompose(e.dataTransfer.files));
} }
}; };
@ -382,7 +389,7 @@ class UI extends PureComponent {
}; };
componentDidMount () { componentDidMount () {
const { signedIn } = this.props.identity; const { signedIn } = this.context.identity;
window.addEventListener('focus', this.handleWindowFocus, false); window.addEventListener('focus', this.handleWindowFocus, false);
window.addEventListener('blur', this.handleWindowBlur, false); window.addEventListener('blur', this.handleWindowBlur, false);
@ -579,7 +586,7 @@ class UI extends PureComponent {
<div className={classNames('ui', { 'is-composing': isComposing })} ref={this.setRef}> <div className={classNames('ui', { 'is-composing': isComposing })} ref={this.setRef}>
<Header /> <Header />
<SwitchingColumnsArea identity={this.props.identity} location={location} singleColumn={layout === 'mobile' || layout === 'single-column'}> <SwitchingColumnsArea location={location} singleColumn={layout === 'mobile' || layout === 'single-column'}>
{children} {children}
</SwitchingColumnsArea> </SwitchingColumnsArea>
@ -595,4 +602,4 @@ class UI extends PureComponent {
} }
export default connect(mapStateToProps)(injectIntl(withRouter(withIdentity(UI)))); export default connect(mapStateToProps)(injectIntl(withRouter(UI)));

View file

@ -1,74 +0,0 @@
import PropTypes from 'prop-types';
import { createContext, useContext } from 'react';
import hoistStatics from 'hoist-non-react-statics';
import type { InitialState } from 'mastodon/initial_state';
export interface IdentityContextType {
signedIn: boolean;
accountId: string | undefined;
disabledAccountId: string | undefined;
accessToken: string | undefined;
permissions: number;
}
export const identityContextPropShape = PropTypes.shape({
signedIn: PropTypes.bool.isRequired,
accountId: PropTypes.string,
disabledAccountId: PropTypes.string,
accessToken: PropTypes.string,
}).isRequired;
export const createIdentityContext = (state: InitialState) => ({
signedIn: !!state.meta.me,
accountId: state.meta.me,
disabledAccountId: state.meta.disabled_account_id,
accessToken: state.meta.access_token,
permissions: state.role?.permissions ?? 0,
});
export const IdentityContext = createContext<IdentityContextType>({
signedIn: false,
permissions: 0,
accountId: undefined,
disabledAccountId: undefined,
accessToken: undefined,
});
export const useIdentity = () => useContext(IdentityContext);
export interface IdentityProps {
ref?: unknown;
wrappedComponentRef?: unknown;
}
/* Injects an `identity` props into the wrapped component to be able to use the new context in class components */
export function withIdentity<
ComponentType extends React.ComponentType<IdentityProps>,
>(Component: ComponentType) {
const displayName = `withIdentity(${Component.displayName ?? Component.name})`;
const C = (props: React.ComponentProps<ComponentType>) => {
const { wrappedComponentRef, ...remainingProps } = props;
return (
<IdentityContext.Consumer>
{(context) => {
return (
// @ts-expect-error - Dynamic covariant generic components are tough to type.
<Component
{...remainingProps}
identity={context}
ref={wrappedComponentRef}
/>
);
}}
</IdentityContext.Consumer>
);
};
C.displayName = displayName;
C.WrappedComponent = Component;
return hoistStatics(C, Component);
}

View file

@ -44,22 +44,12 @@
* @property {string} sso_redirect * @property {string} sso_redirect
*/ */
/**
* @typedef Role
* @property {string} id
* @property {string} name
* @property {string} permissions
* @property {string} color
* @property {boolean} highlighted
*/
/** /**
* @typedef InitialState * @typedef InitialState
* @property {Record<string, import("./api_types/accounts").ApiAccountJSON>} accounts * @property {Record<string, import("./api_types/accounts").ApiAccountJSON>} accounts
* @property {InitialStateLanguage[]} languages * @property {InitialStateLanguage[]} languages
* @property {boolean=} critical_updates_pending * @property {boolean=} critical_updates_pending
* @property {InitialStateMeta} meta * @property {InitialStateMeta} meta
* @property {Role?} role
*/ */
const element = document.getElementById('initial-state'); const element = document.getElementById('initial-state');

View file

@ -474,7 +474,7 @@
"notification.follow_request": "Mae {name} wedi gwneud cais i'ch dilyn", "notification.follow_request": "Mae {name} wedi gwneud cais i'ch dilyn",
"notification.mention": "Crybwyllodd {name} amdanoch chi", "notification.mention": "Crybwyllodd {name} amdanoch chi",
"notification.moderation-warning.learn_more": "Dysgu mwy", "notification.moderation-warning.learn_more": "Dysgu mwy",
"notification.moderation_warning": "Rydych wedi derbyn rhybudd gan gymedrolwr", "notification.moderation_warning": "Rydych wedi derbyn rhybudd cymedroli",
"notification.moderation_warning.action_delete_statuses": "Mae rhai o'ch postiadau wedi'u dileu.", "notification.moderation_warning.action_delete_statuses": "Mae rhai o'ch postiadau wedi'u dileu.",
"notification.moderation_warning.action_disable": "Mae eich cyfrif wedi'i analluogi.", "notification.moderation_warning.action_disable": "Mae eich cyfrif wedi'i analluogi.",
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Mae rhai o'ch postiadau wedi'u marcio'n sensitif.", "notification.moderation_warning.action_mark_statuses_as_sensitive": "Mae rhai o'ch postiadau wedi'u marcio'n sensitif.",

View file

@ -308,8 +308,6 @@
"follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.", "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
"follow_suggestions.curated_suggestion": "Staff pick", "follow_suggestions.curated_suggestion": "Staff pick",
"follow_suggestions.dismiss": "Don't show again", "follow_suggestions.dismiss": "Don't show again",
"follow_suggestions.featured_longer": "Hand-picked by the {domain} team",
"follow_suggestions.friends_of_friends_longer": "Popular among people you follow",
"follow_suggestions.hints.featured": "This profile has been hand-picked by the {domain} team.", "follow_suggestions.hints.featured": "This profile has been hand-picked by the {domain} team.",
"follow_suggestions.hints.friends_of_friends": "This profile is popular among the people you follow.", "follow_suggestions.hints.friends_of_friends": "This profile is popular among the people you follow.",
"follow_suggestions.hints.most_followed": "This profile is one of the most followed on {domain}.", "follow_suggestions.hints.most_followed": "This profile is one of the most followed on {domain}.",
@ -317,8 +315,6 @@
"follow_suggestions.hints.similar_to_recently_followed": "This profile is similar to the profiles you have most recently followed.", "follow_suggestions.hints.similar_to_recently_followed": "This profile is similar to the profiles you have most recently followed.",
"follow_suggestions.personalized_suggestion": "Personalised suggestion", "follow_suggestions.personalized_suggestion": "Personalised suggestion",
"follow_suggestions.popular_suggestion": "Popular suggestion", "follow_suggestions.popular_suggestion": "Popular suggestion",
"follow_suggestions.popular_suggestion_longer": "Popular on {domain}",
"follow_suggestions.similar_to_recently_followed_longer": "Similar to profiles you recently followed",
"follow_suggestions.view_all": "View all", "follow_suggestions.view_all": "View all",
"follow_suggestions.who_to_follow": "Who to follow", "follow_suggestions.who_to_follow": "Who to follow",
"followed_tags": "Followed hashtags", "followed_tags": "Followed hashtags",
@ -473,15 +469,6 @@
"notification.follow": "{name} followed you", "notification.follow": "{name} followed you",
"notification.follow_request": "{name} has requested to follow you", "notification.follow_request": "{name} has requested to follow you",
"notification.mention": "{name} mentioned you", "notification.mention": "{name} mentioned you",
"notification.moderation-warning.learn_more": "Learn more",
"notification.moderation_warning": "You have received a moderation warning",
"notification.moderation_warning.action_delete_statuses": "Some of your posts have been removed.",
"notification.moderation_warning.action_disable": "Your account has been disabled.",
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Some of your posts have been marked as sensitive.",
"notification.moderation_warning.action_none": "Your account has received a moderation warning.",
"notification.moderation_warning.action_sensitive": "Your posts will be marked as sensitive from now on.",
"notification.moderation_warning.action_silence": "Your account has been limited.",
"notification.moderation_warning.action_suspend": "Your account has been suspended.",
"notification.own_poll": "Your poll has ended", "notification.own_poll": "Your poll has ended",
"notification.poll": "A poll you have voted in has ended", "notification.poll": "A poll you have voted in has ended",
"notification.reblog": "{name} boosted your status", "notification.reblog": "{name} boosted your status",

View file

@ -476,12 +476,12 @@
"notification.moderation-warning.learn_more": "Saber más", "notification.moderation-warning.learn_more": "Saber más",
"notification.moderation_warning": "Has recibido una advertencia de moderación", "notification.moderation_warning": "Has recibido una advertencia de moderación",
"notification.moderation_warning.action_delete_statuses": "Se han eliminado algunas de tus publicaciones.", "notification.moderation_warning.action_delete_statuses": "Se han eliminado algunas de tus publicaciones.",
"notification.moderation_warning.action_disable": "Tu cuenta ha sido desactivada.", "notification.moderation_warning.action_disable": "Se ha desactivado su cuenta.",
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Se han marcado como sensibles algunas de tus publicaciones.", "notification.moderation_warning.action_mark_statuses_as_sensitive": "Se han marcado como sensibles algunas de tus publicaciones.",
"notification.moderation_warning.action_none": "Tu cuenta ha recibido un aviso de moderación.", "notification.moderation_warning.action_none": "Tu cuenta ha recibido un aviso de moderación.",
"notification.moderation_warning.action_sensitive": "De ahora en adelante, todas tus publicaciones se marcarán como sensibles.", "notification.moderation_warning.action_sensitive": "De ahora en adelante, todas tus publicaciones se marcarán como sensibles.",
"notification.moderation_warning.action_silence": "Tu cuenta ha sido limitada.", "notification.moderation_warning.action_silence": "Se ha limitado tu cuenta.",
"notification.moderation_warning.action_suspend": "Tu cuenta ha sido suspendida.", "notification.moderation_warning.action_suspend": "Se ha suspendido tu cuenta.",
"notification.own_poll": "Tu encuesta ha terminado", "notification.own_poll": "Tu encuesta ha terminado",
"notification.poll": "Una encuesta en la que has votado ha terminado", "notification.poll": "Una encuesta en la que has votado ha terminado",
"notification.reblog": "{name} ha retooteado tu estado", "notification.reblog": "{name} ha retooteado tu estado",

View file

@ -476,12 +476,12 @@
"notification.moderation-warning.learn_more": "Saber más", "notification.moderation-warning.learn_more": "Saber más",
"notification.moderation_warning": "Has recibido una advertencia de moderación", "notification.moderation_warning": "Has recibido una advertencia de moderación",
"notification.moderation_warning.action_delete_statuses": "Se han eliminado algunas de tus publicaciones.", "notification.moderation_warning.action_delete_statuses": "Se han eliminado algunas de tus publicaciones.",
"notification.moderation_warning.action_disable": "Tu cuenta ha sido desactivada.", "notification.moderation_warning.action_disable": "Se ha desactivado su cuenta.",
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Se han marcado como sensibles algunas de tus publicaciones.", "notification.moderation_warning.action_mark_statuses_as_sensitive": "Se han marcado como sensibles algunas de tus publicaciones.",
"notification.moderation_warning.action_none": "Tu cuenta ha recibido un aviso de moderación.", "notification.moderation_warning.action_none": "Tu cuenta ha recibido un aviso de moderación.",
"notification.moderation_warning.action_sensitive": "De ahora en adelante, todas tus publicaciones se marcarán como sensibles.", "notification.moderation_warning.action_sensitive": "De ahora en adelante, todas tus publicaciones se marcarán como sensibles.",
"notification.moderation_warning.action_silence": "Tu cuenta ha sido limitada.", "notification.moderation_warning.action_silence": "Se ha limitado tu cuenta.",
"notification.moderation_warning.action_suspend": "Tu cuenta ha sido suspendida.", "notification.moderation_warning.action_suspend": "Se ha suspendido tu cuenta.",
"notification.own_poll": "Tu encuesta ha terminado", "notification.own_poll": "Tu encuesta ha terminado",
"notification.poll": "Una encuesta en la que has votado ha terminado", "notification.poll": "Una encuesta en la que has votado ha terminado",
"notification.reblog": "{name} ha impulsado tu publicación", "notification.reblog": "{name} ha impulsado tu publicación",

View file

@ -474,11 +474,11 @@
"notification.follow_request": "{name} biður um at fylgja tær", "notification.follow_request": "{name} biður um at fylgja tær",
"notification.mention": "{name} nevndi teg", "notification.mention": "{name} nevndi teg",
"notification.moderation-warning.learn_more": "Lær meira", "notification.moderation-warning.learn_more": "Lær meira",
"notification.moderation_warning": "Tú hevur móttikið eina umsjónarávaring", "notification.moderation_warning": "Tú hevur móttikið eina umsjónarávarðing",
"notification.moderation_warning.action_delete_statuses": "Onkrir av tínum postum eru strikaðir.", "notification.moderation_warning.action_delete_statuses": "Onkrir av tínum postum eru strikaðir.",
"notification.moderation_warning.action_disable": "Konta tín er gjørd óvirkin.", "notification.moderation_warning.action_disable": "Konta tín er gjørd óvirkin.",
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Nakrir av postum tínum eru merktir sum viðkvæmir.", "notification.moderation_warning.action_mark_statuses_as_sensitive": "Nakrir av postum tínum eru merktir sum viðkvæmir.",
"notification.moderation_warning.action_none": "Konta tín hevur móttikið eina umsjónarávaring.", "notification.moderation_warning.action_none": "Konta tín hevur móttikið eina umsjónarávarðing.",
"notification.moderation_warning.action_sensitive": "Postar tínir verða merktir sum viðkvæmir frá nú av.", "notification.moderation_warning.action_sensitive": "Postar tínir verða merktir sum viðkvæmir frá nú av.",
"notification.moderation_warning.action_silence": "Konta tín er avmarkað.", "notification.moderation_warning.action_silence": "Konta tín er avmarkað.",
"notification.moderation_warning.action_suspend": "Konta tín er ógildað.", "notification.moderation_warning.action_suspend": "Konta tín er ógildað.",

View file

@ -92,7 +92,7 @@
"block_modal.remote_users_caveat": "Ímoslle pedir ao servidor {domain} que respecte a túa decisión. Emporiso, non hai garantía de que atenda a petición xa que os servidores xestionan os bloqueos de formas diferentes. As publicacións públicas poderían aínda ser visibles para usuarias que non iniciaron sesión.", "block_modal.remote_users_caveat": "Ímoslle pedir ao servidor {domain} que respecte a túa decisión. Emporiso, non hai garantía de que atenda a petición xa que os servidores xestionan os bloqueos de formas diferentes. As publicacións públicas poderían aínda ser visibles para usuarias que non iniciaron sesión.",
"block_modal.show_less": "Mostrar menos", "block_modal.show_less": "Mostrar menos",
"block_modal.show_more": "Mostrar máis", "block_modal.show_more": "Mostrar máis",
"block_modal.they_cant_mention": "Non te poden seguir nin mencionar.", "block_modal.they_cant_mention": "Non te pode seguir nin mencionar.",
"block_modal.they_cant_see_posts": "Non pode ver as túas publicacións nin ti as de ela.", "block_modal.they_cant_see_posts": "Non pode ver as túas publicacións nin ti as de ela.",
"block_modal.they_will_know": "Pode ver que a bloqueaches.", "block_modal.they_will_know": "Pode ver que a bloqueaches.",
"block_modal.title": "Bloquear usuaria?", "block_modal.title": "Bloquear usuaria?",

View file

@ -19,7 +19,7 @@
"account.block_domain": "Blocar dominio {domain}", "account.block_domain": "Blocar dominio {domain}",
"account.block_short": "Blocar", "account.block_short": "Blocar",
"account.blocked": "Blocate", "account.blocked": "Blocate",
"account.browse_more_on_origin_server": "Percurrer plus sur le profilo original", "account.browse_more_on_origin_server": "Navigar plus sur le profilo original",
"account.cancel_follow_request": "Cancellar sequimento", "account.cancel_follow_request": "Cancellar sequimento",
"account.copy": "Copiar ligamine a profilo", "account.copy": "Copiar ligamine a profilo",
"account.direct": "Mentionar privatemente @{name}", "account.direct": "Mentionar privatemente @{name}",
@ -122,7 +122,7 @@
"column.direct": "Mentiones private", "column.direct": "Mentiones private",
"column.directory": "Navigar profilos", "column.directory": "Navigar profilos",
"column.domain_blocks": "Dominios blocate", "column.domain_blocks": "Dominios blocate",
"column.favourites": "Favorites", "column.favourites": "Favoritos",
"column.firehose": "Fluxos in directo", "column.firehose": "Fluxos in directo",
"column.follow_requests": "Requestas de sequimento", "column.follow_requests": "Requestas de sequimento",
"column.home": "Initio", "column.home": "Initio",
@ -204,7 +204,7 @@
"disabled_account_banner.account_settings": "Parametros de conto", "disabled_account_banner.account_settings": "Parametros de conto",
"disabled_account_banner.text": "Tu conto {disabledAccount} es actualmente disactivate.", "disabled_account_banner.text": "Tu conto {disabledAccount} es actualmente disactivate.",
"dismissable_banner.community_timeline": "Ecce le messages public le plus recente del personas con contos sur {domain}.", "dismissable_banner.community_timeline": "Ecce le messages public le plus recente del personas con contos sur {domain}.",
"dismissable_banner.dismiss": "Clauder", "dismissable_banner.dismiss": "Dimitter",
"dismissable_banner.explore_links": "Istes es le articulos de novas que se condivide le plus sur le rete social hodie. Le articulos de novas le plus recente, publicate per plus personas differente, se classifica plus in alto.", "dismissable_banner.explore_links": "Istes es le articulos de novas que se condivide le plus sur le rete social hodie. Le articulos de novas le plus recente, publicate per plus personas differente, se classifica plus in alto.",
"dismissable_banner.explore_statuses": "Ecce le messages de tote le rete social que gania popularitate hodie. Le messages plus nove con plus impulsos e favorites se classifica plus in alto.", "dismissable_banner.explore_statuses": "Ecce le messages de tote le rete social que gania popularitate hodie. Le messages plus nove con plus impulsos e favorites se classifica plus in alto.",
"dismissable_banner.explore_tags": "Ecce le hashtags que gania popularitate sur le rete social hodie. Le hashtags usate per plus personas differente se classifica plus in alto.", "dismissable_banner.explore_tags": "Ecce le hashtags que gania popularitate sur le rete social hodie. Le hashtags usate per plus personas differente se classifica plus in alto.",
@ -212,8 +212,8 @@
"domain_block_modal.block": "Blocar le servitor", "domain_block_modal.block": "Blocar le servitor",
"domain_block_modal.block_account_instead": "Blocar @{name} in su loco", "domain_block_modal.block_account_instead": "Blocar @{name} in su loco",
"domain_block_modal.they_can_interact_with_old_posts": "Le personas de iste servitor pote interager con tu messages ancian.", "domain_block_modal.they_can_interact_with_old_posts": "Le personas de iste servitor pote interager con tu messages ancian.",
"domain_block_modal.they_cant_follow": "Necuno de iste servitor pote sequer te.", "domain_block_modal.they_cant_follow": "Nulle persona ab iste servitor pote sequer te.",
"domain_block_modal.they_wont_know": "Ille non sapera que ille ha essite blocate.", "domain_block_modal.they_wont_know": "Illes non sapera que illes ha essite blocate.",
"domain_block_modal.title": "Blocar dominio?", "domain_block_modal.title": "Blocar dominio?",
"domain_block_modal.you_will_lose_followers": "Omne sequitores ab iste servitor essera removite.", "domain_block_modal.you_will_lose_followers": "Omne sequitores ab iste servitor essera removite.",
"domain_block_modal.you_wont_see_posts": "Tu non videra messages e notificationes ab usatores sur iste servitor.", "domain_block_modal.you_wont_see_posts": "Tu non videra messages e notificationes ab usatores sur iste servitor.",
@ -307,7 +307,7 @@
"follow_request.reject": "Rejectar", "follow_request.reject": "Rejectar",
"follow_requests.unlocked_explanation": "Benque tu conto non es serrate, le personal de {domain} pensa que es un bon idea que tu revide manualmente le sequente requestas de iste contos.", "follow_requests.unlocked_explanation": "Benque tu conto non es serrate, le personal de {domain} pensa que es un bon idea que tu revide manualmente le sequente requestas de iste contos.",
"follow_suggestions.curated_suggestion": "Selection del equipa", "follow_suggestions.curated_suggestion": "Selection del equipa",
"follow_suggestions.dismiss": "Non monstrar de novo", "follow_suggestions.dismiss": "Non monstrar novemente",
"follow_suggestions.featured_longer": "Seligite con cura per le equipa de {domain}", "follow_suggestions.featured_longer": "Seligite con cura per le equipa de {domain}",
"follow_suggestions.friends_of_friends_longer": "Popular inter le gente que tu seque", "follow_suggestions.friends_of_friends_longer": "Popular inter le gente que tu seque",
"follow_suggestions.hints.featured": "Iste profilo ha essite seligite manualmente per le equipa de {domain}.", "follow_suggestions.hints.featured": "Iste profilo ha essite seligite manualmente per le equipa de {domain}.",
@ -412,7 +412,7 @@
"lightbox.next": "Sequente", "lightbox.next": "Sequente",
"lightbox.previous": "Precedente", "lightbox.previous": "Precedente",
"limited_account_hint.action": "Monstrar profilo in omne caso", "limited_account_hint.action": "Monstrar profilo in omne caso",
"limited_account_hint.title": "Iste profilo ha essite celate per le moderatores de {domain}.", "limited_account_hint.title": "Iste profilo esseva celate per le moderatores de {domain}.",
"link_preview.author": "Per {name}", "link_preview.author": "Per {name}",
"lists.account.add": "Adder al lista", "lists.account.add": "Adder al lista",
"lists.account.remove": "Remover del lista", "lists.account.remove": "Remover del lista",
@ -432,12 +432,12 @@
"loading_indicator.label": "Cargante…", "loading_indicator.label": "Cargante…",
"media_gallery.toggle_visible": "{number, plural, one {Celar imagine} other {Celar imagines}}", "media_gallery.toggle_visible": "{number, plural, one {Celar imagine} other {Celar imagines}}",
"moved_to_account_banner.text": "Tu conto {disabledAccount} es actualmente disactivate perque tu ha cambiate de conto a {movedToAccount}.", "moved_to_account_banner.text": "Tu conto {disabledAccount} es actualmente disactivate perque tu ha cambiate de conto a {movedToAccount}.",
"mute_modal.hide_from_notifications": "Celar in notificationes", "mute_modal.hide_from_notifications": "Celar ab notificationes",
"mute_modal.hide_options": "Celar optiones", "mute_modal.hide_options": "Celar optiones",
"mute_modal.indefinite": "Usque io dissilentia iste persona", "mute_modal.indefinite": "Usque io dissilentia iste persona",
"mute_modal.show_options": "Monstrar optiones", "mute_modal.show_options": "Monstrar optiones",
"mute_modal.they_can_mention_and_follow": "Ille pote mentionar te e sequer te, ma tu non potera vider le.", "mute_modal.they_can_mention_and_follow": "Illes pote mentionar te e sequer te, ma tu non potera vider los.",
"mute_modal.they_wont_know": "Ille non sapera que ille ha essite silentiate.", "mute_modal.they_wont_know": "Illes non sapera que illes ha essite silentiate.",
"mute_modal.title": "Silentiar le usator?", "mute_modal.title": "Silentiar le usator?",
"mute_modal.you_wont_see_mentions": "Tu non videra le messages que mentiona iste persona.", "mute_modal.you_wont_see_mentions": "Tu non videra le messages que mentiona iste persona.",
"mute_modal.you_wont_see_posts": "Iste persona pote totevia vider tu messages, ma tu non videra le sues.", "mute_modal.you_wont_see_posts": "Iste persona pote totevia vider tu messages, ma tu non videra le sues.",
@ -451,13 +451,13 @@
"navigation_bar.discover": "Discoperir", "navigation_bar.discover": "Discoperir",
"navigation_bar.domain_blocks": "Dominios blocate", "navigation_bar.domain_blocks": "Dominios blocate",
"navigation_bar.explore": "Explorar", "navigation_bar.explore": "Explorar",
"navigation_bar.favourites": "Favorites", "navigation_bar.favourites": "Favoritos",
"navigation_bar.filters": "Parolas silentiate", "navigation_bar.filters": "Parolas silentiate",
"navigation_bar.follow_requests": "Requestas de sequimento", "navigation_bar.follow_requests": "Requestas de sequimento",
"navigation_bar.followed_tags": "Hashtags sequite", "navigation_bar.followed_tags": "Hashtags sequite",
"navigation_bar.follows_and_followers": "Sequites e sequitores", "navigation_bar.follows_and_followers": "Sequites e sequitores",
"navigation_bar.lists": "Listas", "navigation_bar.lists": "Listas",
"navigation_bar.logout": "Clauder session", "navigation_bar.logout": "Clauder le session",
"navigation_bar.mutes": "Usatores silentiate", "navigation_bar.mutes": "Usatores silentiate",
"navigation_bar.opened_in_classic_interface": "Messages, contos e altere paginas specific es aperite per predefinition in le interfacie web classic.", "navigation_bar.opened_in_classic_interface": "Messages, contos e altere paginas specific es aperite per predefinition in le interfacie web classic.",
"navigation_bar.personal": "Personal", "navigation_bar.personal": "Personal",
@ -474,7 +474,7 @@
"notification.follow_request": "{name} ha requestate de sequer te", "notification.follow_request": "{name} ha requestate de sequer te",
"notification.mention": "{name} te ha mentionate", "notification.mention": "{name} te ha mentionate",
"notification.moderation-warning.learn_more": "Apprender plus", "notification.moderation-warning.learn_more": "Apprender plus",
"notification.moderation_warning": "Tu ha recepite un aviso de moderation", "notification.moderation_warning": "Tu ha recipite un advertimento de moderation",
"notification.moderation_warning.action_delete_statuses": "Alcunes de tu messages ha essite removite.", "notification.moderation_warning.action_delete_statuses": "Alcunes de tu messages ha essite removite.",
"notification.moderation_warning.action_disable": "Tu conto ha essite disactivate.", "notification.moderation_warning.action_disable": "Tu conto ha essite disactivate.",
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Alcunes de tu messages ha essite marcate como sensibile.", "notification.moderation_warning.action_mark_statuses_as_sensitive": "Alcunes de tu messages ha essite marcate como sensibile.",
@ -501,7 +501,7 @@
"notifications.column_settings.admin.report": "Nove signalationes:", "notifications.column_settings.admin.report": "Nove signalationes:",
"notifications.column_settings.admin.sign_up": "Nove inscriptiones:", "notifications.column_settings.admin.sign_up": "Nove inscriptiones:",
"notifications.column_settings.alert": "Notificationes de scriptorio", "notifications.column_settings.alert": "Notificationes de scriptorio",
"notifications.column_settings.favourite": "Favorites:", "notifications.column_settings.favourite": "Favoritos:",
"notifications.column_settings.filter_bar.advanced": "Monstrar tote le categorias", "notifications.column_settings.filter_bar.advanced": "Monstrar tote le categorias",
"notifications.column_settings.filter_bar.category": "Barra de filtro rapide", "notifications.column_settings.filter_bar.category": "Barra de filtro rapide",
"notifications.column_settings.follow": "Nove sequitores:", "notifications.column_settings.follow": "Nove sequitores:",
@ -518,7 +518,7 @@
"notifications.column_settings.update": "Modificationes:", "notifications.column_settings.update": "Modificationes:",
"notifications.filter.all": "Toto", "notifications.filter.all": "Toto",
"notifications.filter.boosts": "Impulsos", "notifications.filter.boosts": "Impulsos",
"notifications.filter.favourites": "Favorites", "notifications.filter.favourites": "Favoritos",
"notifications.filter.follows": "Sequites", "notifications.filter.follows": "Sequites",
"notifications.filter.mentions": "Mentiones", "notifications.filter.mentions": "Mentiones",
"notifications.filter.polls": "Resultatos del sondage", "notifications.filter.polls": "Resultatos del sondage",
@ -717,7 +717,7 @@
"status.edited": "Ultime modification le {date}", "status.edited": "Ultime modification le {date}",
"status.edited_x_times": "Modificate {count, plural, one {{count} vice} other {{count} vices}}", "status.edited_x_times": "Modificate {count, plural, one {{count} vice} other {{count} vices}}",
"status.embed": "Incastrar", "status.embed": "Incastrar",
"status.favourite": "Adder al favorites", "status.favourite": "Adder al favoritos",
"status.favourites": "{count, plural, one {favorite} other {favorites}}", "status.favourites": "{count, plural, one {favorite} other {favorites}}",
"status.filter": "Filtrar iste message", "status.filter": "Filtrar iste message",
"status.filtered": "Filtrate", "status.filtered": "Filtrate",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Proclamation", "announcement.announcement": "Proclamation",
"attachments_list.unprocessed": "(íntractat)", "attachments_list.unprocessed": "(íntractat)",
"audio.hide": "Celar audio", "audio.hide": "Celar audio",
"block_modal.remote_users_caveat": "Noi va petir que li servitor {domain} mey respecter tui decision. Támen, obedientie ne es garantit pro que chascun servitor gere bloccas diferentmen. Possibilmen public postas va restar visibil a usatores de inloggat.",
"block_modal.show_less": "Monstrar minu", "block_modal.show_less": "Monstrar minu",
"block_modal.show_more": "Monstrar plu", "block_modal.show_more": "Monstrar plu",
"block_modal.they_cant_mention": "Ne posse mentionar ni sequer te.", "block_modal.they_cant_mention": "Ne posse mentionar ni sequer te.",
@ -225,10 +224,7 @@
"domain_pill.their_username": "Su unic identificator sur su servitor. It es possibil que altri servitores va haver usatores con li sam nómine.", "domain_pill.their_username": "Su unic identificator sur su servitor. It es possibil que altri servitores va haver usatores con li sam nómine.",
"domain_pill.username": "Usator-nómine", "domain_pill.username": "Usator-nómine",
"domain_pill.whats_in_a_handle": "Ex quo consiste un identificator?", "domain_pill.whats_in_a_handle": "Ex quo consiste un identificator?",
"domain_pill.who_they_are": "Pro que identificatores informa qui e u un person is, tu posse interacter con persones tra li rete social de <button>ActivityPub-usant platformes</button>.",
"domain_pill.who_you_are": "Pro que tui identificator informa qui e u tu es, persones posse interacter con te tra li rete social de <button>ActivityPub-usant platformes</button>.",
"domain_pill.your_handle": "Tui identificator:", "domain_pill.your_handle": "Tui identificator:",
"domain_pill.your_server": "Tui digital hem, u trova se omni tui postas. Si it ne plese te, tu posse transferer ad un altri servitor quandecunc e tui sequitores con te.",
"domain_pill.your_username": "Tui unic identificator sur ti-ci servitor. It es possibil que altri servitores va haver usatores con li sam nómine.", "domain_pill.your_username": "Tui unic identificator sur ti-ci servitor. It es possibil que altri servitores va haver usatores con li sam nómine.",
"embed.instructions": "Inbedar ti-ci posta per copiar li code in infra.", "embed.instructions": "Inbedar ti-ci posta per copiar li code in infra.",
"embed.preview": "Vi qualmen it va aspecter:", "embed.preview": "Vi qualmen it va aspecter:",

Some files were not shown because too many files have changed in this diff Show more