Compare commits
84 commits
4f37b4873f
...
6c28810b39
Author | SHA1 | Date | |
---|---|---|---|
|
6c28810b39 | ||
|
48b9837ad8 | ||
|
9b0c46ebf2 | ||
|
e853355f24 | ||
|
20fdf8e22c | ||
|
7b3eef4cb1 | ||
|
0a2110b9af | ||
|
00cf8d3748 | ||
|
2bcbeed951 | ||
|
ca5955ed76 | ||
|
e46321e63d | ||
|
de4815afda | ||
|
def6b686ff | ||
|
70608f824e | ||
|
9658d3e580 | ||
|
0ce22859a5 | ||
|
8b75d18371 | ||
|
814d00cf4b | ||
|
778bd96a52 | ||
|
990a0c19a9 | ||
|
0f07e1cd4c | ||
|
0a343b9a91 | ||
|
a178ba7cd5 | ||
|
6282b6da77 | ||
|
93a617236e | ||
|
2da2a1dae9 | ||
|
57fb2cf948 | ||
|
9668ac5acd | ||
|
12472e7f40 | ||
|
a627219b25 | ||
|
bff7769f5f | ||
|
a6d12299f2 | ||
|
fc166d07f0 | ||
|
3286ad5226 | ||
|
f7f5b9dadd | ||
|
226d7a7bad | ||
|
87b5cfc60e | ||
|
7b078b46a2 | ||
|
061464a563 | ||
|
2810231180 | ||
|
b2388be71e | ||
|
66906a1bc1 | ||
|
1b6eb2c7f0 | ||
|
283a891e92 | ||
|
65e82211cd | ||
|
f0d6dc4519 | ||
|
356bbbaa7f | ||
|
cdb042ae86 | ||
|
c9ee1437c0 | ||
|
60b423b3f7 | ||
|
94493cff92 | ||
|
ca560c1095 | ||
|
d5d3a0fc57 | ||
|
5fd56512de | ||
|
c2ca3d152f | ||
|
4e085dff52 | ||
|
19ed3d9441 | ||
|
85c625d319 | ||
|
6beead3867 | ||
|
aad5e841b5 | ||
|
78a8263f73 | ||
|
7f7eba8753 | ||
|
40639510f8 | ||
|
44e855db78 | ||
|
508e93eb64 | ||
|
38a330f963 | ||
|
47ce5f4ca9 | ||
|
160823716a | ||
|
666760f450 | ||
|
1bf661cddb | ||
|
b5b84fad65 | ||
|
3a7aec2807 | ||
|
13fb54920b | ||
|
9ec7c1f892 | ||
|
6e1b8b33f5 | ||
|
b429c9b8a7 | ||
|
c66fdb3dff | ||
|
cb93c1edf0 | ||
|
f66c9faca0 | ||
|
123108b1cc | ||
|
5e7d88a85d | ||
|
471728d6dd | ||
|
ab4efa3bf8 | ||
|
807cf354fc |
223 changed files with 2281 additions and 911 deletions
|
@ -4,7 +4,8 @@ NODE_ENV=production
|
|||
LOCAL_DOMAIN=cb6e6126.ngrok.io
|
||||
LOCAL_HTTPS=true
|
||||
|
||||
# Required by ActiveRecord encryption feature
|
||||
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=fkSxKD2bF396kdQbrP1EJ7WbU7ZgNokR
|
||||
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=r0hvVmzBVsjxC7AMlwhOzmtc36ZCOS1E
|
||||
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=PhdFyyfy5xJ7WVd2lWBpcPScRQHzRTNr
|
||||
# Secret values required by ActiveRecord encryption feature
|
||||
# Use `bin/rails db:encryption:init` to generate fresh secrets
|
||||
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=test_determinist_key_DO_NOT_USE_IN_PRODUCTION
|
||||
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=test_salt_DO_NOT_USE_IN_PRODUCTION
|
||||
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=test_primary_key_DO_NOT_USE_IN_PRODUCTION
|
||||
|
|
20
.github/workflows/test-ruby.yml
vendored
20
.github/workflows/test-ruby.yml
vendored
|
@ -264,8 +264,8 @@ jobs:
|
|||
ports:
|
||||
- 6379:6379
|
||||
|
||||
search:
|
||||
image: ${{ matrix.search-image }}
|
||||
elasticsearch:
|
||||
image: ${{ contains(matrix.search-image, 'elasticsearch') && matrix.search-image || '' }}
|
||||
env:
|
||||
discovery.type: single-node
|
||||
xpack.security.enabled: false
|
||||
|
@ -277,6 +277,20 @@ jobs:
|
|||
ports:
|
||||
- 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:
|
||||
DB_HOST: localhost
|
||||
DB_USER: postgres
|
||||
|
@ -300,6 +314,8 @@ jobs:
|
|||
include:
|
||||
- ruby-version: '.ruby-version'
|
||||
search-image: docker.elastic.co/elasticsearch/elasticsearch:8.10.2
|
||||
- ruby-version: '.ruby-version'
|
||||
search-image: opensearchproject/opensearch:2
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
|
@ -211,6 +211,11 @@ Style/PercentLiteralDelimiters:
|
|||
Style/RedundantBegin:
|
||||
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
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#stylerescuestandarderror
|
||||
Style/RescueStandardError:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
|
||||
# using RuboCop version 1.62.1.
|
||||
# using RuboCop version 1.63.5.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
|
@ -122,13 +122,6 @@ Style/HashTransformValues:
|
|||
- 'app/serializers/rest/web_push_subscription_serializer.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).
|
||||
Style/MapToHash:
|
||||
Exclude:
|
||||
|
@ -176,16 +169,6 @@ Style/RedundantConstantBase:
|
|||
- 'config/environments/production.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).
|
||||
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
|
||||
# AllowedMethods: present?, blank?, presence, try, try!
|
||||
|
|
22
.simplecov
22
.simplecov
|
@ -1,22 +0,0 @@
|
|||
# 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
|
6
Gemfile
6
Gemfile
|
@ -28,7 +28,7 @@ gem 'active_model_serializers', '~> 0.10'
|
|||
gem 'addressable', '~> 2.8'
|
||||
gem 'bootsnap', '~> 1.18.0', require: false
|
||||
gem 'browser'
|
||||
gem 'charlock_holmes', '~> 0.7.7'
|
||||
gem 'charlock_holmes', github: 'TheEssem/charlock_holmes', ref: '226932af4b03eb60d2e31d58b6c3efd72a3ace68'
|
||||
gem 'chewy', '~> 7.3'
|
||||
gem 'devise', '~> 4.9'
|
||||
gem 'devise-two-factor'
|
||||
|
@ -132,7 +132,7 @@ group :test do
|
|||
gem 'email_spec'
|
||||
|
||||
# Extra RSpec extension methods and helpers for sidekiq
|
||||
gem 'rspec-sidekiq', '~> 4.0'
|
||||
gem 'rspec-sidekiq', '~> 5.0'
|
||||
|
||||
# Browser integration testing
|
||||
gem 'capybara', '~> 3.39'
|
||||
|
@ -178,7 +178,7 @@ group :development do
|
|||
|
||||
# Preview mail in the browser
|
||||
gem 'letter_opener', '~> 1.8'
|
||||
gem 'letter_opener_web', '~> 2.0'
|
||||
gem 'letter_opener_web', '~> 3.0'
|
||||
|
||||
# Security analysis CLI tools
|
||||
gem 'brakeman', '~> 6.0', require: false
|
||||
|
|
162
Gemfile.lock
162
Gemfile.lock
|
@ -7,38 +7,45 @@ GIT
|
|||
hkdf (~> 0.2)
|
||||
jwt (~> 2.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/TheEssem/charlock_holmes.git
|
||||
revision: 226932af4b03eb60d2e31d58b6c3efd72a3ace68
|
||||
ref: 226932af4b03eb60d2e31d58b6c3efd72a3ace68
|
||||
specs:
|
||||
charlock_holmes (0.7.7)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (7.1.3.2)
|
||||
actionpack (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
actioncable (7.1.3.3)
|
||||
actionpack (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
zeitwerk (~> 2.6)
|
||||
actionmailbox (7.1.3.2)
|
||||
actionpack (= 7.1.3.2)
|
||||
activejob (= 7.1.3.2)
|
||||
activerecord (= 7.1.3.2)
|
||||
activestorage (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
actionmailbox (7.1.3.3)
|
||||
actionpack (= 7.1.3.3)
|
||||
activejob (= 7.1.3.3)
|
||||
activerecord (= 7.1.3.3)
|
||||
activestorage (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
mail (>= 2.7.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
actionmailer (7.1.3.2)
|
||||
actionpack (= 7.1.3.2)
|
||||
actionview (= 7.1.3.2)
|
||||
activejob (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
actionmailer (7.1.3.3)
|
||||
actionpack (= 7.1.3.3)
|
||||
actionview (= 7.1.3.3)
|
||||
activejob (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
rails-dom-testing (~> 2.2)
|
||||
actionpack (7.1.3.2)
|
||||
actionview (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
actionpack (7.1.3.3)
|
||||
actionview (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
nokogiri (>= 1.8.5)
|
||||
racc
|
||||
rack (>= 2.2.4)
|
||||
|
@ -46,15 +53,15 @@ GEM
|
|||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
actiontext (7.1.3.2)
|
||||
actionpack (= 7.1.3.2)
|
||||
activerecord (= 7.1.3.2)
|
||||
activestorage (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
actiontext (7.1.3.3)
|
||||
actionpack (= 7.1.3.3)
|
||||
activerecord (= 7.1.3.3)
|
||||
activestorage (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
actionview (7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.11)
|
||||
rails-dom-testing (~> 2.2)
|
||||
|
@ -64,22 +71,22 @@ GEM
|
|||
activemodel (>= 4.1)
|
||||
case_transform (>= 0.2)
|
||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||
activejob (7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
activejob (7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
activerecord (7.1.3.2)
|
||||
activemodel (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
activemodel (7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
activerecord (7.1.3.3)
|
||||
activemodel (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
timeout (>= 0.4.0)
|
||||
activestorage (7.1.3.2)
|
||||
actionpack (= 7.1.3.2)
|
||||
activejob (= 7.1.3.2)
|
||||
activerecord (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
activestorage (7.1.3.3)
|
||||
actionpack (= 7.1.3.3)
|
||||
activejob (= 7.1.3.3)
|
||||
activerecord (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
marcel (~> 1.0)
|
||||
activesupport (7.1.3.2)
|
||||
activesupport (7.1.3.3)
|
||||
base64
|
||||
bigdecimal
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
|
@ -100,16 +107,16 @@ GEM
|
|||
attr_required (1.0.2)
|
||||
awrence (1.2.1)
|
||||
aws-eventstream (1.3.0)
|
||||
aws-partitions (1.922.0)
|
||||
aws-sdk-core (3.194.1)
|
||||
aws-partitions (1.929.0)
|
||||
aws-sdk-core (3.196.1)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.8)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.80.0)
|
||||
aws-sdk-kms (1.81.0)
|
||||
aws-sdk-core (~> 3, >= 3.193.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.149.1)
|
||||
aws-sdk-s3 (1.151.0)
|
||||
aws-sdk-core (~> 3, >= 3.194.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.8)
|
||||
|
@ -159,7 +166,6 @@ GEM
|
|||
case_transform (0.2)
|
||||
activesupport
|
||||
cbor (0.5.9.8)
|
||||
charlock_holmes (0.7.7)
|
||||
chewy (7.6.0)
|
||||
activesupport (>= 5.2)
|
||||
elasticsearch (>= 7.14.0, < 8)
|
||||
|
@ -272,7 +278,7 @@ GEM
|
|||
fog-json (1.2.0)
|
||||
fog-core
|
||||
multi_json (~> 1.10)
|
||||
fog-openstack (1.1.0)
|
||||
fog-openstack (1.1.1)
|
||||
fog-core (~> 2.1)
|
||||
fog-json (>= 1.0)
|
||||
formatador (1.1.0)
|
||||
|
@ -389,10 +395,10 @@ GEM
|
|||
addressable (~> 2.8)
|
||||
letter_opener (1.10.0)
|
||||
launchy (>= 2.2, < 4)
|
||||
letter_opener_web (2.0.0)
|
||||
actionmailer (>= 5.2)
|
||||
letter_opener (~> 1.7)
|
||||
railties (>= 5.2)
|
||||
letter_opener_web (3.0.0)
|
||||
actionmailer (>= 6.1)
|
||||
letter_opener (~> 1.9)
|
||||
railties (>= 6.1)
|
||||
rexml
|
||||
link_header (0.0.8)
|
||||
llhttp-ffi (0.5.0)
|
||||
|
@ -422,7 +428,7 @@ GEM
|
|||
memory_profiler (1.0.1)
|
||||
mime-types (3.5.2)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2024.0305)
|
||||
mime-types-data (3.2024.0507)
|
||||
mini_mime (1.1.5)
|
||||
mini_portile2 (2.8.6)
|
||||
minitest (5.22.3)
|
||||
|
@ -434,7 +440,7 @@ GEM
|
|||
uri
|
||||
net-http-persistent (4.0.2)
|
||||
connection_pool (~> 2.2)
|
||||
net-imap (0.4.10)
|
||||
net-imap (0.4.11)
|
||||
date
|
||||
net-protocol
|
||||
net-ldap (0.19.0)
|
||||
|
@ -444,8 +450,8 @@ GEM
|
|||
timeout
|
||||
net-smtp (0.5.0)
|
||||
net-protocol
|
||||
nio4r (2.7.1)
|
||||
nokogiri (1.16.4)
|
||||
nio4r (2.7.3)
|
||||
nokogiri (1.16.5)
|
||||
mini_portile2 (~> 2.8.2)
|
||||
racc (~> 1.4)
|
||||
nsa (0.3.0)
|
||||
|
@ -543,7 +549,7 @@ GEM
|
|||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-common (~> 0.20.0)
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-instrumentation-pg (0.27.2)
|
||||
opentelemetry-instrumentation-pg (0.27.3)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-helpers-sql-obfuscation
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
|
@ -634,20 +640,20 @@ GEM
|
|||
rackup (1.0.0)
|
||||
rack (< 3)
|
||||
webrick
|
||||
rails (7.1.3.2)
|
||||
actioncable (= 7.1.3.2)
|
||||
actionmailbox (= 7.1.3.2)
|
||||
actionmailer (= 7.1.3.2)
|
||||
actionpack (= 7.1.3.2)
|
||||
actiontext (= 7.1.3.2)
|
||||
actionview (= 7.1.3.2)
|
||||
activejob (= 7.1.3.2)
|
||||
activemodel (= 7.1.3.2)
|
||||
activerecord (= 7.1.3.2)
|
||||
activestorage (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
rails (7.1.3.3)
|
||||
actioncable (= 7.1.3.3)
|
||||
actionmailbox (= 7.1.3.3)
|
||||
actionmailer (= 7.1.3.3)
|
||||
actionpack (= 7.1.3.3)
|
||||
actiontext (= 7.1.3.3)
|
||||
actionview (= 7.1.3.3)
|
||||
activejob (= 7.1.3.3)
|
||||
activemodel (= 7.1.3.3)
|
||||
activerecord (= 7.1.3.3)
|
||||
activestorage (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 7.1.3.2)
|
||||
railties (= 7.1.3.3)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
actionview (>= 5.0.1.rc1)
|
||||
|
@ -662,9 +668,9 @@ GEM
|
|||
rails-i18n (7.0.9)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 6.0.0, < 8)
|
||||
railties (7.1.3.2)
|
||||
actionpack (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
railties (7.1.3.3)
|
||||
actionpack (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
irb
|
||||
rackup (>= 1.0.0)
|
||||
rake (>= 12.2)
|
||||
|
@ -686,14 +692,15 @@ GEM
|
|||
redlock (1.3.2)
|
||||
redis (>= 3.0.0, < 6.0)
|
||||
regexp_parser (2.9.0)
|
||||
reline (0.5.6)
|
||||
reline (0.5.7)
|
||||
io-console (~> 0.5)
|
||||
request_store (1.6.0)
|
||||
rack (>= 1.4)
|
||||
responders (3.1.1)
|
||||
actionpack (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
rexml (3.2.6)
|
||||
rexml (3.2.8)
|
||||
strscan (>= 3.0.9)
|
||||
rotp (6.3.0)
|
||||
rouge (4.2.1)
|
||||
rpam2 (4.0.2)
|
||||
|
@ -708,7 +715,7 @@ GEM
|
|||
rspec-support (~> 3.13.0)
|
||||
rspec-github (2.4.0)
|
||||
rspec-core (~> 3.0)
|
||||
rspec-mocks (3.13.0)
|
||||
rspec-mocks (3.13.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.13.0)
|
||||
rspec-rails (6.1.2)
|
||||
|
@ -719,7 +726,7 @@ GEM
|
|||
rspec-expectations (~> 3.13)
|
||||
rspec-mocks (~> 3.13)
|
||||
rspec-support (~> 3.13)
|
||||
rspec-sidekiq (4.2.0)
|
||||
rspec-sidekiq (5.0.0)
|
||||
rspec-core (~> 3.0)
|
||||
rspec-expectations (~> 3.0)
|
||||
rspec-mocks (~> 3.0)
|
||||
|
@ -774,7 +781,7 @@ GEM
|
|||
scenic (1.8.0)
|
||||
activerecord (>= 4.0.0)
|
||||
railties (>= 4.0.0)
|
||||
selenium-webdriver (4.20.1)
|
||||
selenium-webdriver (4.21.1)
|
||||
base64 (~> 0.2)
|
||||
rexml (~> 3.2, >= 3.2.5)
|
||||
rubyzip (>= 1.2.2, < 3.0)
|
||||
|
@ -815,6 +822,7 @@ GEM
|
|||
stringio (3.1.0)
|
||||
strong_migrations (1.8.0)
|
||||
activerecord (>= 5.2)
|
||||
strscan (3.1.0)
|
||||
swd (1.3.0)
|
||||
activesupport (>= 3)
|
||||
attr_required (>= 0.0.5)
|
||||
|
@ -893,7 +901,7 @@ GEM
|
|||
xorcist (1.1.3)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.6.13)
|
||||
zeitwerk (2.6.14)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
@ -911,7 +919,7 @@ DEPENDENCIES
|
|||
browser
|
||||
bundler-audit (~> 0.9)
|
||||
capybara (~> 3.39)
|
||||
charlock_holmes (~> 0.7.7)
|
||||
charlock_holmes!
|
||||
chewy (~> 7.3)
|
||||
climate_control
|
||||
cocoon (~> 1.2)
|
||||
|
@ -955,7 +963,7 @@ DEPENDENCIES
|
|||
kaminari (~> 1.2)
|
||||
kt-paperclip (~> 7.2)
|
||||
letter_opener (~> 1.8)
|
||||
letter_opener_web (~> 2.0)
|
||||
letter_opener_web (~> 3.0)
|
||||
link_header (~> 0.0)
|
||||
lograge (~> 0.12)
|
||||
mail (~> 2.8)
|
||||
|
@ -1012,7 +1020,7 @@ DEPENDENCIES
|
|||
rqrcode (~> 2.2)
|
||||
rspec-github (~> 2.4)
|
||||
rspec-rails (~> 6.0)
|
||||
rspec-sidekiq (~> 4.0)
|
||||
rspec-sidekiq (~> 5.0)
|
||||
rubocop
|
||||
rubocop-capybara
|
||||
rubocop-performance
|
||||
|
|
|
@ -25,7 +25,7 @@ class AccountsController < ApplicationController
|
|||
|
||||
limit = params[:limit].present? ? [params[:limit].to_i, PAGE_SIZE_MAX].min : PAGE_SIZE
|
||||
@statuses = filtered_statuses.without_reblogs.limit(limit)
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
@statuses = preload_collection(@statuses, Status)
|
||||
end
|
||||
|
||||
format.json do
|
||||
|
|
|
@ -18,7 +18,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
|
|||
def set_items
|
||||
case params[:id]
|
||||
when 'featured'
|
||||
@items = for_signed_account { cache_collection(@account.pinned_statuses.not_local_only, Status) }
|
||||
@items = for_signed_account { preload_collection(@account.pinned_statuses.not_local_only, Status) }
|
||||
@items = @items.map { |item| item.distributable? ? item : ActivityPub::TagManager.instance.uri_for(item) }
|
||||
when 'tags'
|
||||
@items = for_signed_account { @account.featured_tags }
|
||||
|
|
|
@ -60,7 +60,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
|
|||
def set_statuses
|
||||
return unless page_requested?
|
||||
|
||||
@statuses = cache_collection_paginated_by_id(
|
||||
@statuses = preload_collection_paginated_by_id(
|
||||
AccountStatusesFilter.new(@account, signed_request_account).results,
|
||||
Status,
|
||||
LIMIT,
|
||||
|
|
|
@ -19,11 +19,11 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
|||
end
|
||||
|
||||
def load_statuses
|
||||
@account.unavailable? ? [] : cached_account_statuses
|
||||
@account.unavailable? ? [] : preloaded_account_statuses
|
||||
end
|
||||
|
||||
def cached_account_statuses
|
||||
cache_collection_paginated_by_id(
|
||||
def preloaded_account_statuses
|
||||
preload_collection_paginated_by_id(
|
||||
AccountStatusesFilter.new(@account, current_account, params).results,
|
||||
Status,
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
|
|
|
@ -4,6 +4,6 @@ class Api::V1::Apps::CredentialsController < Api::BaseController
|
|||
def show
|
||||
return doorkeeper_render_error unless valid_doorkeeper_token?
|
||||
|
||||
render json: doorkeeper_token.application, serializer: REST::ApplicationSerializer, fields: %i(name website vapid_key client_id scopes)
|
||||
render json: doorkeeper_token.application, serializer: REST::ApplicationSerializer
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ class Api::V1::AppsController < Api::BaseController
|
|||
|
||||
def create
|
||||
@app = Doorkeeper::Application.create!(application_options)
|
||||
render json: @app, serializer: REST::ApplicationSerializer
|
||||
render json: @app, serializer: REST::CredentialApplicationSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -24,6 +24,6 @@ class Api::V1::AppsController < Api::BaseController
|
|||
end
|
||||
|
||||
def app_params
|
||||
params.permit(:client_name, :redirect_uris, :scopes, :website)
|
||||
params.permit(:client_name, :scopes, :website, :redirect_uris, redirect_uris: [])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,11 +13,11 @@ class Api::V1::BookmarksController < Api::BaseController
|
|||
private
|
||||
|
||||
def load_statuses
|
||||
cached_bookmarks
|
||||
preloaded_bookmarks
|
||||
end
|
||||
|
||||
def cached_bookmarks
|
||||
cache_collection(results.map(&:status), Status)
|
||||
def preloaded_bookmarks
|
||||
preload_collection(results.map(&:status), Status)
|
||||
end
|
||||
|
||||
def results
|
||||
|
|
|
@ -13,11 +13,11 @@ class Api::V1::FavouritesController < Api::BaseController
|
|||
private
|
||||
|
||||
def load_statuses
|
||||
cached_favourites
|
||||
preloaded_favourites
|
||||
end
|
||||
|
||||
def cached_favourites
|
||||
cache_collection(results.map(&:status), Status)
|
||||
def preloaded_favourites
|
||||
preload_collection(results.map(&:status), Status)
|
||||
end
|
||||
|
||||
def results
|
||||
|
|
|
@ -41,7 +41,7 @@ class Api::V1::Notifications::RequestsController < Api::BaseController
|
|||
)
|
||||
|
||||
NotificationRequest.preload_cache_collection(requests) do |statuses|
|
||||
cache_collection(statuses, Status)
|
||||
preload_collection(statuses, Status)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ class Api::V1::NotificationsController < Api::BaseController
|
|||
)
|
||||
|
||||
Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses|
|
||||
cache_collection(target_statuses, Status)
|
||||
preload_collection(target_statuses, Status)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -26,13 +26,13 @@ class Api::V1::StatusesController < Api::BaseController
|
|||
DESCENDANTS_DEPTH_LIMIT = 20
|
||||
|
||||
def index
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
@statuses = preload_collection(@statuses, Status)
|
||||
render json: @statuses, each_serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
cache_if_unauthenticated!
|
||||
@status = cache_collection([@status], Status).first
|
||||
@status = preload_collection([@status], Status).first
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
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)
|
||||
descendants_results = @status.descendants(descendants_limit, current_account, descendants_depth_limit)
|
||||
loaded_ancestors = cache_collection(ancestors_results, Status)
|
||||
loaded_descendants = cache_collection(descendants_results, Status)
|
||||
loaded_ancestors = preload_collection(ancestors_results, Status)
|
||||
loaded_descendants = preload_collection(descendants_results, Status)
|
||||
|
||||
@context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
|
||||
statuses = [@status] + @context.ancestors + @context.descendants
|
||||
|
|
|
@ -15,11 +15,11 @@ class Api::V1::Timelines::DirectController < Api::BaseController
|
|||
private
|
||||
|
||||
def load_statuses
|
||||
cached_direct_statuses
|
||||
preloaded_direct_statuses
|
||||
end
|
||||
|
||||
def cached_direct_statuses
|
||||
cache_collection direct_statuses, Status
|
||||
def preloaded_direct_statuses
|
||||
preload_collection direct_statuses, Status
|
||||
end
|
||||
|
||||
def direct_statuses
|
||||
|
|
|
@ -21,11 +21,11 @@ class Api::V1::Timelines::HomeController < Api::V1::Timelines::BaseController
|
|||
private
|
||||
|
||||
def load_statuses
|
||||
cached_home_statuses
|
||||
preloaded_home_statuses
|
||||
end
|
||||
|
||||
def cached_home_statuses
|
||||
cache_collection home_statuses, Status
|
||||
def preloaded_home_statuses
|
||||
preload_collection home_statuses, Status
|
||||
end
|
||||
|
||||
def home_statuses
|
||||
|
|
|
@ -21,11 +21,11 @@ class Api::V1::Timelines::ListController < Api::V1::Timelines::BaseController
|
|||
end
|
||||
|
||||
def set_statuses
|
||||
@statuses = cached_list_statuses
|
||||
@statuses = preloaded_list_statuses
|
||||
end
|
||||
|
||||
def cached_list_statuses
|
||||
cache_collection list_statuses, Status
|
||||
def preloaded_list_statuses
|
||||
preload_collection list_statuses, Status
|
||||
end
|
||||
|
||||
def list_statuses
|
||||
|
|
|
@ -18,11 +18,11 @@ class Api::V1::Timelines::PublicController < Api::V1::Timelines::BaseController
|
|||
end
|
||||
|
||||
def load_statuses
|
||||
cached_public_statuses_page
|
||||
preloaded_public_statuses_page
|
||||
end
|
||||
|
||||
def cached_public_statuses_page
|
||||
cache_collection(public_statuses, Status)
|
||||
def preloaded_public_statuses_page
|
||||
preload_collection(public_statuses, Status)
|
||||
end
|
||||
|
||||
def public_statuses
|
||||
|
|
|
@ -23,11 +23,11 @@ class Api::V1::Timelines::TagController < Api::V1::Timelines::BaseController
|
|||
end
|
||||
|
||||
def load_statuses
|
||||
cached_tagged_statuses
|
||||
preloaded_tagged_statuses
|
||||
end
|
||||
|
||||
def cached_tagged_statuses
|
||||
@tag.nil? ? [] : cache_collection(tag_timeline_statuses, Status)
|
||||
def preloaded_tagged_statuses
|
||||
@tag.nil? ? [] : preload_collection(tag_timeline_statuses, Status)
|
||||
end
|
||||
|
||||
def tag_timeline_statuses
|
||||
|
|
|
@ -20,7 +20,7 @@ class Api::V1::Trends::StatusesController < Api::BaseController
|
|||
|
||||
def set_statuses
|
||||
@statuses = if enabled?
|
||||
cache_collection(statuses_from_trends.offset(offset_param).limit(limit_param(DEFAULT_STATUSES_LIMIT)), Status)
|
||||
preload_collection(statuses_from_trends.offset(offset_param).limit(limit_param(DEFAULT_STATUSES_LIMIT)), Status)
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
|
|
@ -9,6 +9,7 @@ class ApplicationController < ActionController::Base
|
|||
include UserTrackingConcern
|
||||
include SessionTrackingConcern
|
||||
include CacheConcern
|
||||
include PreloadingConcern
|
||||
include DomainControlHelper
|
||||
include ThemingConcern
|
||||
include DatabaseHelper
|
||||
|
|
|
@ -45,20 +45,4 @@ module CacheConcern
|
|||
Rails.cache.write(key, response.body, expires_in: expires_in, raw: true)
|
||||
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
|
||||
|
|
17
app/controllers/concerns/preloading_concern.rb
Normal file
17
app/controllers/concerns/preloading_concern.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# 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
|
|
@ -13,7 +13,7 @@ class Settings::ApplicationsController < Settings::BaseController
|
|||
def new
|
||||
@application = Doorkeeper::Application.new(
|
||||
redirect_uri: Doorkeeper.configuration.native_redirect_uri,
|
||||
scopes: 'read write follow'
|
||||
scopes: 'read:me'
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class TagsController < ApplicationController
|
|||
end
|
||||
|
||||
def set_statuses
|
||||
@statuses = cache_collection(TagFeed.new(@tag, nil, local: @local).get(limit_param), Status)
|
||||
@statuses = preload_collection(TagFeed.new(@tag, nil, local: @local).get(limit_param), Status)
|
||||
end
|
||||
|
||||
def limit_param
|
||||
|
|
|
@ -241,11 +241,20 @@ module ApplicationHelper
|
|||
EmojiFormatter.new(html, custom_emojis, other_options.merge(animate: prefers_autoplay?)).to_s
|
||||
end
|
||||
|
||||
def site_icon_path(type, size = '48')
|
||||
icon = SiteUpload.find_by(var: type)
|
||||
return nil unless icon
|
||||
def mascot_url
|
||||
full_asset_url(instance_presenter.mascot&.file&.url || frontend_asset_path('images/elephant_ui_plane.svg'))
|
||||
end
|
||||
|
||||
icon.file.url(size)
|
||||
def instance_presenter
|
||||
@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
|
||||
|
||||
# glitch-soc addition to handle the multiple flavors
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
# 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
|
|
@ -20,7 +20,7 @@ export function changeSetting(path, value) {
|
|||
}
|
||||
|
||||
const debouncedSave = debounce((dispatch, getState) => {
|
||||
if (getState().getIn(['settings', 'saved'])) {
|
||||
if (getState().getIn(['settings', 'saved']) || !getState().getIn(['meta', 'me'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -174,7 +174,6 @@ Account.propTypes = {
|
|||
onBlock: PropTypes.func,
|
||||
onMute: PropTypes.func,
|
||||
onMuteNotifications: PropTypes.func,
|
||||
intl: PropTypes.object.isRequired,
|
||||
hidden: PropTypes.bool,
|
||||
minimal: PropTypes.bool,
|
||||
defaultAction: PropTypes.string,
|
||||
|
|
|
@ -14,8 +14,10 @@ import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
|||
import SettingsIcon from '@/material-icons/400-24px/settings.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
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 { useAppHistory } from './router';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -51,12 +53,8 @@ BackButton.propTypes = {
|
|||
};
|
||||
|
||||
class ColumnHeader extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
intl: PropTypes.object.isRequired,
|
||||
title: PropTypes.node,
|
||||
icon: PropTypes.string,
|
||||
|
@ -171,7 +169,7 @@ class ColumnHeader extends PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
if (this.context.identity.signedIn && (children || (multiColumn && this.props.onPin))) {
|
||||
if (this.props.identity.signedIn && (children || (multiColumn && this.props.onPin))) {
|
||||
collapseButton = (
|
||||
<button
|
||||
className={collapsibleButtonClassName}
|
||||
|
@ -232,4 +230,4 @@ class ColumnHeader extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(withRouter(ColumnHeader));
|
||||
export default injectIntl(withIdentity(withRouter(ColumnHeader)));
|
||||
|
|
|
@ -14,6 +14,7 @@ import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
|||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import emojify from 'flavours/glitch/features/emoji/emoji';
|
||||
import Motion from 'flavours/glitch/features/ui/util/optional_motion';
|
||||
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
|
||||
|
||||
import { RelativeTimestamp } from './relative_timestamp';
|
||||
|
||||
|
@ -38,12 +39,8 @@ const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => {
|
|||
}, {});
|
||||
|
||||
class Poll extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
poll: ImmutablePropTypes.map,
|
||||
lang: PropTypes.string,
|
||||
intl: PropTypes.object.isRequired,
|
||||
|
@ -235,7 +232,7 @@ class Poll extends ImmutablePureComponent {
|
|||
</ul>
|
||||
|
||||
<div className='poll__footer'>
|
||||
{!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='button button-secondary' disabled={disabled || !this.props.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 && !this.props.disabled && <><button className='poll__link' onClick={this.handleRefresh}><FormattedMessage id='poll.refresh' defaultMessage='Refresh' /></button> · </>}
|
||||
{votesCount}
|
||||
|
@ -247,4 +244,4 @@ class Poll extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(Poll);
|
||||
export default injectIntl(withIdentity(Poll));
|
||||
|
|
|
@ -12,6 +12,7 @@ import { HotKeys } from 'react-hotkeys';
|
|||
import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder';
|
||||
import PollContainer from 'flavours/glitch/containers/poll_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 { withOptionalRouter, WithOptionalRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
|
@ -20,7 +21,6 @@ import Card from '../features/status/components/card';
|
|||
// to use the progress bar to show download progress
|
||||
import Bundle from '../features/ui/components/bundle';
|
||||
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 { displayMedia, visibleReactions } from '../initial_state';
|
||||
|
||||
|
@ -78,6 +78,7 @@ class Status extends ImmutablePureComponent {
|
|||
static contextType = SensitiveMediaContext;
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
containerId: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
status: ImmutablePropTypes.map,
|
||||
|
@ -545,6 +546,7 @@ class Status extends ImmutablePureComponent {
|
|||
nextInReplyToId,
|
||||
rootId,
|
||||
history,
|
||||
identity,
|
||||
...other
|
||||
} = this.props;
|
||||
const { isCollapsed } = this.state;
|
||||
|
@ -846,18 +848,14 @@ class Status extends ImmutablePureComponent {
|
|||
{...statusContentProps}
|
||||
/>
|
||||
|
||||
<IdentityConsumer>
|
||||
{identity => (
|
||||
<StatusReactions
|
||||
statusId={status.get('id')}
|
||||
reactions={status.get('reactions')}
|
||||
numVisible={visibleReactions}
|
||||
addReaction={this.props.onReactionAdd}
|
||||
removeReaction={this.props.onReactionRemove}
|
||||
canReact={identity.signedIn}
|
||||
/>
|
||||
)}
|
||||
</IdentityConsumer>
|
||||
<StatusReactions
|
||||
statusId={status.get('id')}
|
||||
reactions={status.get('reactions')}
|
||||
numVisible={visibleReactions}
|
||||
addReaction={this.props.onReactionAdd}
|
||||
removeReaction={this.props.onReactionRemove}
|
||||
canReact={this.props.identity.signedIn}
|
||||
/>
|
||||
|
||||
{(!isCollapsed || !(muted || !settings.getIn(['collapsed', 'show_action_bar']))) && (
|
||||
<StatusActionBar
|
||||
|
@ -881,4 +879,4 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withOptionalRouter(injectIntl(Status));
|
||||
export default withOptionalRouter(injectIntl((withIdentity(Status))));
|
||||
|
|
|
@ -22,6 +22,7 @@ import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
|
|||
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg';
|
||||
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg';
|
||||
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 { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
@ -70,12 +71,8 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
class StatusActionBar extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
onReply: PropTypes.func,
|
||||
onFavourite: PropTypes.func,
|
||||
|
@ -112,7 +109,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
];
|
||||
|
||||
handleReplyClick = () => {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
this.props.onReply(this.props.status, this.props.history);
|
||||
|
@ -128,7 +125,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleFavouriteClick = (e) => {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
this.props.onFavourite(this.props.status, e);
|
||||
|
@ -142,7 +139,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleReblogClick = e => {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
this.props.onReblog(this.props.status, e);
|
||||
|
@ -218,7 +215,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
|
||||
render () {
|
||||
const { status, intl, withDismiss, withCounters, showReplyCount, scrollKey } = this.props;
|
||||
const { permissions, signedIn } = this.context.identity;
|
||||
const { permissions, signedIn } = this.props.identity;
|
||||
|
||||
const mutingConversation = status.get('muted');
|
||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
||||
|
@ -370,4 +367,4 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(injectIntl(StatusActionBar));
|
||||
export default withRouter(withIdentity(injectIntl(StatusActionBar)));
|
||||
|
|
|
@ -15,6 +15,7 @@ import LinkIcon from '@/material-icons/400-24px/link.svg?react';
|
|||
import MovieIcon from '@/material-icons/400-24px/movie.svg?react';
|
||||
import MusicNoteIcon from '@/material-icons/400-24px/music_note.svg?react';
|
||||
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 { decode as decodeIDNA } from 'flavours/glitch/utils/idna';
|
||||
|
||||
|
@ -126,12 +127,8 @@ const mapStateToProps = state => ({
|
|||
});
|
||||
|
||||
class StatusContent extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
statusContent: PropTypes.string,
|
||||
expanded: PropTypes.bool,
|
||||
|
@ -349,7 +346,7 @@ class StatusContent extends PureComponent {
|
|||
const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
|
||||
const contentLocale = intl.locale.replace(/[_-].*/, '');
|
||||
const targetLanguages = this.props.languages?.get(status.get('language') || 'und');
|
||||
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 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 content = { __html: statusContent ?? getStatusContent(status) };
|
||||
const spoilerContent = { __html: status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml') };
|
||||
|
@ -503,4 +500,4 @@ class StatusContent extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(connect(mapStateToProps)(injectIntl(StatusContent)));
|
||||
export default withRouter(withIdentity(connect(mapStateToProps)(injectIntl(StatusContent))));
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
@ -15,6 +14,7 @@ import { connectUserStream } from 'flavours/glitch/actions/streaming';
|
|||
import ErrorBoundary from 'flavours/glitch/components/error_boundary';
|
||||
import { Router } from 'flavours/glitch/components/router';
|
||||
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 { IntlProvider } from 'flavours/glitch/locales';
|
||||
import { store } from 'flavours/glitch/store';
|
||||
|
@ -33,33 +33,9 @@ if (initialState.meta.me) {
|
|||
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 {
|
||||
|
||||
static childContextTypes = {
|
||||
identity: PropTypes.shape({
|
||||
signedIn: PropTypes.bool.isRequired,
|
||||
accountId: PropTypes.string,
|
||||
disabledAccountId: PropTypes.string,
|
||||
accessToken: PropTypes.string,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
identity = createIdentityContext(initialState);
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
identity: this.identity,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.identity.signedIn) {
|
||||
this.disconnect = store.dispatch(connectUserStream());
|
||||
|
@ -79,19 +55,21 @@ export default class Mastodon extends PureComponent {
|
|||
|
||||
render () {
|
||||
return (
|
||||
<IntlProvider>
|
||||
<ReduxProvider store={store}>
|
||||
<ErrorBoundary>
|
||||
<Router>
|
||||
<ScrollContext shouldUpdateScroll={this.shouldUpdateScroll}>
|
||||
<Route path='/' component={UI} />
|
||||
</ScrollContext>
|
||||
</Router>
|
||||
<IdentityContext.Provider value={this.identity}>
|
||||
<IntlProvider>
|
||||
<ReduxProvider store={store}>
|
||||
<ErrorBoundary>
|
||||
<Router>
|
||||
<ScrollContext shouldUpdateScroll={this.shouldUpdateScroll}>
|
||||
<Route path='/' component={UI} />
|
||||
</ScrollContext>
|
||||
</Router>
|
||||
|
||||
<Helmet defaultTitle={title} titleTemplate={`%s - ${title}`} />
|
||||
</ErrorBoundary>
|
||||
</ReduxProvider>
|
||||
</IntlProvider>
|
||||
<Helmet defaultTitle={title} titleTemplate={`%s - ${title}`} />
|
||||
</ErrorBoundary>
|
||||
</ReduxProvider>
|
||||
</IntlProvider>
|
||||
</IdentityContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import { Icon } from 'flavours/glitch/components/icon';
|
|||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
|
||||
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 { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
|
||||
import { preferencesLink, profileLink, accountAdminLink } from 'flavours/glitch/utils/backend_links';
|
||||
|
@ -94,6 +95,7 @@ const dateFormatOptions = {
|
|||
class Header extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
account: ImmutablePropTypes.record,
|
||||
identity_props: ImmutablePropTypes.list,
|
||||
onFollow: PropTypes.func.isRequired,
|
||||
|
@ -117,10 +119,6 @@ class Header extends ImmutablePureComponent {
|
|||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
openEditProfile = () => {
|
||||
window.open(profileLink, '_blank');
|
||||
};
|
||||
|
@ -170,7 +168,7 @@ class Header extends ImmutablePureComponent {
|
|||
|
||||
render () {
|
||||
const { account, hidden, intl } = this.props;
|
||||
const { signedIn, permissions } = this.context.identity;
|
||||
const { signedIn, permissions } = this.props.identity;
|
||||
|
||||
if (!account) {
|
||||
return null;
|
||||
|
@ -414,4 +412,4 @@ class Header extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(injectIntl(Header));
|
||||
export default withRouter(withIdentity(injectIntl(Header)));
|
||||
|
|
|
@ -9,6 +9,7 @@ import { connect } from 'react-redux';
|
|||
|
||||
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
||||
import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner';
|
||||
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
|
||||
import { domain } from 'flavours/glitch/initial_state';
|
||||
|
||||
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||
|
@ -40,16 +41,12 @@ const mapStateToProps = (state, { columnId }) => {
|
|||
};
|
||||
|
||||
class CommunityTimeline extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
onlyMedia: false,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
columnId: PropTypes.string,
|
||||
intl: PropTypes.object.isRequired,
|
||||
|
@ -80,7 +77,7 @@ class CommunityTimeline extends PureComponent {
|
|||
|
||||
componentDidMount () {
|
||||
const { dispatch, onlyMedia } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
dispatch(expandCommunityTimeline({ onlyMedia }));
|
||||
|
||||
|
@ -90,7 +87,7 @@ class CommunityTimeline extends PureComponent {
|
|||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (prevProps.onlyMedia !== this.props.onlyMedia) {
|
||||
const { dispatch, onlyMedia } = this.props;
|
||||
|
@ -165,4 +162,4 @@ class CommunityTimeline extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(CommunityTimeline));
|
||||
export default withIdentity(connect(mapStateToProps)(injectIntl(CommunityTimeline)));
|
||||
|
|
|
@ -12,6 +12,7 @@ import CancelIcon from '@/material-icons/400-24px/cancel-fill.svg?react';
|
|||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
|
||||
import { domain, searchEnabled } from 'flavours/glitch/initial_state';
|
||||
import { HASHTAG_REGEX } from 'flavours/glitch/utils/hashtags';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
@ -33,12 +34,8 @@ const labelForRecentSearch = search => {
|
|||
};
|
||||
|
||||
class Search extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
value: PropTypes.string.isRequired,
|
||||
recent: ImmutablePropTypes.orderedSet,
|
||||
submitted: PropTypes.bool,
|
||||
|
@ -276,7 +273,7 @@ class Search extends PureComponent {
|
|||
}
|
||||
|
||||
_calculateOptions (value) {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
const trimmedValue = value.trim();
|
||||
const options = [];
|
||||
|
||||
|
@ -318,7 +315,7 @@ class Search extends PureComponent {
|
|||
render () {
|
||||
const { intl, value, submitted, recent } = this.props;
|
||||
const { expanded, options, selectedOption } = this.state;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
const hasValue = value.length > 0 || submitted;
|
||||
|
||||
|
@ -402,4 +399,4 @@ class Search extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(injectIntl(Search));
|
||||
export default withRouter(withIdentity(injectIntl(Search)));
|
||||
|
|
|
@ -13,6 +13,7 @@ import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
|||
import Column from 'flavours/glitch/components/column';
|
||||
import ColumnHeader from 'flavours/glitch/components/column_header';
|
||||
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 Links from './links';
|
||||
|
@ -32,12 +33,8 @@ const mapStateToProps = state => ({
|
|||
});
|
||||
|
||||
class Explore extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
intl: PropTypes.object.isRequired,
|
||||
multiColumn: PropTypes.bool,
|
||||
isSearching: PropTypes.bool,
|
||||
|
@ -53,7 +50,7 @@ class Explore extends PureComponent {
|
|||
|
||||
render() {
|
||||
const { intl, multiColumn, isSearching } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||
|
@ -114,4 +111,4 @@ class Explore extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(Explore));
|
||||
export default withIdentity(connect(mapStateToProps)(injectIntl(Explore)));
|
||||
|
|
|
@ -6,6 +6,7 @@ import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
|
|||
import { Helmet } from 'react-helmet';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
|
||||
import { useIdentity } from '@/flavours/glitch/identity_context';
|
||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||
import { addColumn } from 'flavours/glitch/actions/columns';
|
||||
import { changeSetting } from 'flavours/glitch/actions/settings';
|
||||
|
@ -13,7 +14,7 @@ import { connectPublicStream, connectCommunityStream } from 'flavours/glitch/act
|
|||
import { expandPublicTimeline, expandCommunityTimeline } from 'flavours/glitch/actions/timelines';
|
||||
import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner';
|
||||
import SettingText from 'flavours/glitch/components/setting_text';
|
||||
import initialState, { domain } from 'flavours/glitch/initial_state';
|
||||
import { domain } from 'flavours/glitch/initial_state';
|
||||
import { useAppDispatch, useAppSelector } from 'flavours/glitch/store';
|
||||
|
||||
import Column from '../../components/column';
|
||||
|
@ -26,15 +27,6 @@ const messages = defineMessages({
|
|||
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 intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
|
|
@ -28,6 +28,7 @@ import { fetchLists } from 'flavours/glitch/actions/lists';
|
|||
import { openModal } from 'flavours/glitch/actions/modal';
|
||||
import Column from 'flavours/glitch/features/ui/components/column';
|
||||
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';
|
||||
|
||||
|
||||
|
@ -99,12 +100,8 @@ const badgeDisplay = (number, limit) => {
|
|||
};
|
||||
|
||||
class GettingStarted extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
intl: PropTypes.object.isRequired,
|
||||
myAccount: ImmutablePropTypes.record,
|
||||
columns: ImmutablePropTypes.list,
|
||||
|
@ -123,7 +120,7 @@ class GettingStarted extends ImmutablePureComponent {
|
|||
|
||||
componentDidMount () {
|
||||
const { fetchFollowRequests } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (!signedIn) {
|
||||
return;
|
||||
|
@ -134,7 +131,7 @@ class GettingStarted extends ImmutablePureComponent {
|
|||
|
||||
render () {
|
||||
const { intl, myAccount, columns, multiColumn, unreadFollowRequests, unreadNotifications, lists, openSettings } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
const navItems = [];
|
||||
let listItems = [];
|
||||
|
@ -219,4 +216,4 @@ class GettingStarted extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(makeMapStateToProps, mapDispatchToProps)(injectIntl(GettingStarted));
|
||||
export default withIdentity(connect(makeMapStateToProps, mapDispatchToProps)(injectIntl(GettingStarted)));
|
||||
|
|
|
@ -16,7 +16,7 @@ import { openModal } from 'flavours/glitch/actions/modal';
|
|||
import Column from 'flavours/glitch/features/ui/components/column';
|
||||
import ColumnLink from 'flavours/glitch/features/ui/components/column_link';
|
||||
import ColumnSubheading from 'flavours/glitch/features/ui/components/column_subheading';
|
||||
|
||||
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.heading', defaultMessage: 'Misc' },
|
||||
|
@ -32,11 +32,8 @@ const messages = defineMessages({
|
|||
|
||||
class GettingStartedMisc extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
intl: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
};
|
||||
|
@ -49,7 +46,7 @@ class GettingStartedMisc extends ImmutablePureComponent {
|
|||
|
||||
render () {
|
||||
const { intl } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
return (
|
||||
<Column icon='ellipsis-h' iconComponent={MoreHorizIcon} heading={intl.formatMessage(messages.heading)} alwaysShowBackButton>
|
||||
|
@ -69,4 +66,4 @@ class GettingStartedMisc extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect()(injectIntl(GettingStartedMisc));
|
||||
export default connect()(withIdentity(injectIntl(GettingStartedMisc)));
|
||||
|
|
|
@ -17,6 +17,7 @@ import { fetchHashtag, followHashtag, unfollowHashtag } from 'flavours/glitch/ac
|
|||
import { expandHashtagTimeline, clearTimeline } from 'flavours/glitch/actions/timelines';
|
||||
import Column from 'flavours/glitch/components/column';
|
||||
import ColumnHeader from 'flavours/glitch/components/column_header';
|
||||
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
|
||||
|
||||
import StatusListContainer from '../ui/containers/status_list_container';
|
||||
|
||||
|
@ -29,14 +30,10 @@ const mapStateToProps = (state, props) => ({
|
|||
});
|
||||
|
||||
class HashtagTimeline extends PureComponent {
|
||||
|
||||
disconnects = [];
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
params: PropTypes.object.isRequired,
|
||||
columnId: PropTypes.string,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
|
@ -94,7 +91,7 @@ class HashtagTimeline extends PureComponent {
|
|||
};
|
||||
|
||||
_subscribe (dispatch, id, tags = {}, local) {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (!signedIn) {
|
||||
return;
|
||||
|
@ -168,7 +165,7 @@ class HashtagTimeline extends PureComponent {
|
|||
handleFollow = () => {
|
||||
const { dispatch, params, tag } = this.props;
|
||||
const { id } = params;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (!signedIn) {
|
||||
return;
|
||||
|
@ -185,7 +182,7 @@ class HashtagTimeline extends PureComponent {
|
|||
const { hasUnread, columnId, multiColumn, tag } = this.props;
|
||||
const { id, local } = this.props.params;
|
||||
const pinned = !!columnId;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={`#${id}`}>
|
||||
|
@ -225,4 +222,4 @@ class HashtagTimeline extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(HashtagTimeline);
|
||||
export default connect(mapStateToProps)(withIdentity(HashtagTimeline));
|
||||
|
|
|
@ -14,6 +14,7 @@ import { fetchAnnouncements, toggleShowAnnouncements } from 'flavours/glitch/act
|
|||
import { IconWithBadge } from 'flavours/glitch/components/icon_with_badge';
|
||||
import { NotSignedInIndicator } from 'flavours/glitch/components/not_signed_in_indicator';
|
||||
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 { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||
|
@ -41,12 +42,8 @@ const mapStateToProps = state => ({
|
|||
});
|
||||
|
||||
class HomeTimeline extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
hasUnread: PropTypes.bool,
|
||||
|
@ -128,7 +125,7 @@ class HomeTimeline extends PureComponent {
|
|||
render () {
|
||||
const { intl, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props;
|
||||
const pinned = !!columnId;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
const banners = [];
|
||||
|
||||
let announcementsButton;
|
||||
|
@ -192,4 +189,4 @@ class HomeTimeline extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(HomeTimeline));
|
||||
export default connect(mapStateToProps)(withIdentity(injectIntl(HomeTimeline)));
|
||||
|
|
|
@ -5,6 +5,7 @@ import { FormattedMessage } from 'react-intl';
|
|||
|
||||
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 { CheckboxWithLabel } from './checkbox_with_label';
|
||||
|
@ -13,13 +14,9 @@ import GrantPermissionButton from './grant_permission_button';
|
|||
import PillBarButton from './pill_bar_button';
|
||||
import SettingToggle from './setting_toggle';
|
||||
|
||||
export default class ColumnSettings extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
class ColumnSettings extends PureComponent {
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
settings: ImmutablePropTypes.map.isRequired,
|
||||
pushSettings: ImmutablePropTypes.map.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
|
@ -227,7 +224,7 @@ export default class ColumnSettings extends PureComponent {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
{((this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) && (
|
||||
{((this.props.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) && (
|
||||
<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>
|
||||
|
||||
|
@ -240,7 +237,7 @@ export default class ColumnSettings extends PureComponent {
|
|||
</section>
|
||||
)}
|
||||
|
||||
{((this.context.identity.permissions & PERMISSION_MANAGE_REPORTS) === PERMISSION_MANAGE_REPORTS) && (
|
||||
{((this.props.identity.permissions & PERMISSION_MANAGE_REPORTS) === PERMISSION_MANAGE_REPORTS) && (
|
||||
<section role='group' aria-labelledby='notifications-admin-report'>
|
||||
<h3 id='notifications-status'><FormattedMessage id='notifications.column_settings.admin.report' defaultMessage='New reports:' /></h3>
|
||||
|
||||
|
@ -257,3 +254,5 @@ export default class ColumnSettings extends PureComponent {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
export default withIdentity(ColumnSettings);
|
||||
|
|
|
@ -19,6 +19,7 @@ import NotificationsIcon from '@/material-icons/400-24px/notifications-fill.svg?
|
|||
import { compareId } from 'flavours/glitch/compare_id';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
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 { submitMarkers } from '../../actions/markers';
|
||||
|
@ -93,12 +94,8 @@ const mapDispatchToProps = dispatch => ({
|
|||
});
|
||||
|
||||
class Notifications extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
columnId: PropTypes.string,
|
||||
notifications: ImmutablePropTypes.list.isRequired,
|
||||
showFilterBar: PropTypes.bool.isRequired,
|
||||
|
@ -225,7 +222,7 @@ class Notifications extends PureComponent {
|
|||
const { animatingNCD } = this.state;
|
||||
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 { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
let scrollableContent = null;
|
||||
|
||||
|
@ -373,4 +370,4 @@ class Notifications extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(Notifications));
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(withIdentity(injectIntl(Notifications)));
|
||||
|
|
|
@ -18,6 +18,7 @@ import { replyCompose } from 'flavours/glitch/actions/compose';
|
|||
import { reblog, favourite, unreblog, unfavourite } from 'flavours/glitch/actions/interactions';
|
||||
import { openModal } from 'flavours/glitch/actions/modal';
|
||||
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 { makeGetStatus } from 'flavours/glitch/selectors';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
@ -48,12 +49,8 @@ const makeMapStateToProps = () => {
|
|||
};
|
||||
|
||||
class Footer extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
statusId: PropTypes.string.isRequired,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
|
@ -77,7 +74,7 @@ class Footer extends ImmutablePureComponent {
|
|||
|
||||
handleReplyClick = () => {
|
||||
const { dispatch, askReplyConfirmation, status, intl } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (askReplyConfirmation) {
|
||||
|
@ -106,7 +103,7 @@ class Footer extends ImmutablePureComponent {
|
|||
|
||||
handleFavouriteClick = () => {
|
||||
const { dispatch, status } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (status.get('favourited')) {
|
||||
|
@ -133,7 +130,7 @@ class Footer extends ImmutablePureComponent {
|
|||
|
||||
handleReblogClick = e => {
|
||||
const { dispatch, status } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (status.get('reblogged')) {
|
||||
|
@ -236,4 +233,4 @@ class Footer extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(makeMapStateToProps)(withRouter(injectIntl(Footer)));
|
||||
export default connect(makeMapStateToProps)(withIdentity(withRouter(injectIntl(Footer))));
|
||||
|
|
|
@ -9,6 +9,7 @@ import { connect } from 'react-redux';
|
|||
|
||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||
import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner';
|
||||
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
|
||||
import { domain } from 'flavours/glitch/initial_state';
|
||||
|
||||
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||
|
@ -44,16 +45,12 @@ const mapStateToProps = (state, { columnId }) => {
|
|||
};
|
||||
|
||||
class PublicTimeline extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
onlyMedia: false,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
columnId: PropTypes.string,
|
||||
|
@ -86,7 +83,7 @@ class PublicTimeline extends PureComponent {
|
|||
|
||||
componentDidMount () {
|
||||
const { dispatch, onlyMedia, onlyRemote, allowLocalOnly } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
dispatch(expandPublicTimeline({ onlyMedia, onlyRemote, allowLocalOnly }));
|
||||
if (signedIn) {
|
||||
|
@ -95,7 +92,7 @@ class PublicTimeline extends PureComponent {
|
|||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (prevProps.onlyMedia !== this.props.onlyMedia || prevProps.onlyRemote !== this.props.onlyRemote || prevProps.allowLocalOnly !== this.props.allowLocalOnly) {
|
||||
const { dispatch, onlyMedia, onlyRemote, allowLocalOnly } = this.props;
|
||||
|
@ -170,4 +167,4 @@ class PublicTimeline extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(PublicTimeline));
|
||||
export default connect(mapStateToProps)(withIdentity(injectIntl(PublicTimeline)));
|
||||
|
|
|
@ -21,6 +21,7 @@ import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
|
|||
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
|
||||
import RepeatPrivateIcon from '@/svg-icons/repeat_private.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 { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
@ -62,12 +63,8 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
class ActionBar extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
onReply: PropTypes.func.isRequired,
|
||||
onReblog: PropTypes.func.isRequired,
|
||||
|
@ -165,7 +162,7 @@ class ActionBar extends PureComponent {
|
|||
|
||||
render () {
|
||||
const { status, intl } = this.props;
|
||||
const { signedIn, permissions } = this.context.identity;
|
||||
const { signedIn, permissions } = this.props.identity;
|
||||
|
||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
||||
const pinnableStatus = ['public', 'unlisted', 'private'].includes(status.get('visibility'));
|
||||
|
@ -276,4 +273,4 @@ class ActionBar extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(injectIntl(ActionBar));
|
||||
export default withRouter(withIdentity(injectIntl(ActionBar)));
|
||||
|
|
|
@ -15,6 +15,7 @@ import { getHashtagBarForStatus } from 'flavours/glitch/components/hashtag_bar';
|
|||
import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder';
|
||||
import { VisibilityIcon } from 'flavours/glitch/components/visibility_icon';
|
||||
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 { Avatar } from '../../../components/avatar';
|
||||
|
@ -29,12 +30,8 @@ import Video from '../../video';
|
|||
import Card from './card';
|
||||
|
||||
class DetailedStatus extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
status: ImmutablePropTypes.map,
|
||||
settings: ImmutablePropTypes.map.isRequired,
|
||||
onOpenMedia: PropTypes.func.isRequired,
|
||||
|
@ -319,7 +316,7 @@ class DetailedStatus extends ImmutablePureComponent {
|
|||
reactions={status.get('reactions')}
|
||||
addReaction={this.props.onReactionAdd}
|
||||
removeReaction={this.props.onReactionRemove}
|
||||
canReact={this.context.identity.signedIn}
|
||||
canReact={this.props.identity.signedIn}
|
||||
/>
|
||||
|
||||
<div className='detailed-status__meta'>
|
||||
|
@ -348,4 +345,4 @@ class DetailedStatus extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(DetailedStatus);
|
||||
export default withRouter(withIdentity(DetailedStatus));
|
||||
|
|
|
@ -21,6 +21,7 @@ import { Icon } from 'flavours/glitch/components/icon';
|
|||
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
|
||||
import ScrollContainer from 'flavours/glitch/containers/scroll_container';
|
||||
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 { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
|
@ -187,12 +188,8 @@ const titleFromStatus = (intl, status) => {
|
|||
};
|
||||
|
||||
class Status extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
status: ImmutablePropTypes.map,
|
||||
|
@ -279,7 +276,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
handleFavouriteClick = (status, e) => {
|
||||
const { dispatch } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (status.get('favourited')) {
|
||||
|
@ -310,8 +307,8 @@ class Status extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleReactionAdd = (statusId, name, url) => {
|
||||
const { dispatch } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { dispatch, identity } = this.props;
|
||||
const { signedIn } = identity;
|
||||
|
||||
if (signedIn) {
|
||||
dispatch(addReaction(statusId, name, url));
|
||||
|
@ -332,7 +329,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
handleReplyClick = (status) => {
|
||||
const { askReplyConfirmation, dispatch, intl } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (askReplyConfirmation) {
|
||||
|
@ -372,7 +369,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
handleReblogClick = (status, e) => {
|
||||
const { settings, dispatch } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (settings.get('confirm_boost_missing_media_description') && status.get('media_attachments').some(item => !item.get('description')) && !status.get('reblogged')) {
|
||||
|
@ -810,4 +807,4 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(injectIntl(connect(makeMapStateToProps)(Status)));
|
||||
export default withRouter(injectIntl(connect(makeMapStateToProps)(withIdentity(Status))));
|
||||
|
|
|
@ -7,16 +7,13 @@ import { mountCompose, unmountCompose } from 'flavours/glitch/actions/compose';
|
|||
import ServerBanner from 'flavours/glitch/components/server_banner';
|
||||
import ComposeFormContainer from 'flavours/glitch/features/compose/containers/compose_form_container';
|
||||
import SearchContainer from 'flavours/glitch/features/compose/containers/search_container';
|
||||
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
|
||||
|
||||
import LinkFooter from './link_footer';
|
||||
|
||||
class ComposePanel extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
@ -31,7 +28,7 @@ class ComposePanel extends PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
return (
|
||||
<div className='compose-panel'>
|
||||
|
@ -55,4 +52,4 @@ class ComposePanel extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect()(ComposePanel);
|
||||
export default connect()(withIdentity(ComposePanel));
|
||||
|
|
|
@ -14,6 +14,7 @@ import { Avatar } from 'flavours/glitch/components/avatar';
|
|||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { WordmarkLogo, SymbolLogo } from 'flavours/glitch/components/logo';
|
||||
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';
|
||||
|
||||
const Account = connect(state => ({
|
||||
|
@ -42,12 +43,8 @@ const mapDispatchToProps = (dispatch) => ({
|
|||
});
|
||||
|
||||
class Header extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
openClosedRegistrationsModal: PropTypes.func,
|
||||
location: PropTypes.object,
|
||||
signupUrl: PropTypes.string.isRequired,
|
||||
|
@ -61,7 +58,7 @@ class Header extends PureComponent {
|
|||
}
|
||||
|
||||
render () {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
const { location, openClosedRegistrationsModal, signupUrl, intl } = this.props;
|
||||
|
||||
let content;
|
||||
|
@ -122,4 +119,4 @@ class Header extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(withRouter(connect(mapStateToProps, mapDispatchToProps)(Header)));
|
||||
export default injectIntl(withRouter(withIdentity(connect(mapStateToProps, mapDispatchToProps)(Header))));
|
||||
|
|
|
@ -8,6 +8,7 @@ import { Link } from 'react-router-dom';
|
|||
import { connect } from 'react-redux';
|
||||
|
||||
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 { PERMISSION_INVITE_USERS } from 'flavours/glitch/permissions';
|
||||
import { logOut } from 'flavours/glitch/utils/log_out';
|
||||
|
@ -32,12 +33,8 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
});
|
||||
|
||||
class LinkFooter extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
multiColumn: PropTypes.bool,
|
||||
onLogout: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
|
@ -53,7 +50,7 @@ class LinkFooter extends PureComponent {
|
|||
};
|
||||
|
||||
render () {
|
||||
const { signedIn, permissions } = this.context.identity;
|
||||
const { signedIn, permissions } = this.props.identity;
|
||||
const { multiColumn } = this.props;
|
||||
|
||||
const canInvite = signedIn && ((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS);
|
||||
|
@ -108,4 +105,4 @@ class LinkFooter extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(connect(null, mapDispatchToProps)(LinkFooter));
|
||||
export default injectIntl(withIdentity(connect(null, mapDispatchToProps)(LinkFooter)));
|
||||
|
|
|
@ -30,6 +30,7 @@ import StarIcon from '@/material-icons/400-24px/star.svg?react';
|
|||
import { fetchFollowRequests } from 'flavours/glitch/actions/accounts';
|
||||
import { IconWithBadge } from 'flavours/glitch/components/icon_with_badge';
|
||||
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 { transientSingleColumn } from 'flavours/glitch/is_mobile';
|
||||
import { preferencesLink } from 'flavours/glitch/utils/backend_links';
|
||||
|
@ -98,12 +99,8 @@ const FollowRequestsLink = () => {
|
|||
};
|
||||
|
||||
class NavigationPanel extends Component {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
intl: PropTypes.object.isRequired,
|
||||
onOpenSettings: PropTypes.func,
|
||||
};
|
||||
|
@ -114,7 +111,7 @@ class NavigationPanel extends Component {
|
|||
|
||||
render () {
|
||||
const { intl, onOpenSettings } = this.props;
|
||||
const { signedIn, disabledAccountId } = this.context.identity;
|
||||
const { signedIn, disabledAccountId } = this.props.identity;
|
||||
|
||||
let banner = undefined;
|
||||
|
||||
|
@ -188,4 +185,4 @@ class NavigationPanel extends Component {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(NavigationPanel);
|
||||
export default injectIntl(withIdentity(NavigationPanel));
|
||||
|
|
|
@ -17,6 +17,7 @@ import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'flavour
|
|||
import { INTRODUCTION_VERSION } from 'flavours/glitch/actions/onboarding';
|
||||
import { Permalink } from 'flavours/glitch/components/permalink';
|
||||
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 { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
|
@ -129,12 +130,8 @@ const keyMap = {
|
|||
};
|
||||
|
||||
class SwitchingColumnsArea extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
children: PropTypes.node,
|
||||
location: PropTypes.object,
|
||||
singleColumn: PropTypes.bool,
|
||||
|
@ -169,7 +166,7 @@ class SwitchingColumnsArea extends PureComponent {
|
|||
|
||||
render () {
|
||||
const { children, singleColumn } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
const pathName = this.props.location.pathname;
|
||||
|
||||
let redirect;
|
||||
|
@ -262,12 +259,8 @@ class SwitchingColumnsArea extends PureComponent {
|
|||
}
|
||||
|
||||
class UI extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
children: PropTypes.node,
|
||||
isWide: PropTypes.bool,
|
||||
|
@ -323,7 +316,7 @@ class UI extends PureComponent {
|
|||
this.dragTargets.push(e.target);
|
||||
}
|
||||
|
||||
if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore && this.context.identity.signedIn) {
|
||||
if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore && this.props.identity.signedIn) {
|
||||
this.setState({ draggingOver: true });
|
||||
}
|
||||
};
|
||||
|
@ -351,7 +344,7 @@ class UI extends PureComponent {
|
|||
this.setState({ draggingOver: false });
|
||||
this.dragTargets = [];
|
||||
|
||||
if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore && this.context.identity.signedIn) {
|
||||
if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore && this.props.identity.signedIn) {
|
||||
this.props.dispatch(uploadCompose(e.dataTransfer.files));
|
||||
}
|
||||
};
|
||||
|
@ -403,7 +396,7 @@ class UI extends PureComponent {
|
|||
};
|
||||
|
||||
componentDidMount () {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
window.addEventListener('beforeunload', this.handleBeforeUnload, false);
|
||||
window.addEventListener('resize', this.handleResize, { passive: true });
|
||||
|
@ -649,7 +642,7 @@ class UI extends PureComponent {
|
|||
|
||||
<Header />
|
||||
|
||||
<SwitchingColumnsArea location={location} singleColumn={layout === 'mobile' || layout === 'single-column'}>
|
||||
<SwitchingColumnsArea identity={this.props.identity} location={location} singleColumn={layout === 'mobile' || layout === 'single-column'}>
|
||||
{children}
|
||||
</SwitchingColumnsArea>
|
||||
|
||||
|
@ -665,4 +658,4 @@ class UI extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(withRouter(UI)));
|
||||
export default connect(mapStateToProps)(injectIntl(withRouter(withIdentity(UI))));
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
74
app/javascript/flavours/glitch/identity_context.tsx
Normal file
74
app/javascript/flavours/glitch/identity_context.tsx
Normal file
|
@ -0,0 +1,74 @@
|
|||
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);
|
||||
}
|
|
@ -52,12 +52,22 @@
|
|||
* @property {string} default_content_type
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef Role
|
||||
* @property {string} id
|
||||
* @property {string} name
|
||||
* @property {string} permissions
|
||||
* @property {string} color
|
||||
* @property {boolean} highlighted
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef InitialState
|
||||
* @property {Record<string, import("./api_types/accounts").ApiAccountJSON>} accounts
|
||||
* @property {InitialStateLanguage[]} languages
|
||||
* @property {boolean=} critical_updates_pending
|
||||
* @property {InitialStateMeta} meta
|
||||
* @property {Role?} role
|
||||
* @property {object} local_settings
|
||||
* @property {number} max_feed_hashtags
|
||||
* @property {number} poll_limits
|
||||
|
|
|
@ -4621,6 +4621,10 @@ a.status-card {
|
|||
&:hover {
|
||||
color: $primary-text-color;
|
||||
}
|
||||
|
||||
.icon {
|
||||
transform: rotate(60deg);
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
|
@ -4668,6 +4672,10 @@ a.status-card {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
.no-reduce-motion .column-header__button .icon {
|
||||
transition: transform 150ms ease-in-out;
|
||||
}
|
||||
|
||||
.column-header__collapsible {
|
||||
max-height: 70vh;
|
||||
overflow: hidden;
|
||||
|
|
|
@ -20,7 +20,7 @@ export function changeSetting(path, value) {
|
|||
}
|
||||
|
||||
const debouncedSave = debounce((dispatch, getState) => {
|
||||
if (getState().getIn(['settings', 'saved'])) {
|
||||
if (getState().getIn(['settings', 'saved']) || !getState().getIn(['meta', 'me'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -172,7 +172,6 @@ Account.propTypes = {
|
|||
onBlock: PropTypes.func,
|
||||
onMute: PropTypes.func,
|
||||
onMuteNotifications: PropTypes.func,
|
||||
intl: PropTypes.object.isRequired,
|
||||
hidden: PropTypes.bool,
|
||||
minimal: PropTypes.bool,
|
||||
defaultAction: PropTypes.string,
|
||||
|
|
|
@ -14,8 +14,10 @@ import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
|||
import SettingsIcon from '@/material-icons/400-24px/settings.svg?react';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { ButtonInTabsBar } from 'mastodon/features/ui/util/columns_context';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
|
||||
import { useAppHistory } from './router';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -51,12 +53,8 @@ BackButton.propTypes = {
|
|||
};
|
||||
|
||||
class ColumnHeader extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
intl: PropTypes.object.isRequired,
|
||||
title: PropTypes.node,
|
||||
icon: PropTypes.string,
|
||||
|
@ -171,7 +169,7 @@ class ColumnHeader extends PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
if (this.context.identity.signedIn && (children || (multiColumn && this.props.onPin))) {
|
||||
if (this.props.identity.signedIn && (children || (multiColumn && this.props.onPin))) {
|
||||
collapseButton = (
|
||||
<button
|
||||
className={collapsibleButtonClassName}
|
||||
|
@ -232,4 +230,4 @@ class ColumnHeader extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(withRouter(ColumnHeader));
|
||||
export default injectIntl(withIdentity(withRouter(ColumnHeader)));
|
||||
|
|
|
@ -14,6 +14,7 @@ import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
|||
import { Icon } from 'mastodon/components/icon';
|
||||
import emojify from 'mastodon/features/emoji/emoji';
|
||||
import Motion from 'mastodon/features/ui/util/optional_motion';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
|
||||
import { RelativeTimestamp } from './relative_timestamp';
|
||||
|
||||
|
@ -38,12 +39,8 @@ const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => {
|
|||
}, {});
|
||||
|
||||
class Poll extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
poll: ImmutablePropTypes.map,
|
||||
lang: PropTypes.string,
|
||||
intl: PropTypes.object.isRequired,
|
||||
|
@ -235,7 +232,7 @@ class Poll extends ImmutablePureComponent {
|
|||
</ul>
|
||||
|
||||
<div className='poll__footer'>
|
||||
{!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='button button-secondary' disabled={disabled || !this.props.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 && !this.props.disabled && <><button className='poll__link' onClick={this.handleRefresh}><FormattedMessage id='poll.refresh' defaultMessage='Refresh' /></button> · </>}
|
||||
{votesCount}
|
||||
|
@ -247,4 +244,4 @@ class Poll extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(Poll);
|
||||
export default injectIntl(withIdentity(Poll));
|
||||
|
|
|
@ -22,6 +22,7 @@ import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
|
|||
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
|
||||
import RepeatPrivateIcon from '@/svg-icons/repeat_private.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 { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
|
@ -74,12 +75,8 @@ const mapStateToProps = (state, { status }) => ({
|
|||
});
|
||||
|
||||
class StatusActionBar extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
relationship: ImmutablePropTypes.record,
|
||||
onReply: PropTypes.func,
|
||||
|
@ -118,7 +115,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
];
|
||||
|
||||
handleReplyClick = () => {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
this.props.onReply(this.props.status, this.props.history);
|
||||
|
@ -136,7 +133,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleFavouriteClick = () => {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
this.props.onFavourite(this.props.status);
|
||||
|
@ -146,7 +143,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleReblogClick = e => {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
this.props.onReblog(this.props.status, e);
|
||||
|
@ -250,7 +247,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
|
||||
render () {
|
||||
const { status, relationship, intl, withDismiss, withCounters, scrollKey } = this.props;
|
||||
const { signedIn, permissions } = this.context.identity;
|
||||
const { signedIn, permissions } = this.props.identity;
|
||||
|
||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
||||
const pinnableStatus = ['public', 'unlisted', 'private'].includes(status.get('visibility'));
|
||||
|
@ -410,4 +407,4 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(connect(mapStateToProps)(injectIntl(StatusActionBar)));
|
||||
export default withRouter(withIdentity(connect(mapStateToProps)(injectIntl(StatusActionBar))));
|
||||
|
|
|
@ -12,8 +12,10 @@ import { connect } from 'react-redux';
|
|||
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import PollContainer from 'mastodon/containers/poll_container';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state';
|
||||
|
||||
|
||||
const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
|
||||
|
||||
/**
|
||||
|
@ -67,12 +69,8 @@ const mapStateToProps = state => ({
|
|||
});
|
||||
|
||||
class StatusContent extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
statusContent: PropTypes.string,
|
||||
expanded: PropTypes.bool,
|
||||
|
@ -245,7 +243,7 @@ class StatusContent extends PureComponent {
|
|||
const renderReadMore = this.props.onClick && status.get('collapsed');
|
||||
const contentLocale = intl.locale.replace(/[_-].*/, '');
|
||||
const targetLanguages = this.props.languages?.get(status.get('language') || 'und');
|
||||
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 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 content = { __html: statusContent ?? getStatusContent(status) };
|
||||
const spoilerContent = { __html: status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml') };
|
||||
|
@ -328,4 +326,4 @@ class StatusContent extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(connect(mapStateToProps)(injectIntl(StatusContent)));
|
||||
export default withRouter(withIdentity(connect(mapStateToProps)(injectIntl(StatusContent))));
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
@ -14,6 +13,7 @@ import { connectUserStream } from 'mastodon/actions/streaming';
|
|||
import ErrorBoundary from 'mastodon/components/error_boundary';
|
||||
import { Router } from 'mastodon/components/router';
|
||||
import UI from 'mastodon/features/ui';
|
||||
import { IdentityContext, createIdentityContext } from 'mastodon/identity_context';
|
||||
import initialState, { title as siteTitle } from 'mastodon/initial_state';
|
||||
import { IntlProvider } from 'mastodon/locales';
|
||||
import { store } from 'mastodon/store';
|
||||
|
@ -28,33 +28,9 @@ if (initialState.meta.me) {
|
|||
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 {
|
||||
|
||||
static childContextTypes = {
|
||||
identity: PropTypes.shape({
|
||||
signedIn: PropTypes.bool.isRequired,
|
||||
accountId: PropTypes.string,
|
||||
disabledAccountId: PropTypes.string,
|
||||
accessToken: PropTypes.string,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
identity = createIdentityContext(initialState);
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
identity: this.identity,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.identity.signedIn) {
|
||||
this.disconnect = store.dispatch(connectUserStream());
|
||||
|
@ -74,19 +50,21 @@ export default class Mastodon extends PureComponent {
|
|||
|
||||
render () {
|
||||
return (
|
||||
<IntlProvider>
|
||||
<ReduxProvider store={store}>
|
||||
<ErrorBoundary>
|
||||
<Router>
|
||||
<ScrollContext shouldUpdateScroll={this.shouldUpdateScroll}>
|
||||
<Route path='/' component={UI} />
|
||||
</ScrollContext>
|
||||
</Router>
|
||||
<IdentityContext.Provider value={this.identity}>
|
||||
<IntlProvider>
|
||||
<ReduxProvider store={store}>
|
||||
<ErrorBoundary>
|
||||
<Router>
|
||||
<ScrollContext shouldUpdateScroll={this.shouldUpdateScroll}>
|
||||
<Route path='/' component={UI} />
|
||||
</ScrollContext>
|
||||
</Router>
|
||||
|
||||
<Helmet defaultTitle={title} titleTemplate={`%s - ${title}`} />
|
||||
</ErrorBoundary>
|
||||
</ReduxProvider>
|
||||
</IntlProvider>
|
||||
<Helmet defaultTitle={title} titleTemplate={`%s - ${title}`} />
|
||||
</ErrorBoundary>
|
||||
</ReduxProvider>
|
||||
</IntlProvider>
|
||||
</IdentityContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import { IconButton } from 'mastodon/components/icon_button';
|
|||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||
import { ShortNumber } from 'mastodon/components/short_number';
|
||||
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 { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
@ -111,6 +112,7 @@ const dateFormatOptions = {
|
|||
class Header extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
account: ImmutablePropTypes.record,
|
||||
identity_props: ImmutablePropTypes.list,
|
||||
onFollow: PropTypes.func.isRequired,
|
||||
|
@ -136,10 +138,6 @@ class Header extends ImmutablePureComponent {
|
|||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
};
|
||||
|
@ -255,7 +253,7 @@ class Header extends ImmutablePureComponent {
|
|||
|
||||
render () {
|
||||
const { account, hidden, intl } = this.props;
|
||||
const { signedIn, permissions } = this.context.identity;
|
||||
const { signedIn, permissions } = this.props.identity;
|
||||
|
||||
if (!account) {
|
||||
return null;
|
||||
|
@ -516,4 +514,4 @@ class Header extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(injectIntl(Header));
|
||||
export default withRouter(withIdentity(injectIntl(Header)));
|
||||
|
|
|
@ -9,6 +9,7 @@ import { connect } from 'react-redux';
|
|||
|
||||
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
||||
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { domain } from 'mastodon/initial_state';
|
||||
|
||||
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||
|
@ -38,16 +39,12 @@ const mapStateToProps = (state, { columnId }) => {
|
|||
};
|
||||
|
||||
class CommunityTimeline extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
onlyMedia: false,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
columnId: PropTypes.string,
|
||||
intl: PropTypes.object.isRequired,
|
||||
|
@ -77,7 +74,7 @@ class CommunityTimeline extends PureComponent {
|
|||
|
||||
componentDidMount () {
|
||||
const { dispatch, onlyMedia } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
dispatch(expandCommunityTimeline({ onlyMedia }));
|
||||
|
||||
|
@ -87,7 +84,7 @@ class CommunityTimeline extends PureComponent {
|
|||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (prevProps.onlyMedia !== this.props.onlyMedia) {
|
||||
const { dispatch, onlyMedia } = this.props;
|
||||
|
@ -161,4 +158,4 @@ class CommunityTimeline extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(CommunityTimeline));
|
||||
export default withIdentity(connect(mapStateToProps)(injectIntl(CommunityTimeline)));
|
||||
|
|
|
@ -12,6 +12,7 @@ import CancelIcon from '@/material-icons/400-24px/cancel-fill.svg?react';
|
|||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { domain, searchEnabled } from 'mastodon/initial_state';
|
||||
import { HASHTAG_REGEX } from 'mastodon/utils/hashtags';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
@ -33,12 +34,8 @@ const labelForRecentSearch = search => {
|
|||
};
|
||||
|
||||
class Search extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
value: PropTypes.string.isRequired,
|
||||
recent: ImmutablePropTypes.orderedSet,
|
||||
submitted: PropTypes.bool,
|
||||
|
@ -276,7 +273,7 @@ class Search extends PureComponent {
|
|||
}
|
||||
|
||||
_calculateOptions (value) {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
const trimmedValue = value.trim();
|
||||
const options = [];
|
||||
|
||||
|
@ -318,7 +315,7 @@ class Search extends PureComponent {
|
|||
render () {
|
||||
const { intl, value, submitted, recent } = this.props;
|
||||
const { expanded, options, selectedOption } = this.state;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
const hasValue = value.length > 0 || submitted;
|
||||
|
||||
|
@ -402,4 +399,4 @@ class Search extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(injectIntl(Search));
|
||||
export default withRouter(withIdentity(injectIntl(Search)));
|
||||
|
|
|
@ -13,6 +13,7 @@ import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
|||
import Column from 'mastodon/components/column';
|
||||
import ColumnHeader from 'mastodon/components/column_header';
|
||||
import Search from 'mastodon/features/compose/containers/search_container';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { trendsEnabled } from 'mastodon/initial_state';
|
||||
|
||||
import Links from './links';
|
||||
|
@ -32,12 +33,8 @@ const mapStateToProps = state => ({
|
|||
});
|
||||
|
||||
class Explore extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
intl: PropTypes.object.isRequired,
|
||||
multiColumn: PropTypes.bool,
|
||||
isSearching: PropTypes.bool,
|
||||
|
@ -53,7 +50,7 @@ class Explore extends PureComponent {
|
|||
|
||||
render() {
|
||||
const { intl, multiColumn, isSearching } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||
|
@ -114,4 +111,4 @@ class Explore extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(Explore));
|
||||
export default withIdentity(connect(mapStateToProps)(injectIntl(Explore)));
|
||||
|
|
|
@ -6,13 +6,14 @@ import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
|
|||
import { Helmet } from 'react-helmet';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
|
||||
import { useIdentity } from '@/mastodon/identity_context';
|
||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||
import { addColumn } from 'mastodon/actions/columns';
|
||||
import { changeSetting } from 'mastodon/actions/settings';
|
||||
import { connectPublicStream, connectCommunityStream } from 'mastodon/actions/streaming';
|
||||
import { expandPublicTimeline, expandCommunityTimeline } from 'mastodon/actions/timelines';
|
||||
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
||||
import initialState, { domain } from 'mastodon/initial_state';
|
||||
import { domain } from 'mastodon/initial_state';
|
||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||
|
||||
import Column from '../../components/column';
|
||||
|
@ -24,15 +25,6 @@ const messages = defineMessages({
|
|||
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 dispatch = useAppDispatch();
|
||||
const settings = useAppSelector((state) => state.getIn(['settings', 'firehose']));
|
||||
|
|
|
@ -24,6 +24,7 @@ import { fetchFollowRequests } from 'mastodon/actions/accounts';
|
|||
import Column from 'mastodon/components/column';
|
||||
import ColumnHeader from 'mastodon/components/column_header';
|
||||
import LinkFooter from 'mastodon/features/ui/components/link_footer';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
|
||||
import { me, showTrends } from '../../initial_state';
|
||||
import { NavigationBar } from '../compose/components/navigation_bar';
|
||||
|
@ -75,12 +76,8 @@ const badgeDisplay = (number, limit) => {
|
|||
};
|
||||
|
||||
class GettingStarted extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
intl: PropTypes.object.isRequired,
|
||||
myAccount: ImmutablePropTypes.record,
|
||||
multiColumn: PropTypes.bool,
|
||||
|
@ -91,7 +88,7 @@ class GettingStarted extends ImmutablePureComponent {
|
|||
|
||||
componentDidMount () {
|
||||
const { fetchFollowRequests } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (!signedIn) {
|
||||
return;
|
||||
|
@ -102,7 +99,7 @@ class GettingStarted extends ImmutablePureComponent {
|
|||
|
||||
render () {
|
||||
const { intl, myAccount, multiColumn, unreadFollowRequests } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
const navItems = [];
|
||||
|
||||
|
@ -167,4 +164,4 @@ class GettingStarted extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(GettingStarted));
|
||||
export default withIdentity(connect(mapStateToProps, mapDispatchToProps)(injectIntl(GettingStarted)));
|
||||
|
|
|
@ -17,6 +17,7 @@ import { fetchHashtag, followHashtag, unfollowHashtag } from 'mastodon/actions/t
|
|||
import { expandHashtagTimeline, clearTimeline } from 'mastodon/actions/timelines';
|
||||
import Column from 'mastodon/components/column';
|
||||
import ColumnHeader from 'mastodon/components/column_header';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
|
||||
import StatusListContainer from '../ui/containers/status_list_container';
|
||||
|
||||
|
@ -29,14 +30,10 @@ const mapStateToProps = (state, props) => ({
|
|||
});
|
||||
|
||||
class HashtagTimeline extends PureComponent {
|
||||
|
||||
disconnects = [];
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
params: PropTypes.object.isRequired,
|
||||
columnId: PropTypes.string,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
|
@ -94,7 +91,7 @@ class HashtagTimeline extends PureComponent {
|
|||
};
|
||||
|
||||
_subscribe (dispatch, id, tags = {}, local) {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (!signedIn) {
|
||||
return;
|
||||
|
@ -168,7 +165,7 @@ class HashtagTimeline extends PureComponent {
|
|||
handleFollow = () => {
|
||||
const { dispatch, params, tag } = this.props;
|
||||
const { id } = params;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (!signedIn) {
|
||||
return;
|
||||
|
@ -185,7 +182,7 @@ class HashtagTimeline extends PureComponent {
|
|||
const { hasUnread, columnId, multiColumn, tag } = this.props;
|
||||
const { id, local } = this.props.params;
|
||||
const pinned = !!columnId;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={`#${id}`}>
|
||||
|
@ -225,4 +222,4 @@ class HashtagTimeline extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(HashtagTimeline);
|
||||
export default connect(mapStateToProps)(withIdentity(HashtagTimeline));
|
||||
|
|
|
@ -14,6 +14,7 @@ import { fetchAnnouncements, toggleShowAnnouncements } from 'mastodon/actions/an
|
|||
import { IconWithBadge } from 'mastodon/components/icon_with_badge';
|
||||
import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator';
|
||||
import AnnouncementsContainer from 'mastodon/features/getting_started/containers/announcements_container';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { criticalUpdatesPending } from 'mastodon/initial_state';
|
||||
|
||||
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||
|
@ -40,12 +41,8 @@ const mapStateToProps = state => ({
|
|||
});
|
||||
|
||||
class HomeTimeline extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
hasUnread: PropTypes.bool,
|
||||
|
@ -126,7 +123,7 @@ class HomeTimeline extends PureComponent {
|
|||
render () {
|
||||
const { intl, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props;
|
||||
const pinned = !!columnId;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
const banners = [];
|
||||
|
||||
let announcementsButton;
|
||||
|
@ -190,4 +187,4 @@ class HomeTimeline extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(HomeTimeline));
|
||||
export default connect(mapStateToProps)(withIdentity(injectIntl(HomeTimeline)));
|
||||
|
|
|
@ -5,6 +5,7 @@ import { FormattedMessage } from 'react-intl';
|
|||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_REPORTS } from 'mastodon/permissions';
|
||||
|
||||
import { CheckboxWithLabel } from './checkbox_with_label';
|
||||
|
@ -12,13 +13,9 @@ import ClearColumnButton from './clear_column_button';
|
|||
import GrantPermissionButton from './grant_permission_button';
|
||||
import SettingToggle from './setting_toggle';
|
||||
|
||||
export default class ColumnSettings extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
class ColumnSettings extends PureComponent {
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
settings: ImmutablePropTypes.map.isRequired,
|
||||
pushSettings: ImmutablePropTypes.map.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
|
@ -215,7 +212,7 @@ export default class ColumnSettings extends PureComponent {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
{((this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) && (
|
||||
{((this.props.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) && (
|
||||
<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>
|
||||
|
||||
|
@ -228,7 +225,7 @@ export default class ColumnSettings extends PureComponent {
|
|||
</section>
|
||||
)}
|
||||
|
||||
{((this.context.identity.permissions & PERMISSION_MANAGE_REPORTS) === PERMISSION_MANAGE_REPORTS) && (
|
||||
{((this.props.identity.permissions & PERMISSION_MANAGE_REPORTS) === PERMISSION_MANAGE_REPORTS) && (
|
||||
<section role='group' aria-labelledby='notifications-admin-report'>
|
||||
<h3 id='notifications-status'><FormattedMessage id='notifications.column_settings.admin.report' defaultMessage='New reports:' /></h3>
|
||||
|
||||
|
@ -245,3 +242,5 @@ export default class ColumnSettings extends PureComponent {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
export default withIdentity(ColumnSettings);
|
||||
|
|
|
@ -17,6 +17,7 @@ import NotificationsIcon from '@/material-icons/400-24px/notifications-fill.svg?
|
|||
import { compareId } from 'mastodon/compare_id';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
|
||||
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||
import { submitMarkers } from '../../actions/markers';
|
||||
|
@ -77,12 +78,8 @@ const mapStateToProps = state => ({
|
|||
});
|
||||
|
||||
class Notifications extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
columnId: PropTypes.string,
|
||||
notifications: ImmutablePropTypes.list.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
|
@ -190,7 +187,7 @@ class Notifications extends PureComponent {
|
|||
const { intl, notifications, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, lastReadId, canMarkAsRead, needsNotificationPermission } = this.props;
|
||||
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 { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
let scrollableContent = null;
|
||||
|
||||
|
@ -299,4 +296,4 @@ class Notifications extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(Notifications));
|
||||
export default connect(mapStateToProps)(withIdentity(injectIntl(Notifications)));
|
||||
|
|
|
@ -18,6 +18,7 @@ import { replyCompose } from 'mastodon/actions/compose';
|
|||
import { reblog, favourite, unreblog, unfavourite } from 'mastodon/actions/interactions';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import { IconButton } from 'mastodon/components/icon_button';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { me, boostModal } from 'mastodon/initial_state';
|
||||
import { makeGetStatus } from 'mastodon/selectors';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
@ -47,12 +48,8 @@ const makeMapStateToProps = () => {
|
|||
};
|
||||
|
||||
class Footer extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
statusId: PropTypes.string.isRequired,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
|
@ -75,7 +72,7 @@ class Footer extends ImmutablePureComponent {
|
|||
|
||||
handleReplyClick = () => {
|
||||
const { dispatch, askReplyConfirmation, status, intl } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (askReplyConfirmation) {
|
||||
|
@ -104,7 +101,7 @@ class Footer extends ImmutablePureComponent {
|
|||
|
||||
handleFavouriteClick = () => {
|
||||
const { dispatch, status } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (status.get('favourited')) {
|
||||
|
@ -131,7 +128,7 @@ class Footer extends ImmutablePureComponent {
|
|||
|
||||
handleReblogClick = e => {
|
||||
const { dispatch, status } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (status.get('reblogged')) {
|
||||
|
@ -209,4 +206,4 @@ class Footer extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(makeMapStateToProps)(withRouter(injectIntl(Footer)));
|
||||
export default connect(makeMapStateToProps)(withIdentity(withRouter(injectIntl(Footer))));
|
||||
|
|
|
@ -9,6 +9,7 @@ import { connect } from 'react-redux';
|
|||
|
||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { domain } from 'mastodon/initial_state';
|
||||
|
||||
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||
|
@ -40,16 +41,12 @@ const mapStateToProps = (state, { columnId }) => {
|
|||
};
|
||||
|
||||
class PublicTimeline extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
onlyMedia: false,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
columnId: PropTypes.string,
|
||||
|
@ -80,7 +77,7 @@ class PublicTimeline extends PureComponent {
|
|||
|
||||
componentDidMount () {
|
||||
const { dispatch, onlyMedia, onlyRemote } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
dispatch(expandPublicTimeline({ onlyMedia, onlyRemote }));
|
||||
|
||||
|
@ -90,7 +87,7 @@ class PublicTimeline extends PureComponent {
|
|||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (prevProps.onlyMedia !== this.props.onlyMedia || prevProps.onlyRemote !== this.props.onlyRemote) {
|
||||
const { dispatch, onlyMedia, onlyRemote } = this.props;
|
||||
|
@ -164,4 +161,4 @@ class PublicTimeline extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(PublicTimeline));
|
||||
export default connect(mapStateToProps)(withIdentity(injectIntl(PublicTimeline)));
|
||||
|
|
|
@ -21,6 +21,7 @@ import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
|
|||
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
|
||||
import RepeatPrivateIcon from '@/svg-icons/repeat_private.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 { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
|
@ -67,12 +68,8 @@ const mapStateToProps = (state, { status }) => ({
|
|||
});
|
||||
|
||||
class ActionBar extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
relationship: ImmutablePropTypes.record,
|
||||
onReply: PropTypes.func.isRequired,
|
||||
|
@ -198,7 +195,7 @@ class ActionBar extends PureComponent {
|
|||
|
||||
render () {
|
||||
const { status, relationship, intl } = this.props;
|
||||
const { signedIn, permissions } = this.context.identity;
|
||||
const { signedIn, permissions } = this.props.identity;
|
||||
|
||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
||||
const pinnableStatus = ['public', 'unlisted', 'private'].includes(status.get('visibility'));
|
||||
|
@ -326,4 +323,4 @@ class ActionBar extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(connect(mapStateToProps)(injectIntl(ActionBar)));
|
||||
export default withRouter(connect(mapStateToProps)(withIdentity(injectIntl(ActionBar))));
|
||||
|
|
|
@ -20,6 +20,7 @@ import { Icon } from 'mastodon/components/icon';
|
|||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||
import ScrollContainer from 'mastodon/containers/scroll_container';
|
||||
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 {
|
||||
|
@ -189,12 +190,8 @@ const titleFromStatus = (intl, status) => {
|
|||
};
|
||||
|
||||
class Status extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
status: ImmutablePropTypes.map,
|
||||
|
@ -244,7 +241,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
handleFavouriteClick = (status) => {
|
||||
const { dispatch } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (status.get('favourited')) {
|
||||
|
@ -274,7 +271,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
handleReplyClick = (status) => {
|
||||
const { askReplyConfirmation, dispatch, intl } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (askReplyConfirmation) {
|
||||
|
@ -307,7 +304,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
handleReblogClick = (status, e) => {
|
||||
const { dispatch } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (status.get('reblogged')) {
|
||||
|
@ -745,4 +742,4 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(injectIntl(connect(makeMapStateToProps)(Status)));
|
||||
export default withRouter(injectIntl(connect(makeMapStateToProps)(withIdentity(Status))));
|
||||
|
|
|
@ -7,16 +7,13 @@ import { changeComposing, mountCompose, unmountCompose } from 'mastodon/actions/
|
|||
import ServerBanner from 'mastodon/components/server_banner';
|
||||
import ComposeFormContainer from 'mastodon/features/compose/containers/compose_form_container';
|
||||
import SearchContainer from 'mastodon/features/compose/containers/search_container';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
|
||||
import LinkFooter from './link_footer';
|
||||
|
||||
class ComposePanel extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
@ -41,7 +38,7 @@ class ComposePanel extends PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
return (
|
||||
<div className='compose-panel' onFocus={this.onFocus}>
|
||||
|
@ -65,4 +62,4 @@ class ComposePanel extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect()(ComposePanel);
|
||||
export default connect()(withIdentity(ComposePanel));
|
||||
|
|
|
@ -13,6 +13,7 @@ import { fetchServer } from 'mastodon/actions/server';
|
|||
import { Avatar } from 'mastodon/components/avatar';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { WordmarkLogo, SymbolLogo } from 'mastodon/components/logo';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { registrationsOpen, me, sso_redirect } from 'mastodon/initial_state';
|
||||
|
||||
const Account = connect(state => ({
|
||||
|
@ -41,12 +42,8 @@ const mapDispatchToProps = (dispatch) => ({
|
|||
});
|
||||
|
||||
class Header extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
openClosedRegistrationsModal: PropTypes.func,
|
||||
location: PropTypes.object,
|
||||
signupUrl: PropTypes.string.isRequired,
|
||||
|
@ -60,7 +57,7 @@ class Header extends PureComponent {
|
|||
}
|
||||
|
||||
render () {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
const { location, openClosedRegistrationsModal, signupUrl, intl } = this.props;
|
||||
|
||||
let content;
|
||||
|
@ -121,4 +118,4 @@ class Header extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(withRouter(connect(mapStateToProps, mapDispatchToProps)(Header)));
|
||||
export default injectIntl(withRouter(withIdentity(connect(mapStateToProps, mapDispatchToProps)(Header))));
|
||||
|
|
|
@ -8,6 +8,7 @@ import { Link } from 'react-router-dom';
|
|||
import { connect } from 'react-redux';
|
||||
|
||||
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 { PERMISSION_INVITE_USERS } from 'mastodon/permissions';
|
||||
import { logOut } from 'mastodon/utils/log_out';
|
||||
|
@ -32,12 +33,8 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
});
|
||||
|
||||
class LinkFooter extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
multiColumn: PropTypes.bool,
|
||||
onLogout: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
|
@ -53,7 +50,7 @@ class LinkFooter extends PureComponent {
|
|||
};
|
||||
|
||||
render () {
|
||||
const { signedIn, permissions } = this.context.identity;
|
||||
const { signedIn, permissions } = this.props.identity;
|
||||
const { multiColumn } = this.props;
|
||||
|
||||
const canInvite = signedIn && ((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS);
|
||||
|
@ -108,4 +105,4 @@ class LinkFooter extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(connect(null, mapDispatchToProps)(LinkFooter));
|
||||
export default injectIntl(withIdentity(connect(null, mapDispatchToProps)(LinkFooter)));
|
||||
|
|
|
@ -31,6 +31,7 @@ import { fetchFollowRequests } from 'mastodon/actions/accounts';
|
|||
import { IconWithBadge } from 'mastodon/components/icon_with_badge';
|
||||
import { WordmarkLogo } from 'mastodon/components/logo';
|
||||
import { NavigationPortal } from 'mastodon/components/navigation_portal';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { timelinePreview, trendsEnabled } from 'mastodon/initial_state';
|
||||
import { transientSingleColumn } from 'mastodon/is_mobile';
|
||||
|
||||
|
@ -97,12 +98,8 @@ const FollowRequestsLink = () => {
|
|||
};
|
||||
|
||||
class NavigationPanel extends Component {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
|
@ -112,7 +109,7 @@ class NavigationPanel extends Component {
|
|||
|
||||
render () {
|
||||
const { intl } = this.props;
|
||||
const { signedIn, disabledAccountId } = this.context.identity;
|
||||
const { signedIn, disabledAccountId } = this.props.identity;
|
||||
|
||||
let banner = undefined;
|
||||
|
||||
|
@ -189,4 +186,4 @@ class NavigationPanel extends Component {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(NavigationPanel);
|
||||
export default injectIntl(withIdentity(NavigationPanel));
|
||||
|
|
|
@ -15,6 +15,7 @@ import { focusApp, unfocusApp, changeLayout } from 'mastodon/actions/app';
|
|||
import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'mastodon/actions/markers';
|
||||
import { INTRODUCTION_VERSION } from 'mastodon/actions/onboarding';
|
||||
import { PictureInPicture } from 'mastodon/features/picture_in_picture';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { layoutFromWindow } from 'mastodon/is_mobile';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
|
@ -120,12 +121,8 @@ const keyMap = {
|
|||
};
|
||||
|
||||
class SwitchingColumnsArea extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
children: PropTypes.node,
|
||||
location: PropTypes.object,
|
||||
singleColumn: PropTypes.bool,
|
||||
|
@ -160,7 +157,7 @@ class SwitchingColumnsArea extends PureComponent {
|
|||
|
||||
render () {
|
||||
const { children, singleColumn } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
const pathName = this.props.location.pathname;
|
||||
|
||||
let redirect;
|
||||
|
@ -252,12 +249,8 @@ class SwitchingColumnsArea extends PureComponent {
|
|||
}
|
||||
|
||||
class UI extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
children: PropTypes.node,
|
||||
isComposing: PropTypes.bool,
|
||||
|
@ -309,7 +302,7 @@ class UI extends PureComponent {
|
|||
this.dragTargets.push(e.target);
|
||||
}
|
||||
|
||||
if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore && this.context.identity.signedIn) {
|
||||
if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore && this.props.identity.signedIn) {
|
||||
this.setState({ draggingOver: true });
|
||||
}
|
||||
};
|
||||
|
@ -337,7 +330,7 @@ class UI extends PureComponent {
|
|||
this.setState({ draggingOver: false });
|
||||
this.dragTargets = [];
|
||||
|
||||
if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore && this.context.identity.signedIn) {
|
||||
if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore && this.props.identity.signedIn) {
|
||||
this.props.dispatch(uploadCompose(e.dataTransfer.files));
|
||||
}
|
||||
};
|
||||
|
@ -389,7 +382,7 @@ class UI extends PureComponent {
|
|||
};
|
||||
|
||||
componentDidMount () {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
window.addEventListener('focus', this.handleWindowFocus, false);
|
||||
window.addEventListener('blur', this.handleWindowBlur, false);
|
||||
|
@ -586,7 +579,7 @@ class UI extends PureComponent {
|
|||
<div className={classNames('ui', { 'is-composing': isComposing })} ref={this.setRef}>
|
||||
<Header />
|
||||
|
||||
<SwitchingColumnsArea location={location} singleColumn={layout === 'mobile' || layout === 'single-column'}>
|
||||
<SwitchingColumnsArea identity={this.props.identity} location={location} singleColumn={layout === 'mobile' || layout === 'single-column'}>
|
||||
{children}
|
||||
</SwitchingColumnsArea>
|
||||
|
||||
|
@ -602,4 +595,4 @@ class UI extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(withRouter(UI)));
|
||||
export default connect(mapStateToProps)(injectIntl(withRouter(withIdentity(UI))));
|
||||
|
|
74
app/javascript/mastodon/identity_context.tsx
Normal file
74
app/javascript/mastodon/identity_context.tsx
Normal file
|
@ -0,0 +1,74 @@
|
|||
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);
|
||||
}
|
|
@ -44,12 +44,22 @@
|
|||
* @property {string} sso_redirect
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef Role
|
||||
* @property {string} id
|
||||
* @property {string} name
|
||||
* @property {string} permissions
|
||||
* @property {string} color
|
||||
* @property {boolean} highlighted
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef InitialState
|
||||
* @property {Record<string, import("./api_types/accounts").ApiAccountJSON>} accounts
|
||||
* @property {InitialStateLanguage[]} languages
|
||||
* @property {boolean=} critical_updates_pending
|
||||
* @property {InitialStateMeta} meta
|
||||
* @property {Role?} role
|
||||
*/
|
||||
|
||||
const element = document.getElementById('initial-state');
|
||||
|
|
|
@ -474,7 +474,7 @@
|
|||
"notification.follow_request": "Mae {name} wedi gwneud cais i'ch dilyn",
|
||||
"notification.mention": "Crybwyllodd {name} amdanoch chi",
|
||||
"notification.moderation-warning.learn_more": "Dysgu mwy",
|
||||
"notification.moderation_warning": "Rydych wedi derbyn rhybudd cymedroli",
|
||||
"notification.moderation_warning": "Rydych wedi derbyn rhybudd gan gymedrolwr",
|
||||
"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_mark_statuses_as_sensitive": "Mae rhai o'ch postiadau wedi'u marcio'n sensitif.",
|
||||
|
|
|
@ -308,6 +308,8 @@
|
|||
"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.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.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}.",
|
||||
|
@ -315,6 +317,8 @@
|
|||
"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.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.who_to_follow": "Who to follow",
|
||||
"followed_tags": "Followed hashtags",
|
||||
|
@ -469,6 +473,15 @@
|
|||
"notification.follow": "{name} followed you",
|
||||
"notification.follow_request": "{name} has requested to follow 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.poll": "A poll you have voted in has ended",
|
||||
"notification.reblog": "{name} boosted your status",
|
||||
|
|
|
@ -476,12 +476,12 @@
|
|||
"notification.moderation-warning.learn_more": "Saber más",
|
||||
"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_disable": "Se ha desactivado su cuenta.",
|
||||
"notification.moderation_warning.action_disable": "Tu cuenta ha sido desactivada.",
|
||||
"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_sensitive": "De ahora en adelante, todas tus publicaciones se marcarán como sensibles.",
|
||||
"notification.moderation_warning.action_silence": "Se ha limitado tu cuenta.",
|
||||
"notification.moderation_warning.action_suspend": "Se ha suspendido tu cuenta.",
|
||||
"notification.moderation_warning.action_silence": "Tu cuenta ha sido limitada.",
|
||||
"notification.moderation_warning.action_suspend": "Tu cuenta ha sido suspendida.",
|
||||
"notification.own_poll": "Tu encuesta ha terminado",
|
||||
"notification.poll": "Una encuesta en la que has votado ha terminado",
|
||||
"notification.reblog": "{name} ha retooteado tu estado",
|
||||
|
|
|
@ -476,12 +476,12 @@
|
|||
"notification.moderation-warning.learn_more": "Saber más",
|
||||
"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_disable": "Se ha desactivado su cuenta.",
|
||||
"notification.moderation_warning.action_disable": "Tu cuenta ha sido desactivada.",
|
||||
"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_sensitive": "De ahora en adelante, todas tus publicaciones se marcarán como sensibles.",
|
||||
"notification.moderation_warning.action_silence": "Se ha limitado tu cuenta.",
|
||||
"notification.moderation_warning.action_suspend": "Se ha suspendido tu cuenta.",
|
||||
"notification.moderation_warning.action_silence": "Tu cuenta ha sido limitada.",
|
||||
"notification.moderation_warning.action_suspend": "Tu cuenta ha sido suspendida.",
|
||||
"notification.own_poll": "Tu encuesta ha terminado",
|
||||
"notification.poll": "Una encuesta en la que has votado ha terminado",
|
||||
"notification.reblog": "{name} ha impulsado tu publicación",
|
||||
|
|
|
@ -474,11 +474,11 @@
|
|||
"notification.follow_request": "{name} biður um at fylgja tær",
|
||||
"notification.mention": "{name} nevndi teg",
|
||||
"notification.moderation-warning.learn_more": "Lær meira",
|
||||
"notification.moderation_warning": "Tú hevur móttikið eina umsjónarávarðing",
|
||||
"notification.moderation_warning": "Tú hevur móttikið eina umsjónarávaring",
|
||||
"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_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ávarðing.",
|
||||
"notification.moderation_warning.action_none": "Konta tín hevur móttikið eina umsjónarávaring.",
|
||||
"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_suspend": "Konta tín er ógildað.",
|
||||
|
|
|
@ -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.show_less": "Mostrar menos",
|
||||
"block_modal.show_more": "Mostrar máis",
|
||||
"block_modal.they_cant_mention": "Non te pode seguir nin mencionar.",
|
||||
"block_modal.they_cant_mention": "Non te poden 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_will_know": "Pode ver que a bloqueaches.",
|
||||
"block_modal.title": "Bloquear usuaria?",
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
"account.block_domain": "Blocar dominio {domain}",
|
||||
"account.block_short": "Blocar",
|
||||
"account.blocked": "Blocate",
|
||||
"account.browse_more_on_origin_server": "Navigar plus sur le profilo original",
|
||||
"account.browse_more_on_origin_server": "Percurrer plus sur le profilo original",
|
||||
"account.cancel_follow_request": "Cancellar sequimento",
|
||||
"account.copy": "Copiar ligamine a profilo",
|
||||
"account.direct": "Mentionar privatemente @{name}",
|
||||
|
@ -122,7 +122,7 @@
|
|||
"column.direct": "Mentiones private",
|
||||
"column.directory": "Navigar profilos",
|
||||
"column.domain_blocks": "Dominios blocate",
|
||||
"column.favourites": "Favoritos",
|
||||
"column.favourites": "Favorites",
|
||||
"column.firehose": "Fluxos in directo",
|
||||
"column.follow_requests": "Requestas de sequimento",
|
||||
"column.home": "Initio",
|
||||
|
@ -204,7 +204,7 @@
|
|||
"disabled_account_banner.account_settings": "Parametros de conto",
|
||||
"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.dismiss": "Dimitter",
|
||||
"dismissable_banner.dismiss": "Clauder",
|
||||
"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_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_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_cant_follow": "Nulle persona ab iste servitor pote sequer te.",
|
||||
"domain_block_modal.they_wont_know": "Illes non sapera que illes ha essite blocate.",
|
||||
"domain_block_modal.they_cant_follow": "Necuno de iste servitor pote sequer te.",
|
||||
"domain_block_modal.they_wont_know": "Ille non sapera que ille ha essite blocate.",
|
||||
"domain_block_modal.title": "Blocar dominio?",
|
||||
"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.",
|
||||
|
@ -307,7 +307,7 @@
|
|||
"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_suggestions.curated_suggestion": "Selection del equipa",
|
||||
"follow_suggestions.dismiss": "Non monstrar novemente",
|
||||
"follow_suggestions.dismiss": "Non monstrar de novo",
|
||||
"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.hints.featured": "Iste profilo ha essite seligite manualmente per le equipa de {domain}.",
|
||||
|
@ -412,7 +412,7 @@
|
|||
"lightbox.next": "Sequente",
|
||||
"lightbox.previous": "Precedente",
|
||||
"limited_account_hint.action": "Monstrar profilo in omne caso",
|
||||
"limited_account_hint.title": "Iste profilo esseva celate per le moderatores de {domain}.",
|
||||
"limited_account_hint.title": "Iste profilo ha essite celate per le moderatores de {domain}.",
|
||||
"link_preview.author": "Per {name}",
|
||||
"lists.account.add": "Adder al lista",
|
||||
"lists.account.remove": "Remover del lista",
|
||||
|
@ -432,12 +432,12 @@
|
|||
"loading_indicator.label": "Cargante…",
|
||||
"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}.",
|
||||
"mute_modal.hide_from_notifications": "Celar ab notificationes",
|
||||
"mute_modal.hide_from_notifications": "Celar in notificationes",
|
||||
"mute_modal.hide_options": "Celar optiones",
|
||||
"mute_modal.indefinite": "Usque io dissilentia iste persona",
|
||||
"mute_modal.show_options": "Monstrar optiones",
|
||||
"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": "Illes non sapera que illes ha essite silentiate.",
|
||||
"mute_modal.they_can_mention_and_follow": "Ille pote mentionar te e sequer te, ma tu non potera vider le.",
|
||||
"mute_modal.they_wont_know": "Ille non sapera que ille ha essite silentiate.",
|
||||
"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_posts": "Iste persona pote totevia vider tu messages, ma tu non videra le sues.",
|
||||
|
@ -451,13 +451,13 @@
|
|||
"navigation_bar.discover": "Discoperir",
|
||||
"navigation_bar.domain_blocks": "Dominios blocate",
|
||||
"navigation_bar.explore": "Explorar",
|
||||
"navigation_bar.favourites": "Favoritos",
|
||||
"navigation_bar.favourites": "Favorites",
|
||||
"navigation_bar.filters": "Parolas silentiate",
|
||||
"navigation_bar.follow_requests": "Requestas de sequimento",
|
||||
"navigation_bar.followed_tags": "Hashtags sequite",
|
||||
"navigation_bar.follows_and_followers": "Sequites e sequitores",
|
||||
"navigation_bar.lists": "Listas",
|
||||
"navigation_bar.logout": "Clauder le session",
|
||||
"navigation_bar.logout": "Clauder session",
|
||||
"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.personal": "Personal",
|
||||
|
@ -474,7 +474,7 @@
|
|||
"notification.follow_request": "{name} ha requestate de sequer te",
|
||||
"notification.mention": "{name} te ha mentionate",
|
||||
"notification.moderation-warning.learn_more": "Apprender plus",
|
||||
"notification.moderation_warning": "Tu ha recipite un advertimento de moderation",
|
||||
"notification.moderation_warning": "Tu ha recepite un aviso de moderation",
|
||||
"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_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.sign_up": "Nove inscriptiones:",
|
||||
"notifications.column_settings.alert": "Notificationes de scriptorio",
|
||||
"notifications.column_settings.favourite": "Favoritos:",
|
||||
"notifications.column_settings.favourite": "Favorites:",
|
||||
"notifications.column_settings.filter_bar.advanced": "Monstrar tote le categorias",
|
||||
"notifications.column_settings.filter_bar.category": "Barra de filtro rapide",
|
||||
"notifications.column_settings.follow": "Nove sequitores:",
|
||||
|
@ -518,7 +518,7 @@
|
|||
"notifications.column_settings.update": "Modificationes:",
|
||||
"notifications.filter.all": "Toto",
|
||||
"notifications.filter.boosts": "Impulsos",
|
||||
"notifications.filter.favourites": "Favoritos",
|
||||
"notifications.filter.favourites": "Favorites",
|
||||
"notifications.filter.follows": "Sequites",
|
||||
"notifications.filter.mentions": "Mentiones",
|
||||
"notifications.filter.polls": "Resultatos del sondage",
|
||||
|
@ -717,7 +717,7 @@
|
|||
"status.edited": "Ultime modification le {date}",
|
||||
"status.edited_x_times": "Modificate {count, plural, one {{count} vice} other {{count} vices}}",
|
||||
"status.embed": "Incastrar",
|
||||
"status.favourite": "Adder al favoritos",
|
||||
"status.favourite": "Adder al favorites",
|
||||
"status.favourites": "{count, plural, one {favorite} other {favorites}}",
|
||||
"status.filter": "Filtrar iste message",
|
||||
"status.filtered": "Filtrate",
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
"announcement.announcement": "Proclamation",
|
||||
"attachments_list.unprocessed": "(íntractat)",
|
||||
"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_more": "Monstrar plu",
|
||||
"block_modal.they_cant_mention": "Ne posse mentionar ni sequer te.",
|
||||
|
@ -224,7 +225,10 @@
|
|||
"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.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_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.",
|
||||
"embed.instructions": "Inbedar ti-ci posta per copiar li code in infra.",
|
||||
"embed.preview": "Vi qualmen it va aspecter:",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue