mirror of
https://github.com/glitch-soc/mastodon.git
synced 2026-03-29 03:00:33 +02:00
Merge pull request #3460 from glitch-soc/glitch-soc/merge-upstream
Merge upstream changes up to 9d5e10a70e
This commit is contained in:
2
.github/actions/setup-ruby/action.yml
vendored
2
.github/actions/setup-ruby/action.yml
vendored
@@ -14,7 +14,7 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libicu-dev libidn11-dev libvips42 ${{ inputs.additional-system-dependencies }}
|
sudo apt-get install --no-install-recommends -y libicu-dev libidn11-dev libvips42 ${{ inputs.additional-system-dependencies }}
|
||||||
|
|
||||||
- name: Set up Ruby
|
- name: Set up Ruby
|
||||||
uses: ruby/setup-ruby@4eb9f110bac952a8b68ecf92e3b5c7a987594ba6 # v1
|
uses: ruby/setup-ruby@4eb9f110bac952a8b68ecf92e3b5c7a987594ba6 # v1
|
||||||
|
|||||||
10
.github/workflows/test-ruby.yml
vendored
10
.github/workflows/test-ruby.yml
vendored
@@ -124,7 +124,6 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ruby-version:
|
ruby-version:
|
||||||
- '3.2'
|
|
||||||
- '3.3'
|
- '3.3'
|
||||||
- '.ruby-version'
|
- '.ruby-version'
|
||||||
steps:
|
steps:
|
||||||
@@ -217,7 +216,6 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ruby-version:
|
ruby-version:
|
||||||
- '3.2'
|
|
||||||
- '3.3'
|
- '3.3'
|
||||||
- '.ruby-version'
|
- '.ruby-version'
|
||||||
|
|
||||||
@@ -348,7 +346,6 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ruby-version:
|
ruby-version:
|
||||||
- '3.2'
|
|
||||||
- '3.3'
|
- '3.3'
|
||||||
- '.ruby-version'
|
- '.ruby-version'
|
||||||
search-image:
|
search-image:
|
||||||
@@ -387,10 +384,3 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: test-search-logs-${{ matrix.ruby-version }}
|
name: test-search-logs-${{ matrix.ruby-version }}
|
||||||
path: log/
|
path: log/
|
||||||
|
|
||||||
- name: Archive test screenshots
|
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
|
||||||
if: failure()
|
|
||||||
with:
|
|
||||||
name: test-search-screenshots
|
|
||||||
path: tmp/capybara/
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ AllCops:
|
|||||||
- lib/mastodon/migration_helpers.rb
|
- lib/mastodon/migration_helpers.rb
|
||||||
ExtraDetails: true
|
ExtraDetails: true
|
||||||
NewCops: enable
|
NewCops: enable
|
||||||
TargetRubyVersion: 3.2 # Oldest supported ruby version
|
TargetRubyVersion: 3.3 # Oldest supported ruby version
|
||||||
|
|
||||||
inherit_from:
|
inherit_from:
|
||||||
- .rubocop/layout.yml
|
- .rubocop/layout.yml
|
||||||
|
|||||||
@@ -137,11 +137,7 @@ const preview: Preview = {
|
|||||||
}, [currentLocale, currentLocaleData]);
|
}, [currentLocale, currentLocaleData]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IntlProvider
|
<IntlProvider locale={currentLocale} messages={currentLocaleData}>
|
||||||
locale={currentLocale}
|
|
||||||
messages={currentLocaleData}
|
|
||||||
textComponent='span'
|
|
||||||
>
|
|
||||||
<Story />
|
<Story />
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
2
Gemfile
2
Gemfile
@@ -1,7 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
ruby '>= 3.2.0', '< 3.5.0'
|
ruby '>= 3.3.0', '< 3.5.0'
|
||||||
|
|
||||||
gem 'propshaft'
|
gem 'propshaft'
|
||||||
gem 'puma', '~> 7.0'
|
gem 'puma', '~> 7.0'
|
||||||
|
|||||||
108
Gemfile.lock
108
Gemfile.lock
@@ -12,29 +12,29 @@ GEM
|
|||||||
specs:
|
specs:
|
||||||
action_text-trix (2.1.17)
|
action_text-trix (2.1.17)
|
||||||
railties
|
railties
|
||||||
actioncable (8.1.2.1)
|
actioncable (8.1.3)
|
||||||
actionpack (= 8.1.2.1)
|
actionpack (= 8.1.3)
|
||||||
activesupport (= 8.1.2.1)
|
activesupport (= 8.1.3)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
zeitwerk (~> 2.6)
|
zeitwerk (~> 2.6)
|
||||||
actionmailbox (8.1.2.1)
|
actionmailbox (8.1.3)
|
||||||
actionpack (= 8.1.2.1)
|
actionpack (= 8.1.3)
|
||||||
activejob (= 8.1.2.1)
|
activejob (= 8.1.3)
|
||||||
activerecord (= 8.1.2.1)
|
activerecord (= 8.1.3)
|
||||||
activestorage (= 8.1.2.1)
|
activestorage (= 8.1.3)
|
||||||
activesupport (= 8.1.2.1)
|
activesupport (= 8.1.3)
|
||||||
mail (>= 2.8.0)
|
mail (>= 2.8.0)
|
||||||
actionmailer (8.1.2.1)
|
actionmailer (8.1.3)
|
||||||
actionpack (= 8.1.2.1)
|
actionpack (= 8.1.3)
|
||||||
actionview (= 8.1.2.1)
|
actionview (= 8.1.3)
|
||||||
activejob (= 8.1.2.1)
|
activejob (= 8.1.3)
|
||||||
activesupport (= 8.1.2.1)
|
activesupport (= 8.1.3)
|
||||||
mail (>= 2.8.0)
|
mail (>= 2.8.0)
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
actionpack (8.1.2.1)
|
actionpack (8.1.3)
|
||||||
actionview (= 8.1.2.1)
|
actionview (= 8.1.3)
|
||||||
activesupport (= 8.1.2.1)
|
activesupport (= 8.1.3)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
rack (>= 2.2.4)
|
rack (>= 2.2.4)
|
||||||
rack-session (>= 1.0.1)
|
rack-session (>= 1.0.1)
|
||||||
@@ -42,16 +42,16 @@ GEM
|
|||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
rails-html-sanitizer (~> 1.6)
|
rails-html-sanitizer (~> 1.6)
|
||||||
useragent (~> 0.16)
|
useragent (~> 0.16)
|
||||||
actiontext (8.1.2.1)
|
actiontext (8.1.3)
|
||||||
action_text-trix (~> 2.1.15)
|
action_text-trix (~> 2.1.15)
|
||||||
actionpack (= 8.1.2.1)
|
actionpack (= 8.1.3)
|
||||||
activerecord (= 8.1.2.1)
|
activerecord (= 8.1.3)
|
||||||
activestorage (= 8.1.2.1)
|
activestorage (= 8.1.3)
|
||||||
activesupport (= 8.1.2.1)
|
activesupport (= 8.1.3)
|
||||||
globalid (>= 0.6.0)
|
globalid (>= 0.6.0)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (8.1.2.1)
|
actionview (8.1.3)
|
||||||
activesupport (= 8.1.2.1)
|
activesupport (= 8.1.3)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.11)
|
erubi (~> 1.11)
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
@@ -61,22 +61,22 @@ GEM
|
|||||||
activemodel (>= 4.1)
|
activemodel (>= 4.1)
|
||||||
case_transform (>= 0.2)
|
case_transform (>= 0.2)
|
||||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||||
activejob (8.1.2.1)
|
activejob (8.1.3)
|
||||||
activesupport (= 8.1.2.1)
|
activesupport (= 8.1.3)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (8.1.2.1)
|
activemodel (8.1.3)
|
||||||
activesupport (= 8.1.2.1)
|
activesupport (= 8.1.3)
|
||||||
activerecord (8.1.2.1)
|
activerecord (8.1.3)
|
||||||
activemodel (= 8.1.2.1)
|
activemodel (= 8.1.3)
|
||||||
activesupport (= 8.1.2.1)
|
activesupport (= 8.1.3)
|
||||||
timeout (>= 0.4.0)
|
timeout (>= 0.4.0)
|
||||||
activestorage (8.1.2.1)
|
activestorage (8.1.3)
|
||||||
actionpack (= 8.1.2.1)
|
actionpack (= 8.1.3)
|
||||||
activejob (= 8.1.2.1)
|
activejob (= 8.1.3)
|
||||||
activerecord (= 8.1.2.1)
|
activerecord (= 8.1.3)
|
||||||
activesupport (= 8.1.2.1)
|
activesupport (= 8.1.3)
|
||||||
marcel (~> 1.0)
|
marcel (~> 1.0)
|
||||||
activesupport (8.1.2.1)
|
activesupport (8.1.3)
|
||||||
base64
|
base64
|
||||||
bigdecimal
|
bigdecimal
|
||||||
concurrent-ruby (~> 1.0, >= 1.3.1)
|
concurrent-ruby (~> 1.0, >= 1.3.1)
|
||||||
@@ -354,7 +354,7 @@ GEM
|
|||||||
azure-blob (~> 0.5.2)
|
azure-blob (~> 0.5.2)
|
||||||
hashie (~> 5.0)
|
hashie (~> 5.0)
|
||||||
jmespath (1.6.2)
|
jmespath (1.6.2)
|
||||||
json (2.19.2)
|
json (2.19.3)
|
||||||
json-canonicalization (1.0.0)
|
json-canonicalization (1.0.0)
|
||||||
json-jwt (1.17.0)
|
json-jwt (1.17.0)
|
||||||
activesupport (>= 4.2)
|
activesupport (>= 4.2)
|
||||||
@@ -657,20 +657,20 @@ GEM
|
|||||||
rack (>= 1.3)
|
rack (>= 1.3)
|
||||||
rackup (2.3.1)
|
rackup (2.3.1)
|
||||||
rack (>= 3)
|
rack (>= 3)
|
||||||
rails (8.1.2.1)
|
rails (8.1.3)
|
||||||
actioncable (= 8.1.2.1)
|
actioncable (= 8.1.3)
|
||||||
actionmailbox (= 8.1.2.1)
|
actionmailbox (= 8.1.3)
|
||||||
actionmailer (= 8.1.2.1)
|
actionmailer (= 8.1.3)
|
||||||
actionpack (= 8.1.2.1)
|
actionpack (= 8.1.3)
|
||||||
actiontext (= 8.1.2.1)
|
actiontext (= 8.1.3)
|
||||||
actionview (= 8.1.2.1)
|
actionview (= 8.1.3)
|
||||||
activejob (= 8.1.2.1)
|
activejob (= 8.1.3)
|
||||||
activemodel (= 8.1.2.1)
|
activemodel (= 8.1.3)
|
||||||
activerecord (= 8.1.2.1)
|
activerecord (= 8.1.3)
|
||||||
activestorage (= 8.1.2.1)
|
activestorage (= 8.1.3)
|
||||||
activesupport (= 8.1.2.1)
|
activesupport (= 8.1.3)
|
||||||
bundler (>= 1.15.0)
|
bundler (>= 1.15.0)
|
||||||
railties (= 8.1.2.1)
|
railties (= 8.1.3)
|
||||||
rails-dom-testing (2.3.0)
|
rails-dom-testing (2.3.0)
|
||||||
activesupport (>= 5.0.0)
|
activesupport (>= 5.0.0)
|
||||||
minitest
|
minitest
|
||||||
@@ -681,9 +681,9 @@ GEM
|
|||||||
rails-i18n (8.1.0)
|
rails-i18n (8.1.0)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
railties (>= 8.0.0, < 9)
|
railties (>= 8.0.0, < 9)
|
||||||
railties (8.1.2.1)
|
railties (8.1.3)
|
||||||
actionpack (= 8.1.2.1)
|
actionpack (= 8.1.3)
|
||||||
activesupport (= 8.1.2.1)
|
activesupport (= 8.1.3)
|
||||||
irb (~> 1.13)
|
irb (~> 1.13)
|
||||||
rackup (>= 1.0.0)
|
rackup (>= 1.0.0)
|
||||||
rake (>= 12.2)
|
rake (>= 12.2)
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ Mastodon is a **free, open-source social network server** based on [ActivityPub]
|
|||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
- **Ruby** 3.2+
|
- **Ruby** 3.3+
|
||||||
- **PostgreSQL** 14+
|
- **PostgreSQL** 14+
|
||||||
- **Redis** 7.0+
|
- **Redis** 7.0+
|
||||||
- **Node.js** 20+
|
- **Node.js** 20+
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::Accounts::EmailSubscriptionsController < Api::BaseController
|
||||||
|
before_action :set_account
|
||||||
|
before_action :require_feature_enabled!
|
||||||
|
before_action :require_account_permissions!
|
||||||
|
|
||||||
|
def create
|
||||||
|
@account.email_subscriptions.create!(email: params[:email], locale: I18n.locale)
|
||||||
|
render_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.local.find(params[:account_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def require_feature_enabled!
|
||||||
|
head 404 unless Mastodon::Feature.email_subscriptions_enabled?
|
||||||
|
end
|
||||||
|
|
||||||
|
def require_account_permissions!
|
||||||
|
head 404 if @account.unavailable? || !@account.user_can?(:manage_email_subscriptions) || !@account.user_email_subscriptions_enabled?
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::Instances::TermsOfServiceController < Api::V1::Instances::BaseController
|
||||||
|
before_action :cache_even_if_authenticated!
|
||||||
|
|
||||||
|
def index
|
||||||
|
@terms_of_service = TermsOfService.current || raise(ActiveRecord::RecordNotFound)
|
||||||
|
render json: @terms_of_service, serializer: REST::TermsOfServiceSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
@terms_of_service = TermsOfService.published.find_by!(effective_date: params[:date])
|
||||||
|
render json: @terms_of_service, serializer: REST::TermsOfServiceSerializer
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::V1::Instances::TermsOfServicesController < Api::V1::Instances::BaseController
|
|
||||||
before_action :set_terms_of_service
|
|
||||||
|
|
||||||
def show
|
|
||||||
cache_even_if_authenticated!
|
|
||||||
render json: @terms_of_service, serializer: REST::TermsOfServiceSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_terms_of_service
|
|
||||||
@terms_of_service = begin
|
|
||||||
if params[:date].present?
|
|
||||||
TermsOfService.published.find_by!(effective_date: params[:date])
|
|
||||||
else
|
|
||||||
TermsOfService.current
|
|
||||||
end
|
|
||||||
end
|
|
||||||
not_found if @terms_of_service.nil?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -53,19 +53,21 @@ module SignatureVerification
|
|||||||
|
|
||||||
raise Mastodon::SignatureVerificationError, 'Request not signed' unless signed_request?
|
raise Mastodon::SignatureVerificationError, 'Request not signed' unless signed_request?
|
||||||
|
|
||||||
actor = actor_from_key_id
|
keypair = keypair_from_key_id
|
||||||
|
|
||||||
raise Mastodon::SignatureVerificationError, "Public key not found for key #{signature_key_id}" if actor.nil?
|
raise Mastodon::SignatureVerificationError, "Public key not found for key #{signature_key_id}" if keypair.nil?
|
||||||
|
|
||||||
return (@signed_request_actor = actor) if signed_request.verified?(actor)
|
check_keypair_validity!(keypair)
|
||||||
|
return (@signed_request_actor = keypair.actor) if signed_request.verified?(keypair)
|
||||||
|
|
||||||
actor = stoplight_wrapper.run { actor_refresh_key!(actor) }
|
keypair = stoplight_wrapper.run { keypair_refresh_key!(keypair) }
|
||||||
|
|
||||||
raise Mastodon::SignatureVerificationError, "Could not refresh public key #{signature_key_id}" if actor.nil?
|
raise Mastodon::SignatureVerificationError, "Could not refresh public key #{signature_key_id}" if keypair.nil?
|
||||||
|
|
||||||
return (@signed_request_actor = actor) if signed_request.verified?(actor)
|
check_keypair_validity!(keypair)
|
||||||
|
return (@signed_request_actor = keypair.actor) if signed_request.verified?(keypair)
|
||||||
|
|
||||||
fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri}"
|
fail_with! "Verification failed for #{keypair.actor.to_log_human_identifier} #{keypair.actor.uri} #{keypair.uri}"
|
||||||
rescue Mastodon::MalformedHeaderError => e
|
rescue Mastodon::MalformedHeaderError => e
|
||||||
@signature_verification_failure_code = 400
|
@signature_verification_failure_code = 400
|
||||||
fail_with! e.message
|
fail_with! e.message
|
||||||
@@ -89,7 +91,7 @@ module SignatureVerification
|
|||||||
@signed_request_actor = nil
|
@signed_request_actor = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def actor_from_key_id
|
def keypair_from_key_id
|
||||||
key_id = signed_request.key_id
|
key_id = signed_request.key_id
|
||||||
domain = key_id.start_with?('acct:') ? key_id.split('@').last : key_id
|
domain = key_id.start_with?('acct:') ? key_id.split('@').last : key_id
|
||||||
|
|
||||||
@@ -101,9 +103,10 @@ module SignatureVerification
|
|||||||
if key_id.start_with?('acct:')
|
if key_id.start_with?('acct:')
|
||||||
stoplight_wrapper.run { ResolveAccountService.new.call(key_id.delete_prefix('acct:'), suppress_errors: false) }
|
stoplight_wrapper.run { ResolveAccountService.new.call(key_id.delete_prefix('acct:'), suppress_errors: false) }
|
||||||
elsif !ActivityPub::TagManager.instance.local_uri?(key_id)
|
elsif !ActivityPub::TagManager.instance.local_uri?(key_id)
|
||||||
account = ActivityPub::TagManager.instance.uri_to_actor(key_id)
|
keypair = Keypair.from_keyid(key_id)
|
||||||
account ||= stoplight_wrapper.run { ActivityPub::FetchRemoteKeyService.new.call(key_id, suppress_errors: false) }
|
return keypair if keypair.present?
|
||||||
account
|
|
||||||
|
stoplight_wrapper.run { ActivityPub::FetchRemoteKeyService.new.call(key_id, suppress_errors: false) }
|
||||||
end
|
end
|
||||||
rescue Mastodon::PrivateNetworkAddressError => e
|
rescue Mastodon::PrivateNetworkAddressError => e
|
||||||
raise Mastodon::SignatureVerificationError, "Requests to private network addresses are disallowed (tried to query #{e.host})"
|
raise Mastodon::SignatureVerificationError, "Requests to private network addresses are disallowed (tried to query #{e.host})"
|
||||||
@@ -120,14 +123,20 @@ module SignatureVerification
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def actor_refresh_key!(actor)
|
def keypair_refresh_key!(keypair)
|
||||||
return if actor.local? || !actor.activitypub?
|
# TODO: this currently only is concerned with refreshing the actor and returning the legacy key, this needs to be reworked
|
||||||
return actor.refresh! if actor.respond_to?(:refresh!) && actor.possibly_stale?
|
return if keypair.actor.local? || !keypair.actor.activitypub?
|
||||||
|
return keypair.actor.refresh! if keypair.actor.respond_to?(:refresh!) && keypair.actor.possibly_stale?
|
||||||
|
|
||||||
ActivityPub::FetchRemoteActorService.new.call(actor.uri, only_key: true, suppress_errors: false)
|
Keypair.from_legacy_account(ActivityPub::FetchRemoteActorService.new.call(keypair.actor.uri, only_key: true, suppress_errors: false))
|
||||||
rescue Mastodon::PrivateNetworkAddressError => e
|
rescue Mastodon::PrivateNetworkAddressError => e
|
||||||
raise Mastodon::SignatureVerificationError, "Requests to private network addresses are disallowed (tried to query #{e.host})"
|
raise Mastodon::SignatureVerificationError, "Requests to private network addresses are disallowed (tried to query #{e.host})"
|
||||||
rescue Mastodon::HostValidationError, ActivityPub::FetchRemoteActorService::Error, Webfinger::Error => e
|
rescue Mastodon::HostValidationError, ActivityPub::FetchRemoteActorService::Error, Webfinger::Error => e
|
||||||
raise Mastodon::SignatureVerificationError, e.message
|
raise Mastodon::SignatureVerificationError, e.message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def check_keypair_validity!(keypair)
|
||||||
|
raise Mastodon::SignatureVerification, "Key #{signature_key_id} is revoked" if keypair.revoked?
|
||||||
|
raise Mastodon::SignatureVerification, "Key #{signature_key_id} has expired" if keypair.expired?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class EmailSubscriptions::ConfirmationsController < ApplicationController
|
||||||
|
layout 'auth'
|
||||||
|
|
||||||
|
before_action :set_email_subscription
|
||||||
|
|
||||||
|
def show
|
||||||
|
@email_subscription.confirm! unless @email_subscription.confirmed?
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_email_subscription
|
||||||
|
@email_subscription = EmailSubscription.find_by!(confirmation_token: params[:confirmation_token])
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class MailSubscriptionsController < ApplicationController
|
|
||||||
layout 'auth'
|
|
||||||
|
|
||||||
skip_before_action :require_functional!
|
|
||||||
|
|
||||||
before_action :set_user
|
|
||||||
before_action :set_type
|
|
||||||
|
|
||||||
protect_from_forgery with: :null_session
|
|
||||||
|
|
||||||
def show; end
|
|
||||||
|
|
||||||
def create
|
|
||||||
@user.settings[email_type_from_param] = false
|
|
||||||
@user.save!
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_user
|
|
||||||
@user = GlobalID::Locator.locate_signed(params[:token], for: 'unsubscribe')
|
|
||||||
not_found unless @user
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_type
|
|
||||||
@type = email_type_from_param
|
|
||||||
end
|
|
||||||
|
|
||||||
def email_type_from_param
|
|
||||||
case params[:type]
|
|
||||||
when 'follow', 'reblog', 'favourite', 'mention', 'follow_request'
|
|
||||||
"notification_emails.#{params[:type]}"
|
|
||||||
else
|
|
||||||
not_found
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
class Settings::PrivacyController < Settings::BaseController
|
class Settings::PrivacyController < Settings::BaseController
|
||||||
before_action :set_account
|
before_action :set_account
|
||||||
|
before_action :set_email_subscriptions_count
|
||||||
|
|
||||||
def show; end
|
def show; end
|
||||||
|
|
||||||
@@ -24,4 +25,8 @@ class Settings::PrivacyController < Settings::BaseController
|
|||||||
def set_account
|
def set_account
|
||||||
@account = current_account
|
@account = current_account
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_email_subscriptions_count
|
||||||
|
@email_subscriptions_count = with_read_replica { @account.email_subscriptions.confirmed.count }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
58
app/controllers/unsubscriptions_controller.rb
Normal file
58
app/controllers/unsubscriptions_controller.rb
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class UnsubscriptionsController < ApplicationController
|
||||||
|
layout 'auth'
|
||||||
|
|
||||||
|
skip_before_action :require_functional!
|
||||||
|
|
||||||
|
before_action :set_recipient
|
||||||
|
before_action :set_type
|
||||||
|
before_action :set_scope
|
||||||
|
before_action :require_type_if_user!
|
||||||
|
|
||||||
|
protect_from_forgery with: :null_session
|
||||||
|
|
||||||
|
def show; end
|
||||||
|
|
||||||
|
def create
|
||||||
|
case @scope
|
||||||
|
when :user
|
||||||
|
@recipient.settings[@type] = false
|
||||||
|
@recipient.save!
|
||||||
|
when :email_subscription
|
||||||
|
@recipient.destroy!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_recipient
|
||||||
|
@recipient = GlobalID::Locator.locate_signed(params[:token], for: 'unsubscribe')
|
||||||
|
not_found unless @recipient
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_scope
|
||||||
|
if @recipient.is_a?(User)
|
||||||
|
@scope = :user
|
||||||
|
elsif @recipient.is_a?(EmailSubscription)
|
||||||
|
@scope = :email_subscription
|
||||||
|
else
|
||||||
|
not_found
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_type
|
||||||
|
@type = email_type_from_param
|
||||||
|
end
|
||||||
|
|
||||||
|
def require_type_if_user!
|
||||||
|
not_found if @recipient.is_a?(User) && @type.blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
def email_type_from_param
|
||||||
|
case params[:type]
|
||||||
|
when 'follow', 'reblog', 'favourite', 'mention', 'follow_request'
|
||||||
|
"notification_emails.#{params[:type]}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -18,23 +18,7 @@ module WellKnown
|
|||||||
private
|
private
|
||||||
|
|
||||||
def set_account
|
def set_account
|
||||||
username = username_from_resource
|
@account = WebfingerResource.new(resource_param).account
|
||||||
|
|
||||||
@account = begin
|
|
||||||
if username == Rails.configuration.x.local_domain || username == Rails.configuration.x.web_domain
|
|
||||||
Account.representative
|
|
||||||
else
|
|
||||||
Account.find_local!(username)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def username_from_resource
|
|
||||||
resource_user = resource_param
|
|
||||||
username, domain = resource_user.split('@')
|
|
||||||
resource_user = "#{username}@#{Rails.configuration.x.local_domain}" if Rails.configuration.x.alternate_domains.include?(domain)
|
|
||||||
|
|
||||||
WebfingerResource.new(resource_user).username
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def resource_param
|
def resource_param
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ module ContextHelper
|
|||||||
NAMED_CONTEXT_MAP = {
|
NAMED_CONTEXT_MAP = {
|
||||||
activitystreams: 'https://www.w3.org/ns/activitystreams',
|
activitystreams: 'https://www.w3.org/ns/activitystreams',
|
||||||
security: 'https://w3id.org/security/v1',
|
security: 'https://w3id.org/security/v1',
|
||||||
|
webfinger: 'https://purl.archive.org/socialweb/webfinger',
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
CONTEXT_EXTENSION_MAP = {
|
CONTEXT_EXTENSION_MAP = {
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { injectIntl, defineMessages } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import api from 'flavours/glitch/api';
|
import api from 'flavours/glitch/api';
|
||||||
|
|
||||||
|
import { injectIntl } from '../intl';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
legal: { id: 'report.categories.legal', defaultMessage: 'Legal' },
|
legal: { id: 'report.categories.legal', defaultMessage: 'Legal' },
|
||||||
other: { id: 'report.categories.other', defaultMessage: 'Other' },
|
other: { id: 'report.categories.other', defaultMessage: 'Other' },
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useState, useCallback, useRef, useId } from 'react';
|
import { useState, useCallback, useRef, useId } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
OffsetValue,
|
OffsetValue,
|
||||||
@@ -8,24 +8,37 @@ import type {
|
|||||||
} from 'react-overlays/esm/usePopper';
|
} from 'react-overlays/esm/usePopper';
|
||||||
import Overlay from 'react-overlays/Overlay';
|
import Overlay from 'react-overlays/Overlay';
|
||||||
|
|
||||||
|
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||||
import { useSelectableClick } from 'flavours/glitch/hooks/useSelectableClick';
|
import { useSelectableClick } from 'flavours/glitch/hooks/useSelectableClick';
|
||||||
|
|
||||||
|
import { IconButton } from '../icon_button';
|
||||||
|
|
||||||
|
import classes from './styles.module.scss';
|
||||||
|
|
||||||
const offset = [0, 4] as OffsetValue;
|
const offset = [0, 4] as OffsetValue;
|
||||||
const popperConfig = { strategy: 'fixed' } as UsePopperOptions;
|
const popperConfig = { strategy: 'fixed' } as UsePopperOptions;
|
||||||
|
|
||||||
export const AltTextBadge: React.FC<{ description: string }> = ({
|
export const AltTextBadge: React.FC<{ description: string }> = ({
|
||||||
description,
|
description,
|
||||||
}) => {
|
}) => {
|
||||||
const accessibilityId = useId();
|
const intl = useIntl();
|
||||||
const anchorRef = useRef<HTMLButtonElement>(null);
|
const uniqueId = useId();
|
||||||
|
const popoverId = `${uniqueId}-popover`;
|
||||||
|
const titleId = `${uniqueId}-title`;
|
||||||
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||||
|
const popoverRef = useRef<HTMLDivElement>(null);
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
setOpen((v) => !v);
|
setOpen((v) => !v);
|
||||||
|
setTimeout(() => {
|
||||||
|
popoverRef.current?.focus();
|
||||||
|
}, 0);
|
||||||
}, [setOpen]);
|
}, [setOpen]);
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
|
buttonRef.current?.focus();
|
||||||
}, [setOpen]);
|
}, [setOpen]);
|
||||||
|
|
||||||
const [handleMouseDown, handleMouseUp] = useSelectableClick(handleClose);
|
const [handleMouseDown, handleMouseUp] = useSelectableClick(handleClose);
|
||||||
@@ -34,11 +47,12 @@ export const AltTextBadge: React.FC<{ description: string }> = ({
|
|||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
ref={anchorRef}
|
ref={buttonRef}
|
||||||
className='media-gallery__alt__label'
|
className='media-gallery__alt__label'
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
aria-expanded={open}
|
aria-expanded={open}
|
||||||
aria-controls={accessibilityId}
|
aria-controls={popoverId}
|
||||||
|
aria-haspopup='dialog'
|
||||||
>
|
>
|
||||||
ALT
|
ALT
|
||||||
</button>
|
</button>
|
||||||
@@ -47,7 +61,7 @@ export const AltTextBadge: React.FC<{ description: string }> = ({
|
|||||||
rootClose
|
rootClose
|
||||||
onHide={handleClose}
|
onHide={handleClose}
|
||||||
show={open}
|
show={open}
|
||||||
target={anchorRef}
|
target={buttonRef}
|
||||||
placement='top-end'
|
placement='top-end'
|
||||||
flip
|
flip
|
||||||
offset={offset}
|
offset={offset}
|
||||||
@@ -57,17 +71,33 @@ export const AltTextBadge: React.FC<{ description: string }> = ({
|
|||||||
<div {...props} className='hover-card-controller'>
|
<div {...props} className='hover-card-controller'>
|
||||||
<div // eslint-disable-line jsx-a11y/no-noninteractive-element-interactions
|
<div // eslint-disable-line jsx-a11y/no-noninteractive-element-interactions
|
||||||
className='info-tooltip dropdown-animation'
|
className='info-tooltip dropdown-animation'
|
||||||
role='region'
|
role='dialog'
|
||||||
id={accessibilityId}
|
aria-labelledby={titleId}
|
||||||
|
ref={popoverRef}
|
||||||
|
id={popoverId}
|
||||||
onMouseDown={handleMouseDown}
|
onMouseDown={handleMouseDown}
|
||||||
onMouseUp={handleMouseUp}
|
onMouseUp={handleMouseUp}
|
||||||
|
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
||||||
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<h4>
|
<h4 id={titleId}>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='alt_text_badge.title'
|
id='alt_text_badge.title'
|
||||||
defaultMessage='Alt text'
|
defaultMessage='Alt text'
|
||||||
/>
|
/>
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id: 'lightbox.close',
|
||||||
|
defaultMessage: 'Close',
|
||||||
|
})}
|
||||||
|
icon='close'
|
||||||
|
iconComponent={CloseIcon}
|
||||||
|
onClick={handleClose}
|
||||||
|
className={classes.closeButton}
|
||||||
|
/>
|
||||||
|
|
||||||
<p>{description}</p>
|
<p>{description}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
.closeButton {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
inset-inline-end: 2px;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
--default-icon-color: var(--color-text-on-media);
|
||||||
|
--default-bg-color: transparent;
|
||||||
|
--hover-icon-color: var(--color-text-on-media);
|
||||||
|
--hover-bg-color: rgb(from var(--color-text-on-media) r g b / 10%);
|
||||||
|
--focus-outline-color: var(--color-text-on-media);
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -96,7 +96,7 @@ export const Avatar: React.FC<Props> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const AvatarById: React.FC<
|
export const AvatarById: React.FC<
|
||||||
{ accountId: string } & Omit<Props, 'account'>
|
{ accountId: string | undefined } & Omit<Props, 'account'>
|
||||||
> = ({ accountId, ...otherProps }) => {
|
> = ({ accountId, ...otherProps }) => {
|
||||||
const account = useAccount(accountId);
|
const account = useAccount(accountId);
|
||||||
return <Avatar account={account} {...otherProps} />;
|
return <Avatar account={account} {...otherProps} />;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export const Badge: FC<BadgeProps> = ({
|
|||||||
data-account-role-id={roleId}
|
data-account-role-id={roleId}
|
||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
{label}
|
<span>{label}</span>
|
||||||
{domain && <span className='account-role__domain'>{domain}</span>}
|
{domain && <span className='account-role__domain'>{domain}</span>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
align-items: start;
|
align-items: start;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
background-color: var(--color-bg-brand-softer);
|
background-color: var(--color-bg-brand-softest);
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
}
|
}
|
||||||
@@ -86,11 +86,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.variantSubtle {
|
.variantSubtle {
|
||||||
border: 1px solid var(--color-bg-brand-softer);
|
border: 1px solid var(--color-bg-brand-softest);
|
||||||
background-color: var(--color-bg-primary);
|
background-color: var(--color-bg-primary);
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
background-color: var(--color-bg-brand-softer);
|
background-color: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,11 +105,11 @@
|
|||||||
|
|
||||||
.variantInverted {
|
.variantInverted {
|
||||||
background-color: var(--color-bg-inverted);
|
background-color: var(--color-bg-inverted);
|
||||||
color: var(--color-text-on-inverted);
|
color: var(--color-text-inverted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.variantSuccess {
|
.variantSuccess {
|
||||||
background-color: var(--color-bg-success-softer);
|
background-color: var(--color-bg-success-softest);
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
background-color: var(--color-bg-success-soft);
|
background-color: var(--color-bg-success-soft);
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.variantWarning {
|
.variantWarning {
|
||||||
background-color: var(--color-bg-warning-softer);
|
background-color: var(--color-bg-warning-softest);
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
background-color: var(--color-bg-warning-soft);
|
background-color: var(--color-bg-warning-soft);
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.variantError {
|
.variantError {
|
||||||
background-color: var(--color-bg-error-softer);
|
background-color: var(--color-bg-error-softest);
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
background-color: var(--color-bg-error-soft);
|
background-color: var(--color-bg-error-soft);
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ import { Skeleton } from '../skeleton';
|
|||||||
import type { DisplayNameProps } from './index';
|
import type { DisplayNameProps } from './index';
|
||||||
import { DisplayNameWithoutDomain } from './no-domain';
|
import { DisplayNameWithoutDomain } from './no-domain';
|
||||||
|
|
||||||
export const DisplayNameDefault: FC<
|
export function useAccountHandle(
|
||||||
Omit<DisplayNameProps, 'variant'> & ComponentPropsWithoutRef<'span'>
|
account: DisplayNameProps['account'],
|
||||||
> = ({ account, localDomain, className, ...props }) => {
|
localDomain: DisplayNameProps['localDomain'],
|
||||||
const username = useMemo(() => {
|
) {
|
||||||
|
return useMemo(() => {
|
||||||
if (!account) {
|
if (!account) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -20,6 +21,12 @@ export const DisplayNameDefault: FC<
|
|||||||
}
|
}
|
||||||
return `@${acct}`;
|
return `@${acct}`;
|
||||||
}, [account, localDomain]);
|
}, [account, localDomain]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DisplayNameDefault: FC<
|
||||||
|
Omit<DisplayNameProps, 'variant'> & ComponentPropsWithoutRef<'span'>
|
||||||
|
> = ({ account, localDomain, className, ...props }) => {
|
||||||
|
const username = useAccountHandle(account, localDomain);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DisplayNameWithoutDomain
|
<DisplayNameWithoutDomain
|
||||||
|
|||||||
@@ -61,6 +61,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[data-has-error='true'] & {
|
[data-has-error='true'] & {
|
||||||
border-color: var(--color-text-error);
|
border-color: var(--color-border-error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,11 +32,11 @@
|
|||||||
&:focus:user-invalid,
|
&:focus:user-invalid,
|
||||||
&:required:user-invalid,
|
&:required:user-invalid,
|
||||||
[data-has-error='true'] & {
|
[data-has-error='true'] & {
|
||||||
outline-color: var(--color-text-error);
|
outline-color: var(--color-border-error);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline-color: var(--color-text-brand);
|
outline-color: var(--color-border-brand);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:required:user-valid {
|
&:required:user-valid {
|
||||||
|
|||||||
26
app/javascript/flavours/glitch/components/intl.tsx
Normal file
26
app/javascript/flavours/glitch/components/intl.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import type { ComponentClass } from 'react';
|
||||||
|
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
interface IntlHocProps<TProps extends Record<string, unknown>> {
|
||||||
|
component: ComponentClass<TProps>;
|
||||||
|
props: TProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const IntlHoc = <TProps extends Record<string, unknown>>({
|
||||||
|
component: Component,
|
||||||
|
props,
|
||||||
|
}: IntlHocProps<TProps>) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
return <Component {...props} intl={intl} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const injectIntl = <TProps extends Record<string, unknown>>(
|
||||||
|
Component: ComponentClass<TProps>,
|
||||||
|
) => {
|
||||||
|
const WrappedComponent = (props: Omit<TProps, 'intl'>) => (
|
||||||
|
<IntlHoc component={Component} props={props as TProps} />
|
||||||
|
);
|
||||||
|
WrappedComponent.displayName = `injectIntl(${(Component.displayName ?? Component.name) || 'Component'})`;
|
||||||
|
return WrappedComponent;
|
||||||
|
};
|
||||||
@@ -6,16 +6,12 @@ import { AuthorLink } from 'flavours/glitch/features/explore/components/author_l
|
|||||||
export const MoreFromAuthor: React.FC<{ accountId: string }> = ({
|
export const MoreFromAuthor: React.FC<{ accountId: string }> = ({
|
||||||
accountId,
|
accountId,
|
||||||
}) => (
|
}) => (
|
||||||
<FormattedMessage
|
<div className='more-from-author'>
|
||||||
id='link_preview.more_from_author'
|
<IconLogo />
|
||||||
defaultMessage='More from {name}'
|
<FormattedMessage
|
||||||
values={{ name: <AuthorLink accountId={accountId} /> }}
|
id='link_preview.more_from_author'
|
||||||
>
|
defaultMessage='More from {name}'
|
||||||
{(chunks) => (
|
values={{ name: <AuthorLink accountId={accountId} /> }}
|
||||||
<div className='more-from-author'>
|
/>
|
||||||
<IconLogo />
|
</div>
|
||||||
{chunks}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</FormattedMessage>
|
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export const NotSignedInIndicator: React.FC = () => (
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='not_signed_in_indicator.not_signed_in'
|
id='not_signed_in_indicator.not_signed_in'
|
||||||
defaultMessage='You need to login to access this resource.'
|
defaultMessage='You need to login to access this resource.'
|
||||||
|
tagName='span'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export const RegenerationIndicator: React.FC = () => (
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='regeneration_indicator.please_stand_by'
|
id='regeneration_indicator.please_stand_by'
|
||||||
defaultMessage='Please stand by.'
|
defaultMessage='Please stand by.'
|
||||||
|
tagName='span'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -37,7 +37,11 @@ export const ItemList = forwardRef<
|
|||||||
}
|
}
|
||||||
>(({ isLoading, emptyMessage, className, children, ...otherProps }, ref) => {
|
>(({ isLoading, emptyMessage, className, children, ...otherProps }, ref) => {
|
||||||
if (!isLoading && Children.count(children) === 0 && emptyMessage) {
|
if (!isLoading && Children.count(children) === 0 && emptyMessage) {
|
||||||
return <div className='empty-column-indicator'>{emptyMessage}</div>;
|
return (
|
||||||
|
<div className='empty-column-indicator'>
|
||||||
|
<span>{emptyMessage}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -385,7 +385,7 @@ class ScrollableList extends PureComponent {
|
|||||||
{alwaysPrepend && prepend}
|
{alwaysPrepend && prepend}
|
||||||
|
|
||||||
<div className='empty-column-indicator'>
|
<div className='empty-column-indicator'>
|
||||||
{emptyMessage}
|
<span>{emptyMessage}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{footer}
|
{footer}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
import { FormattedMessage, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
@@ -14,6 +14,8 @@ import { ShortNumber } from 'flavours/glitch/components/short_number';
|
|||||||
import { Skeleton } from 'flavours/glitch/components/skeleton';
|
import { Skeleton } from 'flavours/glitch/components/skeleton';
|
||||||
import { domain } from 'flavours/glitch/initial_state';
|
import { domain } from 'flavours/glitch/initial_state';
|
||||||
|
|
||||||
|
import { injectIntl } from './intl';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
aboutActiveUsers: { id: 'server_banner.about_active_users', defaultMessage: 'People using this server during the last 30 days (Monthly Active Users)' },
|
aboutActiveUsers: { id: 'server_banner.about_active_users', defaultMessage: 'People using this server during the last 30 days (Monthly Active Users)' },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
@@ -21,6 +21,7 @@ import { MediaGallery, Video, Audio } from '../features/ui/util/async-components
|
|||||||
import { SensitiveMediaContext } from '../features/ui/util/sensitive_media_context';
|
import { SensitiveMediaContext } from '../features/ui/util/sensitive_media_context';
|
||||||
import { displayMedia } from '../initial_state';
|
import { displayMedia } from '../initial_state';
|
||||||
|
|
||||||
|
import { injectIntl } from './intl';
|
||||||
import AttachmentList from './attachment_list';
|
import AttachmentList from './attachment_list';
|
||||||
import { StatusHeader } from './status/header'
|
import { StatusHeader } from './status/header'
|
||||||
import { getHashtagBarForStatus } from './hashtag_bar';
|
import { getHashtagBarForStatus } from './hashtag_bar';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
@@ -25,11 +25,13 @@ import { Dropdown } from 'flavours/glitch/components/dropdown_menu';
|
|||||||
import { me, quickBoosting } from '../../initial_state';
|
import { me, quickBoosting } from '../../initial_state';
|
||||||
|
|
||||||
import { IconButton } from '../icon_button';
|
import { IconButton } from '../icon_button';
|
||||||
|
import { injectIntl } from '../intl';
|
||||||
import { RelativeTimestamp } from '../relative_timestamp';
|
import { RelativeTimestamp } from '../relative_timestamp';
|
||||||
import { BoostButton } from '../status/boost_button';
|
import { BoostButton } from '../status/boost_button';
|
||||||
import { RemoveQuoteHint } from './remove_quote_hint';
|
import { RemoveQuoteHint } from './remove_quote_hint';
|
||||||
import { quoteItemState, selectStatusState } from '../status/boost_button_utils';
|
import { quoteItemState, selectStatusState } from '../status/boost_button_utils';
|
||||||
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
||||||
redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
|
redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage, injectIntl } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
@@ -16,6 +16,7 @@ import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity
|
|||||||
import { languages as preloadedLanguages } from 'flavours/glitch/initial_state';
|
import { languages as preloadedLanguages } from 'flavours/glitch/initial_state';
|
||||||
|
|
||||||
import { EmojiHTML } from './emoji/html';
|
import { EmojiHTML } from './emoji/html';
|
||||||
|
import { injectIntl } from './intl';
|
||||||
import { HandledLink } from './status/handled_link';
|
import { HandledLink } from './status/handled_link';
|
||||||
|
|
||||||
const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
|
const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ import HomeIcon from '@/material-icons/400-24px/home.svg?react';
|
|||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { MediaIcon } from 'flavours/glitch/components/media_icon';
|
import { MediaIcon } from 'flavours/glitch/components/media_icon';
|
||||||
import { languages } from 'flavours/glitch/initial_state';
|
import { languages } from 'flavours/glitch/initial_state';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
|
|
||||||
import { VisibilityIcon } from './visibility_icon';
|
import { VisibilityIcon } from './visibility_icon';
|
||||||
|
|
||||||
|
|||||||
@@ -161,7 +161,9 @@ export default class StatusPrepend extends PureComponent {
|
|||||||
icon={iconComponent}
|
icon={iconComponent}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Message />
|
<span>
|
||||||
|
<Message />
|
||||||
|
</span>
|
||||||
{children}
|
{children}
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -336,7 +336,6 @@ export const QuotedStatus: React.FC<QuotedStatusProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='status__quote'>
|
<div className='status__quote'>
|
||||||
{/* @ts-expect-error Status is not yet typed */}
|
|
||||||
<StatusContainer
|
<StatusContainer
|
||||||
isQuotedPost
|
isQuotedPost
|
||||||
id={quotedStatusId}
|
id={quotedStatusId}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export const StatusThreadLabel: React.FC<{
|
|||||||
<div className='status__prepend__icon'>
|
<div className='status__prepend__icon'>
|
||||||
<Icon id='reply' icon={ReplyIcon} />
|
<Icon id='reply' icon={ReplyIcon} />
|
||||||
</div>
|
</div>
|
||||||
{label}
|
<span>{label}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ button.tag:focus-visible {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.active {
|
.active {
|
||||||
border-color: var(--color-text-brand);
|
border-color: var(--color-border-brand);
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
color: var(--color-text-brand);
|
color: var(--color-text-brand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import { fetchServer, fetchExtendedDescription, fetchDomainBlocks } from 'flavours/glitch/actions/server';
|
import { fetchServer, fetchExtendedDescription, fetchDomainBlocks } from 'flavours/glitch/actions/server';
|
||||||
import { Account } from 'flavours/glitch/components/account';
|
import { Account } from 'flavours/glitch/components/account';
|
||||||
import Column from 'flavours/glitch/components/column';
|
import Column from 'flavours/glitch/components/column';
|
||||||
|
|||||||
@@ -63,7 +63,11 @@ export const ActionBar: React.FC<{ account: Account }> = ({ account }) => {
|
|||||||
className='account__action-bar__tab'
|
className='account__action-bar__tab'
|
||||||
to={`/@${account.get('acct')}`}
|
to={`/@${account.get('acct')}`}
|
||||||
>
|
>
|
||||||
<FormattedMessage id='account.posts' defaultMessage='Posts' />
|
<FormattedMessage
|
||||||
|
id='account.posts'
|
||||||
|
defaultMessage='Posts'
|
||||||
|
tagName='span'
|
||||||
|
/>
|
||||||
<strong>
|
<strong>
|
||||||
<FormattedNumber value={account.get('statuses_count')} />
|
<FormattedNumber value={account.get('statuses_count')} />
|
||||||
</strong>
|
</strong>
|
||||||
@@ -75,7 +79,11 @@ export const ActionBar: React.FC<{ account: Account }> = ({ account }) => {
|
|||||||
className='account__action-bar__tab'
|
className='account__action-bar__tab'
|
||||||
to={`/@${account.get('acct')}/following`}
|
to={`/@${account.get('acct')}/following`}
|
||||||
>
|
>
|
||||||
<FormattedMessage id='account.follows' defaultMessage='Follows' />
|
<FormattedMessage
|
||||||
|
id='account.follows'
|
||||||
|
defaultMessage='Follows'
|
||||||
|
tagName='span'
|
||||||
|
/>
|
||||||
<strong>
|
<strong>
|
||||||
<FormattedNumber value={account.get('following_count')} />
|
<FormattedNumber value={account.get('following_count')} />
|
||||||
</strong>
|
</strong>
|
||||||
@@ -90,6 +98,7 @@ export const ActionBar: React.FC<{ account: Account }> = ({ account }) => {
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='account.followers'
|
id='account.followers'
|
||||||
defaultMessage='Followers'
|
defaultMessage='Followers'
|
||||||
|
tagName='span'
|
||||||
/>
|
/>
|
||||||
<strong>
|
<strong>
|
||||||
{account.get('followers_count') < 0 ? (
|
{account.get('followers_count') < 0 ? (
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useCallback, useState } from 'react';
|
|||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import { CharacterCounter } from '@/flavours/glitch/components/character_counter';
|
||||||
import { Details } from '@/flavours/glitch/components/details';
|
import { Details } from '@/flavours/glitch/components/details';
|
||||||
import { TextAreaField } from '@/flavours/glitch/components/form_fields';
|
import { TextAreaField } from '@/flavours/glitch/components/form_fields';
|
||||||
import { LoadingIndicator } from '@/flavours/glitch/components/loading_indicator';
|
import { LoadingIndicator } from '@/flavours/glitch/components/loading_indicator';
|
||||||
@@ -84,7 +85,12 @@ export const ImageAltTextField: FC<{
|
|||||||
const altLimit = useAppSelector(
|
const altLimit = useAppSelector(
|
||||||
(state) =>
|
(state) =>
|
||||||
state.server.getIn(
|
state.server.getIn(
|
||||||
['server', 'configuration', 'media_attachments', 'description_limit'],
|
[
|
||||||
|
'server',
|
||||||
|
'configuration',
|
||||||
|
'accounts',
|
||||||
|
'max_header_description_length',
|
||||||
|
],
|
||||||
150,
|
150,
|
||||||
) as number,
|
) as number,
|
||||||
);
|
);
|
||||||
@@ -100,23 +106,26 @@ export const ImageAltTextField: FC<{
|
|||||||
<>
|
<>
|
||||||
<img src={imageSrc} alt='' className={classes.altImage} />
|
<img src={imageSrc} alt='' className={classes.altImage} />
|
||||||
|
|
||||||
<TextAreaField
|
<div>
|
||||||
label={
|
<TextAreaField
|
||||||
<FormattedMessage
|
label={
|
||||||
id='account_edit.image_alt_modal.text_label'
|
<FormattedMessage
|
||||||
defaultMessage='Alt text'
|
id='account_edit.image_alt_modal.text_label'
|
||||||
/>
|
defaultMessage='Alt text'
|
||||||
}
|
/>
|
||||||
hint={
|
}
|
||||||
<FormattedMessage
|
hint={
|
||||||
id='account_edit.image_alt_modal.text_hint'
|
<FormattedMessage
|
||||||
defaultMessage='Alt text helps screen reader users to understand your content.'
|
id='account_edit.image_alt_modal.text_hint'
|
||||||
/>
|
defaultMessage='Alt text helps screen reader users to understand your content.'
|
||||||
}
|
/>
|
||||||
onChange={handleChange}
|
}
|
||||||
value={altText}
|
onChange={handleChange}
|
||||||
maxLength={altLimit}
|
value={altText}
|
||||||
/>
|
maxLength={altLimit}
|
||||||
|
/>
|
||||||
|
<CharacterCounter currentString={altText} maxLength={altLimit} />
|
||||||
|
</div>
|
||||||
|
|
||||||
{!hideTip && (
|
{!hideTip && (
|
||||||
<Details
|
<Details
|
||||||
@@ -130,7 +139,7 @@ export const ImageAltTextField: FC<{
|
|||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='account_edit.image_alt_modal.details_content'
|
id='account_edit.image_alt_modal.details_content'
|
||||||
defaultMessage='DO: <ul> <li>Describe yourself as pictured</li> <li>Use third person language (e.g. “Alex” instead of “me”)</li> <li>Be succinct – a few words is often enough</li> </ul> DON’T: <ul> <li>Start with “Photo of” – it’s redundant for screen readers</li> </ul> EXAMPLE: <ul> <li>“Alex wearing a green shirt and glasses”</li> </ul>'
|
defaultMessage='DO: <ul> <li>Describe yourself as pictured</li> <li>Use third person language (e.g. “Alex” instead of “me”)</li> <li>Be succinct – a few words is often enough</li> </ul> DON’T: <ul> <li>Start with “Photo of” – it’s redundant for screen readers</li> </ul> EXAMPLE: <ul> <li>“Alex wearing a green shirt and glasses”</li></ul>'
|
||||||
values={{
|
values={{
|
||||||
ul: (chunks) => <ul>{chunks}</ul>,
|
ul: (chunks) => <ul>{chunks}</ul>,
|
||||||
li: (chunks) => <li>{chunks}</li>,
|
li: (chunks) => <li>{chunks}</li>,
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
transition: background 0.2s ease-in-out;
|
transition: background 0.2s ease-in-out;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
|
|||||||
@@ -160,7 +160,7 @@
|
|||||||
&:active,
|
&:active,
|
||||||
&:focus,
|
&:focus,
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--color-bg-brand-softer);
|
background-color: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
@@ -177,7 +177,7 @@
|
|||||||
|
|
||||||
.deleteButton {
|
.deleteButton {
|
||||||
--default-icon-color: var(--color-text-error);
|
--default-icon-color: var(--color-text-error);
|
||||||
--hover-bg-color: var(--color-bg-error-base-hover);
|
--hover-bg-color: var(--color-bg-error-base);
|
||||||
--hover-icon-color: var(--color-text-on-error-base);
|
--hover-icon-color: var(--color-text-on-error-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +201,7 @@
|
|||||||
&,
|
&,
|
||||||
&:global(.active) {
|
&:global(.active) {
|
||||||
// Overrides the transparent background added by default with .active
|
// Overrides the transparent background added by default with .active
|
||||||
--hover-bg-color: var(--color-bg-brand-softer-solid);
|
--hover-bg-color: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -65,5 +65,9 @@ export const EmptyMessage: React.FC<EmptyMessageProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className='empty-column-indicator'>{message}</div>;
|
return (
|
||||||
|
<div className='empty-column-indicator'>
|
||||||
|
<span>{message}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
|
|||||||
key={item.id}
|
key={item.id}
|
||||||
collection={item}
|
collection={item}
|
||||||
withoutBorder={index === listedCollections.length - 1}
|
withoutBorder={index === listedCollections.length - 1}
|
||||||
|
withAuthorHandle={false}
|
||||||
positionInList={index + 1}
|
positionInList={index + 1}
|
||||||
listSize={listedCollections.length}
|
listSize={listedCollections.length}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -9,6 +9,11 @@
|
|||||||
|
|
||||||
.barWrapper {
|
.barWrapper {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
|
padding-inline: 24px;
|
||||||
|
|
||||||
|
@container (width < 500px) {
|
||||||
|
padding-inline: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatarWrapper {
|
.avatarWrapper {
|
||||||
@@ -93,7 +98,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
@@ -125,7 +130,7 @@ $button-fallback-breakpoint: $button-breakpoint + 55px;
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
bottom: var(--mobile-bottom-nav-height);
|
bottom: var(--mobile-bottom-nav-height);
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
margin: 0 -20px;
|
margin: 0 -16px;
|
||||||
|
|
||||||
@container (width >= #{$button-breakpoint}) {
|
@container (width >= #{$button-breakpoint}) {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -184,7 +189,7 @@ $button-fallback-breakpoint: $button-breakpoint + 55px;
|
|||||||
|
|
||||||
.badgeMuted {
|
.badgeMuted {
|
||||||
background-color: var(--color-bg-inverted);
|
background-color: var(--color-bg-inverted);
|
||||||
color: var(--color-text-on-inverted);
|
color: var(--color-text-inverted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.badgeBlocked {
|
.badgeBlocked {
|
||||||
@@ -270,7 +275,7 @@ svg.badgeIcon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fieldVerified {
|
.fieldVerified {
|
||||||
background-color: var(--color-bg-success-softer);
|
background-color: var(--color-bg-success-softest);
|
||||||
|
|
||||||
dt {
|
dt {
|
||||||
padding-right: 24px;
|
padding-right: 24px;
|
||||||
@@ -292,8 +297,8 @@ svg.badgeIcon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fieldOverflowButton {
|
.fieldOverflowButton {
|
||||||
--default-bg-color: var(--color-bg-secondary-solid);
|
--default-bg-color: var(--color-bg-secondary);
|
||||||
--hover-bg-color: var(--color-bg-brand-softer-solid);
|
--hover-bg-color: var(--color-bg-brand-softest);
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 8px;
|
right: 8px;
|
||||||
@@ -388,7 +393,7 @@ svg.badgeIcon {
|
|||||||
padding: 0 24px;
|
padding: 0 24px;
|
||||||
|
|
||||||
@container (width < 500px) {
|
@container (width < 500px) {
|
||||||
padding: 0 12px;
|
padding: 0 16px;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
flex: 1 1 0px;
|
flex: 1 1 0px;
|
||||||
@@ -413,7 +418,7 @@ svg.badgeIcon {
|
|||||||
|
|
||||||
:global(.active) {
|
:global(.active) {
|
||||||
color: var(--color-text-brand);
|
color: var(--color-text-brand);
|
||||||
border-bottom: 4px solid var(--color-text-brand);
|
border-bottom: 4px solid var(--color-border-brand);
|
||||||
padding-bottom: 14px;
|
padding-bottom: 14px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ export const TagSuggestions: FC = () => {
|
|||||||
values={{
|
values={{
|
||||||
link: (chunks) => <Link to='/profile/featured_tags'>{chunks}</Link>,
|
link: (chunks) => <Link to='/profile/featured_tags'>{chunks}</Link>,
|
||||||
}}
|
}}
|
||||||
|
tagName='span'
|
||||||
/>
|
/>
|
||||||
</Callout>
|
</Callout>
|
||||||
);
|
);
|
||||||
@@ -122,6 +123,7 @@ export const TagSuggestions: FC = () => {
|
|||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
|
tagName='span'
|
||||||
/>
|
/>
|
||||||
</Callout>
|
</Callout>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ export const InfoButton: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='info_button.what_is_alt_text'
|
id='info_button.what_is_alt_text'
|
||||||
|
// eslint-disable-next-line formatjs/prefer-full-sentence
|
||||||
defaultMessage='<h1>What is alt text?</h1>
|
defaultMessage='<h1>What is alt text?</h1>
|
||||||
|
|
||||||
<p>Alt text provides image descriptions for people with vision impairments, low-bandwidth connections, or those seeking extra context.</p>
|
<p>Alt text provides image descriptions for people with vision impairments, low-bandwidth connections, or those seeking extra context.</p>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
@@ -8,6 +8,7 @@ import { connect } from 'react-redux';
|
|||||||
|
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
|
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import BlockIcon from '@/material-icons/400-24px/block-fill.svg?react';
|
import BlockIcon from '@/material-icons/400-24px/block-fill.svg?react';
|
||||||
import { Account } from 'flavours/glitch/components/account';
|
import { Account } from 'flavours/glitch/components/account';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Fragment, useCallback, useRef, useState } from 'react';
|
import { useCallback, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
@@ -100,14 +100,12 @@ const RevokeControls: React.FC<{
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='collections.detail.accept_inclusion'
|
id='collections.detail.accept_inclusion'
|
||||||
defaultMessage='Okay'
|
defaultMessage='Okay'
|
||||||
tagName={Fragment}
|
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button secondary onClick={confirmRevoke}>
|
<Button secondary onClick={confirmRevoke}>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='collections.detail.revoke_inclusion'
|
id='collections.detail.revoke_inclusion'
|
||||||
defaultMessage='Remove me'
|
defaultMessage='Remove me'
|
||||||
tagName={Fragment}
|
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -143,7 +141,6 @@ const SensitiveScreen: React.FC<{
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='content_warning.show'
|
id='content_warning.show'
|
||||||
defaultMessage='Show anyway'
|
defaultMessage='Show anyway'
|
||||||
tagName={Fragment}
|
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -205,7 +202,6 @@ export const CollectionAccountsList: React.FC<{
|
|||||||
values={{
|
values={{
|
||||||
author: <SimpleAuthorName id={collection.account_id} />,
|
author: <SimpleAuthorName id={collection.account_id} />,
|
||||||
}}
|
}}
|
||||||
tagName={Fragment}
|
|
||||||
/>
|
/>
|
||||||
</h3>
|
</h3>
|
||||||
<Article
|
<Article
|
||||||
@@ -231,7 +227,6 @@ export const CollectionAccountsList: React.FC<{
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='collections.detail.other_accounts_in_collection'
|
id='collections.detail.other_accounts_in_collection'
|
||||||
defaultMessage='Others in this collection:'
|
defaultMessage='Others in this collection:'
|
||||||
tagName={Fragment}
|
|
||||||
/>
|
/>
|
||||||
</h3>
|
</h3>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
.wrapper {
|
.wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: start;
|
||||||
|
margin-inline: 24px;
|
||||||
|
padding-block: 12px;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
padding-inline: 16px;
|
|
||||||
|
|
||||||
&:not(.wrapperWithoutBorder) {
|
&:not(.wrapperWithoutBorder) {
|
||||||
border-bottom: 1px solid var(--color-border-primary);
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
@@ -12,12 +13,42 @@
|
|||||||
.content {
|
.content {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
padding-block: 15px;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
column-gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatarGrid {
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, min-content);
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
&.avatarGridSensitive {
|
||||||
|
.avatar {
|
||||||
|
filter: blur(4px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
background: var(--color-bg-brand-softest);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatarSensitiveBadge {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
margin: auto;
|
||||||
|
padding: 3px;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
background: var(--color-bg-warning-softest);
|
||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
.link {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 2px;
|
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@@ -40,15 +71,12 @@
|
|||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.metaList {
|
.menuButton {
|
||||||
--gap: 0.75ch;
|
padding: 4px;
|
||||||
|
margin-top: -2px;
|
||||||
|
|
||||||
display: flex;
|
svg {
|
||||||
flex-wrap: wrap;
|
width: 20px;
|
||||||
gap: var(--gap);
|
height: 20px;
|
||||||
|
|
||||||
& > li:not(:last-child)::after {
|
|
||||||
content: '·';
|
|
||||||
margin-inline-start: var(--gap);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,70 +5,65 @@ import { FormattedMessage } from 'react-intl';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
import WarningIcon from '@/material-icons/400-24px/warning.svg?react';
|
||||||
import type { ApiCollectionJSON } from 'flavours/glitch/api_types/collections';
|
import type { ApiCollectionJSON } from 'flavours/glitch/api_types/collections';
|
||||||
|
import { AvatarById } from 'flavours/glitch/components/avatar';
|
||||||
|
import { useAccountHandle } from 'flavours/glitch/components/display_name/default';
|
||||||
import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp';
|
import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp';
|
||||||
import { Article } from 'flavours/glitch/components/scrollable_list/components';
|
import { Article } from 'flavours/glitch/components/scrollable_list/components';
|
||||||
|
import { useAccount } from 'flavours/glitch/hooks/useAccount';
|
||||||
|
import { domain } from 'flavours/glitch/initial_state';
|
||||||
|
|
||||||
import classes from './collection_list_item.module.scss';
|
import classes from './collection_list_item.module.scss';
|
||||||
import { CollectionMenu } from './collection_menu';
|
import { CollectionMenu } from './collection_menu';
|
||||||
|
|
||||||
export const CollectionMetaData: React.FC<{
|
export const AvatarGrid: React.FC<{
|
||||||
collection: ApiCollectionJSON;
|
accountIds: (string | undefined)[];
|
||||||
extended?: boolean;
|
sensitive?: boolean;
|
||||||
className?: string;
|
}> = ({ accountIds: ids, sensitive }) => {
|
||||||
}> = ({ collection, extended, className }) => {
|
const avatarIds = [ids[0], ids[1], ids[2], ids[3]];
|
||||||
return (
|
return (
|
||||||
<ul className={classNames(classes.metaList, className)}>
|
<div
|
||||||
<FormattedMessage
|
className={classNames(
|
||||||
id='collections.account_count'
|
classes.avatarGrid,
|
||||||
defaultMessage='{count, plural, one {# account} other {# accounts}}'
|
sensitive ? classes.avatarGridSensitive : null,
|
||||||
values={{ count: collection.item_count }}
|
|
||||||
tagName='li'
|
|
||||||
/>
|
|
||||||
{extended && (
|
|
||||||
<>
|
|
||||||
{collection.discoverable ? (
|
|
||||||
<FormattedMessage
|
|
||||||
id='collections.visibility_public'
|
|
||||||
defaultMessage='Public'
|
|
||||||
tagName='li'
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<FormattedMessage
|
|
||||||
id='collections.visibility_unlisted'
|
|
||||||
defaultMessage='Unlisted'
|
|
||||||
tagName='li'
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{collection.sensitive && (
|
|
||||||
<FormattedMessage
|
|
||||||
id='collections.sensitive'
|
|
||||||
defaultMessage='Sensitive'
|
|
||||||
tagName='li'
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
<FormattedMessage
|
>
|
||||||
id='collections.last_updated_at'
|
{avatarIds.map((id) => (
|
||||||
defaultMessage='Last updated: {date}'
|
<AvatarById
|
||||||
values={{
|
animate={false}
|
||||||
date: <RelativeTimestamp timestamp={collection.updated_at} long />,
|
key={id}
|
||||||
}}
|
accountId={id}
|
||||||
tagName='li'
|
className={classes.avatar}
|
||||||
/>
|
size={25}
|
||||||
</ul>
|
/>
|
||||||
|
))}
|
||||||
|
{sensitive && <WarningIcon className={classes.avatarSensitiveBadge} />}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CollectionListItem: React.FC<{
|
export const CollectionListItem: React.FC<{
|
||||||
collection: ApiCollectionJSON;
|
collection: ApiCollectionJSON;
|
||||||
withoutBorder?: boolean;
|
withoutBorder?: boolean;
|
||||||
|
withAuthorHandle?: boolean;
|
||||||
|
withTimestamp?: boolean;
|
||||||
positionInList: number;
|
positionInList: number;
|
||||||
listSize: number;
|
listSize: number;
|
||||||
}> = ({ collection, withoutBorder, positionInList, listSize }) => {
|
}> = ({
|
||||||
|
collection,
|
||||||
|
withoutBorder,
|
||||||
|
withAuthorHandle = true,
|
||||||
|
withTimestamp,
|
||||||
|
positionInList,
|
||||||
|
listSize,
|
||||||
|
}) => {
|
||||||
const { id, name } = collection;
|
const { id, name } = collection;
|
||||||
const linkId = useId();
|
const uniqueId = useId();
|
||||||
|
const linkId = `${uniqueId}-link`;
|
||||||
|
const infoId = `${uniqueId}-info`;
|
||||||
|
const authorAccount = useAccount(collection.account_id);
|
||||||
|
const authorHandle = useAccountHandle(authorAccount, domain);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Article
|
<Article
|
||||||
@@ -78,19 +73,67 @@ export const CollectionListItem: React.FC<{
|
|||||||
withoutBorder && classes.wrapperWithoutBorder,
|
withoutBorder && classes.wrapperWithoutBorder,
|
||||||
)}
|
)}
|
||||||
aria-labelledby={linkId}
|
aria-labelledby={linkId}
|
||||||
|
aria-describedby={infoId}
|
||||||
aria-posinset={positionInList}
|
aria-posinset={positionInList}
|
||||||
aria-setsize={listSize}
|
aria-setsize={listSize}
|
||||||
>
|
>
|
||||||
<div className={classes.content}>
|
<div className={classes.content}>
|
||||||
<h2 id={linkId}>
|
<AvatarGrid
|
||||||
<Link to={`/collections/${id}`} className={classes.link}>
|
accountIds={collection.items.map((item) => item.account_id)}
|
||||||
{name}
|
sensitive={collection.sensitive}
|
||||||
</Link>
|
/>
|
||||||
</h2>
|
<div>
|
||||||
<CollectionMetaData collection={collection} className={classes.info} />
|
<h2 id={linkId}>
|
||||||
|
<Link to={`/collections/${id}`} className={classes.link}>
|
||||||
|
{name}
|
||||||
|
</Link>
|
||||||
|
</h2>
|
||||||
|
<ul className={classes.info} id={infoId}>
|
||||||
|
{collection.sensitive && (
|
||||||
|
<li className='sr-only'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='collections.sensitive'
|
||||||
|
defaultMessage='Sensitive'
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
{withAuthorHandle && authorAccount && (
|
||||||
|
<FormattedMessage
|
||||||
|
id='collections.by_account'
|
||||||
|
defaultMessage='by {account_handle}'
|
||||||
|
values={{
|
||||||
|
account_handle: authorHandle,
|
||||||
|
}}
|
||||||
|
tagName='li'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<FormattedMessage
|
||||||
|
id='collections.account_count'
|
||||||
|
defaultMessage='{count, plural, one {# account} other {# accounts}}'
|
||||||
|
values={{ count: collection.item_count }}
|
||||||
|
tagName='li'
|
||||||
|
/>
|
||||||
|
{withTimestamp && (
|
||||||
|
<FormattedMessage
|
||||||
|
id='collections.last_updated_at'
|
||||||
|
defaultMessage='Last updated: {date}'
|
||||||
|
values={{
|
||||||
|
date: (
|
||||||
|
<RelativeTimestamp timestamp={collection.updated_at} long />
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
tagName='li'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CollectionMenu context='list' collection={collection} />
|
<CollectionMenu
|
||||||
|
context='list'
|
||||||
|
collection={collection}
|
||||||
|
className={classes.menuButton}
|
||||||
|
/>
|
||||||
</Article>
|
</Article>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { Helmet } from 'react-helmet';
|
|||||||
import { useHistory, useLocation, useParams } from 'react-router';
|
import { useHistory, useLocation, useParams } from 'react-router';
|
||||||
|
|
||||||
import { openModal } from '@/flavours/glitch/actions/modal';
|
import { openModal } from '@/flavours/glitch/actions/modal';
|
||||||
|
import { RelativeTimestamp } from '@/flavours/glitch/components/relative_timestamp';
|
||||||
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
|
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
|
||||||
import ShareIcon from '@/material-icons/400-24px/share.svg?react';
|
import ShareIcon from '@/material-icons/400-24px/share.svg?react';
|
||||||
import type { ApiCollectionJSON } from 'flavours/glitch/api_types/collections';
|
import type { ApiCollectionJSON } from 'flavours/glitch/api_types/collections';
|
||||||
@@ -25,7 +26,6 @@ import { fetchCollection } from 'flavours/glitch/reducers/slices/collections';
|
|||||||
import { useAppDispatch, useAppSelector } from 'flavours/glitch/store';
|
import { useAppDispatch, useAppSelector } from 'flavours/glitch/store';
|
||||||
|
|
||||||
import { CollectionAccountsList } from './accounts_list';
|
import { CollectionAccountsList } from './accounts_list';
|
||||||
import { CollectionMetaData } from './collection_list_item';
|
|
||||||
import { CollectionMenu } from './collection_menu';
|
import { CollectionMenu } from './collection_menu';
|
||||||
import classes from './styles.module.scss';
|
import classes from './styles.module.scss';
|
||||||
|
|
||||||
@@ -40,6 +40,54 @@ const messages = defineMessages({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const CollectionMetaData: React.FC<{
|
||||||
|
collection: ApiCollectionJSON;
|
||||||
|
extended?: boolean;
|
||||||
|
}> = ({ collection, extended }) => {
|
||||||
|
return (
|
||||||
|
<ul className={classes.metaList}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='collections.account_count'
|
||||||
|
defaultMessage='{count, plural, one {# account} other {# accounts}}'
|
||||||
|
values={{ count: collection.item_count }}
|
||||||
|
tagName='li'
|
||||||
|
/>
|
||||||
|
{extended && (
|
||||||
|
<>
|
||||||
|
{collection.discoverable ? (
|
||||||
|
<FormattedMessage
|
||||||
|
id='collections.visibility_public'
|
||||||
|
defaultMessage='Public'
|
||||||
|
tagName='li'
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<FormattedMessage
|
||||||
|
id='collections.visibility_unlisted'
|
||||||
|
defaultMessage='Unlisted'
|
||||||
|
tagName='li'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{collection.sensitive && (
|
||||||
|
<FormattedMessage
|
||||||
|
id='collections.sensitive'
|
||||||
|
defaultMessage='Sensitive'
|
||||||
|
tagName='li'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<FormattedMessage
|
||||||
|
id='collections.last_updated_at'
|
||||||
|
defaultMessage='Last updated: {date}'
|
||||||
|
values={{
|
||||||
|
date: <RelativeTimestamp timestamp={collection.updated_at} long />,
|
||||||
|
}}
|
||||||
|
tagName='li'
|
||||||
|
/>
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const AuthorNote: React.FC<{ id: string; previewMode?: boolean }> = ({
|
export const AuthorNote: React.FC<{ id: string; previewMode?: boolean }> = ({
|
||||||
id,
|
id,
|
||||||
// When previewMode is enabled, your own display name
|
// When previewMode is enabled, your own display name
|
||||||
@@ -137,7 +185,6 @@ const CollectionHeader: React.FC<{ collection: ApiCollectionJSON }> = ({
|
|||||||
<CollectionMetaData
|
<CollectionMetaData
|
||||||
extended={account_id === me}
|
extended={account_id === me}
|
||||||
collection={collection}
|
collection={collection}
|
||||||
className={classes.metaData}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -52,9 +52,19 @@
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.metaData {
|
.metaList {
|
||||||
|
--gap: 0.75ch;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
|
gap: var(--gap);
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
|
|
||||||
|
& > li:not(:last-child)::after {
|
||||||
|
content: '·';
|
||||||
|
margin-inline-start: var(--gap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.columnSubheading {
|
.columnSubheading {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Fragment, useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage, useIntl } from 'react-intl';
|
import { FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
@@ -413,7 +413,6 @@ const LanguageField: React.FC = () => {
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='collections.collection_language_none'
|
id='collections.collection_language_none'
|
||||||
defaultMessage='None'
|
defaultMessage='None'
|
||||||
tagName={Fragment}
|
|
||||||
/>
|
/>
|
||||||
</option>
|
</option>
|
||||||
{languages?.map(([code, name, localName]) => (
|
{languages?.map(([code, name, localName]) => (
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export const Collections: React.FC<{
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='collections.error_loading_collections'
|
id='collections.error_loading_collections'
|
||||||
defaultMessage='There was an error when trying to load your collections.'
|
defaultMessage='There was an error when trying to load your collections.'
|
||||||
|
tagName='span'
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -92,6 +93,8 @@ export const Collections: React.FC<{
|
|||||||
<ItemList emptyMessage={emptyMessage} isLoading={status === 'loading'}>
|
<ItemList emptyMessage={emptyMessage} isLoading={status === 'loading'}>
|
||||||
{collections.map((item, index) => (
|
{collections.map((item, index) => (
|
||||||
<CollectionListItem
|
<CollectionListItem
|
||||||
|
withTimestamp
|
||||||
|
withAuthorHandle={false}
|
||||||
key={item.id}
|
key={item.id}
|
||||||
collection={item}
|
collection={item}
|
||||||
positionInList={index + 1}
|
positionInList={index + 1}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import SettingText from 'flavours/glitch/components/setting_text';
|
import SettingText from 'flavours/glitch/components/setting_text';
|
||||||
import SettingToggle from 'flavours/glitch/features/notifications/components/setting_toggle';
|
import SettingToggle from 'flavours/glitch/features/notifications/components/setting_toggle';
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner';
|
import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner';
|
||||||
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
|
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
|
||||||
import { domain, localLiveFeedAccess } from 'flavours/glitch/initial_state';
|
import { domain, localLiveFeedAccess } from 'flavours/glitch/initial_state';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { createRef } from 'react';
|
import { createRef } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
@@ -15,6 +15,7 @@ import { missingAltTextModal } from 'flavours/glitch/initial_state';
|
|||||||
import AutosuggestInput from 'flavours/glitch/components/autosuggest_input';
|
import AutosuggestInput from 'flavours/glitch/components/autosuggest_input';
|
||||||
import AutosuggestTextarea from 'flavours/glitch/components/autosuggest_textarea';
|
import AutosuggestTextarea from 'flavours/glitch/components/autosuggest_textarea';
|
||||||
import { Button } from 'flavours/glitch/components/button';
|
import { Button } from 'flavours/glitch/components/button';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import EmojiPickerDropdown from '../containers/emoji_picker_dropdown_container';
|
import EmojiPickerDropdown from '../containers/emoji_picker_dropdown_container';
|
||||||
import PollButtonContainer from '../containers/poll_button_container';
|
import PollButtonContainer from '../containers/poll_button_container';
|
||||||
import SpoilerButtonContainer from '../containers/spoiler_button_container';
|
import SpoilerButtonContainer from '../containers/spoiler_button_container';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ import Overlay from 'react-overlays/Overlay';
|
|||||||
|
|
||||||
import MoodIcon from '@/material-icons/400-20px/mood.svg?react';
|
import MoodIcon from '@/material-icons/400-20px/mood.svg?react';
|
||||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
|
|
||||||
import { buildCustomEmojis, categoriesFromEmojis } from '../../emoji/emoji';
|
import { buildCustomEmojis, categoriesFromEmojis } from '../../emoji/emoji';
|
||||||
import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components';
|
import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components';
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import BarChart4BarsIcon from '@/material-icons/400-20px/bar_chart_4_bars.svg?react';
|
import BarChart4BarsIcon from '@/material-icons/400-20px/bar_chart_4_bars.svg?react';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
|
|
||||||
import { IconButton } from '../../../components/icon_button';
|
import { IconButton } from '../../../components/icon_button';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
@@ -10,6 +10,8 @@ import PhotoLibraryIcon from '@/material-icons/400-20px/photo_library.svg?react'
|
|||||||
import BrushIcon from '@/material-icons/400-24px/brush.svg?react';
|
import BrushIcon from '@/material-icons/400-24px/brush.svg?react';
|
||||||
import UploadFileIcon from '@/material-icons/400-24px/upload_file.svg?react';
|
import UploadFileIcon from '@/material-icons/400-24px/upload_file.svg?react';
|
||||||
|
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
|
|
||||||
import { DropdownIconButton } from './dropdown_icon_button';
|
import { DropdownIconButton } from './dropdown_icon_button';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|||||||
@@ -30,17 +30,19 @@ export const UploadProgress: React.FC<UploadProgressProps> = ({
|
|||||||
<Icon id='upload' icon={UploadFileIcon} />
|
<Icon id='upload' icon={UploadFileIcon} />
|
||||||
|
|
||||||
<div className='upload-progress__message'>
|
<div className='upload-progress__message'>
|
||||||
{isProcessing ? (
|
<span>
|
||||||
<FormattedMessage
|
{isProcessing ? (
|
||||||
id='upload_progress.processing'
|
<FormattedMessage
|
||||||
defaultMessage='Processing…'
|
id='upload_progress.processing'
|
||||||
/>
|
defaultMessage='Processing…'
|
||||||
) : (
|
/>
|
||||||
<FormattedMessage
|
) : (
|
||||||
id='upload_progress.label'
|
<FormattedMessage
|
||||||
defaultMessage='Uploading…'
|
id='upload_progress.label'
|
||||||
/>
|
defaultMessage='Uploading…'
|
||||||
)}
|
/>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
|
||||||
<div className='upload-progress__backdrop'>
|
<div className='upload-progress__backdrop'>
|
||||||
<animated.div className='upload-progress__tracker' style={styles} />
|
<animated.div className='upload-progress__tracker' style={styles} />
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { injectIntl, defineMessages } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import WarningIcon from '@/material-icons/400-20px/warning.svg?react';
|
import WarningIcon from '@/material-icons/400-20px/warning.svg?react';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||||
|
|
||||||
import { changeComposeSpoilerness } from '../../../actions/compose';
|
import { changeComposeSpoilerness } from '../../../actions/compose';
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import SettingToggle from 'flavours/glitch/features/notifications/components/setting_toggle';
|
import SettingToggle from 'flavours/glitch/features/notifications/components/setting_toggle';
|
||||||
|
|
||||||
import SettingText from '../../../components/setting_text';
|
import SettingText from '../../../components/setting_text';
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class Links extends PureComponent {
|
|||||||
return (
|
return (
|
||||||
<div className='explore__links scrollable scrollable--flex'>
|
<div className='explore__links scrollable scrollable--flex'>
|
||||||
<div className='empty-column-indicator'>
|
<div className='empty-column-indicator'>
|
||||||
<FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' />
|
<FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' tagName='span' />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class Suggestions extends PureComponent {
|
|||||||
return (
|
return (
|
||||||
<div className='explore__suggestions scrollable scrollable--flex'>
|
<div className='explore__suggestions scrollable scrollable--flex'>
|
||||||
<div className='empty-column-indicator'>
|
<div className='empty-column-indicator'>
|
||||||
<FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' />
|
<FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' tagName='span' />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class Tags extends PureComponent {
|
|||||||
return (
|
return (
|
||||||
<div className='explore__links scrollable scrollable--flex'>
|
<div className='explore__links scrollable scrollable--flex'>
|
||||||
<div className='empty-column-indicator'>
|
<div className='empty-column-indicator'>
|
||||||
<FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' />
|
<FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' tagName='span' />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
@@ -16,6 +16,7 @@ import { fetchFavourites, expandFavourites } from 'flavours/glitch/actions/inter
|
|||||||
import { Account } from 'flavours/glitch/components/account';
|
import { Account } from 'flavours/glitch/components/account';
|
||||||
import ColumnHeader from 'flavours/glitch/components/column_header';
|
import ColumnHeader from 'flavours/glitch/components/column_header';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
|
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
|
||||||
import ScrollableList from 'flavours/glitch/components/scrollable_list';
|
import ScrollableList from 'flavours/glitch/components/scrollable_list';
|
||||||
import Column from 'flavours/glitch/features/ui/components/column';
|
import Column from 'flavours/glitch/features/ui/components/column';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
@@ -9,6 +9,7 @@ import fuzzysort from 'fuzzysort';
|
|||||||
|
|
||||||
import AddIcon from '@/material-icons/400-24px/add.svg?react';
|
import AddIcon from '@/material-icons/400-24px/add.svg?react';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import { toServerSideType } from 'flavours/glitch/utils/filters';
|
import { toServerSideType } from 'flavours/glitch/utils/filters';
|
||||||
import { loupeIcon, deleteIcon } from 'flavours/glitch/utils/icons';
|
import { loupeIcon, deleteIcon } from 'flavours/glitch/utils/icons';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
@@ -11,6 +11,7 @@ import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
|||||||
import { Avatar } from '@/flavours/glitch/components/avatar';
|
import { Avatar } from '@/flavours/glitch/components/avatar';
|
||||||
import { DisplayName } from '@/flavours/glitch/components/display_name';
|
import { DisplayName } from '@/flavours/glitch/components/display_name';
|
||||||
import { IconButton } from '@/flavours/glitch/components/icon_button';
|
import { IconButton } from '@/flavours/glitch/components/icon_button';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import { EmojiHTML } from '@/flavours/glitch/components/emoji/html';
|
import { EmojiHTML } from '@/flavours/glitch/components/emoji/html';
|
||||||
import { Permalink } from '@/flavours/glitch/components/permalink';
|
import { Permalink } from '@/flavours/glitch/components/permalink';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ import { connect } from 'react-redux';
|
|||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
|
|
||||||
import PersonAddIcon from '@/material-icons/400-24px/person_add.svg?react';
|
import PersonAddIcon from '@/material-icons/400-24px/person_add.svg?react';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
|
|
||||||
import { fetchFollowRequests, expandFollowRequests } from '../../actions/accounts';
|
import { fetchFollowRequests, expandFollowRequests } from '../../actions/accounts';
|
||||||
import ScrollableList from '../../components/scrollable_list';
|
import ScrollableList from '../../components/scrollable_list';
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ const Followers: FC = () => {
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='followers.hide_other_followers'
|
id='followers.hide_other_followers'
|
||||||
defaultMessage='This user has chosen to not make their other followers visible'
|
defaultMessage='This user has chosen to not make their other followers visible'
|
||||||
|
tagName='span'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ const Followers: FC = () => {
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='following.hide_other_following'
|
id='following.hide_other_following'
|
||||||
defaultMessage='This user has chosen to not make the rest of who they follow visible'
|
defaultMessage='This user has chosen to not make the rest of who they follow visible'
|
||||||
|
tagName='span'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
@@ -11,6 +11,8 @@ import Toggle from 'react-toggle';
|
|||||||
|
|
||||||
import { maxFeedHashtags } from 'flavours/glitch/initial_state';
|
import { maxFeedHashtags } from 'flavours/glitch/initial_state';
|
||||||
|
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
|
|
||||||
import SettingToggle from '../../notifications/components/setting_toggle';
|
import SettingToggle from '../../notifications/components/setting_toggle';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ const Source: React.FC<{ id: ApiSuggestionSourceJSON }> = ({ id }) => {
|
|||||||
title={hint}
|
title={hint}
|
||||||
>
|
>
|
||||||
<Icon id='' icon={InfoIcon} />
|
<Icon id='' icon={InfoIcon} />
|
||||||
{label}
|
<span>{label}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
@@ -10,6 +10,7 @@ import { connect } from 'react-redux';
|
|||||||
|
|
||||||
import CampaignIcon from '@/material-icons/400-24px/campaign.svg?react';
|
import CampaignIcon from '@/material-icons/400-24px/campaign.svg?react';
|
||||||
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import { SymbolLogo } from 'flavours/glitch/components/logo';
|
import { SymbolLogo } from 'flavours/glitch/components/logo';
|
||||||
import { fetchAnnouncements, toggleShowAnnouncements } from 'flavours/glitch/actions/announcements';
|
import { fetchAnnouncements, toggleShowAnnouncements } from 'flavours/glitch/actions/announcements';
|
||||||
import { IconWithBadge } from 'flavours/glitch/components/icon_with_badge';
|
import { IconWithBadge } from 'flavours/glitch/components/icon_with_badge';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
@@ -9,6 +9,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||||||
import InfoIcon from '@/material-icons/400-24px/info.svg?react';
|
import InfoIcon from '@/material-icons/400-24px/info.svg?react';
|
||||||
import Column from 'flavours/glitch/components/column';
|
import Column from 'flavours/glitch/components/column';
|
||||||
import ColumnHeader from 'flavours/glitch/components/column_header';
|
import ColumnHeader from 'flavours/glitch/components/column_header';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
heading: { id: 'keyboard_shortcuts.heading', defaultMessage: 'Keyboard Shortcuts' },
|
heading: { id: 'keyboard_shortcuts.heading', defaultMessage: 'Keyboard Shortcuts' },
|
||||||
|
|||||||
@@ -285,6 +285,7 @@ const ListMembers: React.FC<{
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='lists.no_results_found'
|
id='lists.no_results_found'
|
||||||
defaultMessage='No results found.'
|
defaultMessage='No results found.'
|
||||||
|
tagName='span'
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { injectIntl, defineMessages } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||||
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
|
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
|
||||||
@@ -10,6 +10,7 @@ import ImageIcon from '@/material-icons/400-24px/image.svg?react';
|
|||||||
import ManufacturingIcon from '@/material-icons/400-24px/manufacturing.svg?react';
|
import ManufacturingIcon from '@/material-icons/400-24px/manufacturing.svg?react';
|
||||||
import SettingsIcon from '@/material-icons/400-24px/settings-fill.svg?react';
|
import SettingsIcon from '@/material-icons/400-24px/settings-fill.svg?react';
|
||||||
import WarningIcon from '@/material-icons/400-24px/warning.svg?react';
|
import WarningIcon from '@/material-icons/400-24px/warning.svg?react';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import { preferencesLink } from 'flavours/glitch/utils/backend_links';
|
import { preferencesLink } from 'flavours/glitch/utils/backend_links';
|
||||||
|
|
||||||
import LocalSettingsNavigationItem from './item';
|
import LocalSettingsNavigationItem from './item';
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
|
|
||||||
// Our imports
|
// Our imports
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import { expandSpoilers } from 'flavours/glitch/initial_state';
|
import { expandSpoilers } from 'flavours/glitch/initial_state';
|
||||||
import { preferenceLink } from 'flavours/glitch/utils/backend_links';
|
import { preferenceLink } from 'flavours/glitch/utils/backend_links';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ import { debounce } from 'lodash';
|
|||||||
|
|
||||||
import VolumeOffIcon from '@/material-icons/400-24px/volume_off.svg?react';
|
import VolumeOffIcon from '@/material-icons/400-24px/volume_off.svg?react';
|
||||||
import { Account } from 'flavours/glitch/components/account';
|
import { Account } from 'flavours/glitch/components/account';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
|
|
||||||
import { fetchMutes, expandMutes } from '../../actions/mutes';
|
import { fetchMutes, expandMutes } from '../../actions/mutes';
|
||||||
import { LoadingIndicator } from '../../components/loading_indicator';
|
import { LoadingIndicator } from '../../components/loading_indicator';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
@@ -10,6 +10,7 @@ import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
|||||||
import { Avatar } from 'flavours/glitch/components/avatar';
|
import { Avatar } from 'flavours/glitch/components/avatar';
|
||||||
import { DisplayName } from 'flavours/glitch/components/display_name';
|
import { DisplayName } from 'flavours/glitch/components/display_name';
|
||||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import { Permalink } from 'flavours/glitch/components/permalink';
|
import { Permalink } from 'flavours/glitch/components/permalink';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { injectIntl, FormattedMessage, defineMessages } from 'react-intl';
|
import { FormattedMessage, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
@@ -14,6 +14,7 @@ import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react';
|
|||||||
import { Account } from 'flavours/glitch/components/account';
|
import { Account } from 'flavours/glitch/components/account';
|
||||||
import { LinkedDisplayName } from '@/flavours/glitch/components/display_name';
|
import { LinkedDisplayName } from '@/flavours/glitch/components/display_name';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import { Hotkeys } from 'flavours/glitch/components/hotkeys';
|
import { Hotkeys } from 'flavours/glitch/components/hotkeys';
|
||||||
import { StatusQuoteManager } from 'flavours/glitch/components/status_quoted';
|
import { StatusQuoteManager } from 'flavours/glitch/components/status_quoted';
|
||||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import { AvatarOverlay } from 'flavours/glitch/components/avatar_overlay';
|
import { AvatarOverlay } from 'flavours/glitch/components/avatar_overlay';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp';
|
import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp';
|
||||||
|
|
||||||
// This needs to be kept in sync with app/models/report.rb
|
// This needs to be kept in sync with app/models/report.rb
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { openModal } from 'flavours/glitch/actions/modal';
|
import { openModal } from 'flavours/glitch/actions/modal';
|
||||||
import { fetchNotifications , setNotificationsFilter } from 'flavours/glitch/actions/notification_groups';
|
import { fetchNotifications , setNotificationsFilter } from 'flavours/glitch/actions/notification_groups';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
|
|
||||||
import { showAlert } from '../../../actions/alerts';
|
import { showAlert } from '../../../actions/alerts';
|
||||||
import { requestBrowserPermission } from '../../../actions/notifications';
|
import { requestBrowserPermission } from '../../../actions/notifications';
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ export const NotificationGroupWithStatus: React.FC<{
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='notification-group__main__header__label'>
|
<div className='notification-group__main__header__label'>
|
||||||
{label}
|
<span>{label}</span>
|
||||||
{timestamp && (
|
{timestamp && (
|
||||||
<>
|
<>
|
||||||
<span className='notification-group__main__header__label-separator'>
|
<span className='notification-group__main__header__label-separator'>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
@@ -9,6 +9,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import PushPinIcon from '@/material-icons/400-24px/push_pin.svg?react';
|
import PushPinIcon from '@/material-icons/400-24px/push_pin.svg?react';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import { getStatusList } from 'flavours/glitch/selectors';
|
import { getStatusList } from 'flavours/glitch/selectors';
|
||||||
|
|
||||||
import { fetchPinnedStatuses } from '../../actions/pin_statuses';
|
import { fetchPinnedStatuses } from '../../actions/pin_statuses';
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
|
|
||||||
import SettingText from '../../../components/setting_text';
|
import SettingText from '../../../components/setting_text';
|
||||||
import SettingToggle from '../../notifications/components/setting_toggle';
|
import SettingToggle from '../../notifications/components/setting_toggle';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
@@ -9,6 +9,7 @@ import { connect } from 'react-redux';
|
|||||||
|
|
||||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||||
import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner';
|
import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
|
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
|
||||||
import { domain, localLiveFeedAccess, remoteLiveFeedAccess } from 'flavours/glitch/initial_state';
|
import { domain, localLiveFeedAccess, remoteLiveFeedAccess } from 'flavours/glitch/initial_state';
|
||||||
import { canViewFeed } from 'flavours/glitch/permissions';
|
import { canViewFeed } from 'flavours/glitch/permissions';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@ import RefreshIcon from '@/material-icons/400-24px/refresh.svg?react';
|
|||||||
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
|
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
|
||||||
import { Account } from 'flavours/glitch/components/account';
|
import { Account } from 'flavours/glitch/components/account';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
|
|
||||||
import { fetchReblogs, expandReblogs } from '../../actions/interactions';
|
import { fetchReblogs, expandReblogs } from '../../actions/interactions';
|
||||||
import ColumnHeader from '../../components/column_header';
|
import ColumnHeader from '../../components/column_header';
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { List as ImmutableList } from 'immutable';
|
import { List as ImmutableList } from 'immutable';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { Button } from 'flavours/glitch/components/button';
|
import { Button } from 'flavours/glitch/components/button';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
|
|
||||||
import Option from './components/option';
|
import Option from './components/option';
|
||||||
|
|
||||||
|
|||||||
@@ -194,6 +194,7 @@ const Comment: React.FC<Props> = ({
|
|||||||
id='report.forward'
|
id='report.forward'
|
||||||
defaultMessage='Forward to {target}'
|
defaultMessage='Forward to {target}'
|
||||||
values={{ target: domain }}
|
values={{ target: domain }}
|
||||||
|
tagName='span'
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
@@ -13,6 +13,7 @@ import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
|
|||||||
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
|
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
|
||||||
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
||||||
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
|
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
|
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
|
||||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
|
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
|
||||||
import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
|
import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
@@ -16,6 +16,7 @@ import VisibilityIcon from '@/material-icons/400-24px/visibility.svg?react';
|
|||||||
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
|
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
|
||||||
import { Hotkeys } from 'flavours/glitch/components/hotkeys';
|
import { Hotkeys } from 'flavours/glitch/components/hotkeys';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
|
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
|
||||||
import { ScrollContainer } from 'flavours/glitch/containers/scroll_container';
|
import { ScrollContainer } from 'flavours/glitch/containers/scroll_container';
|
||||||
import BundleColumnError from 'flavours/glitch/features/ui/components/bundle_column_error';
|
import BundleColumnError from 'flavours/glitch/features/ui/components/bundle_column_error';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { is, List as ImmutableList, Set as ImmutableSet } from 'immutable';
|
import { is, List as ImmutableList, Set as ImmutableSet } from 'immutable';
|
||||||
@@ -12,6 +12,7 @@ import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
|||||||
import { followAccount } from 'flavours/glitch/actions/accounts';
|
import { followAccount } from 'flavours/glitch/actions/accounts';
|
||||||
import { Button } from 'flavours/glitch/components/button';
|
import { Button } from 'flavours/glitch/components/button';
|
||||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import Option from 'flavours/glitch/features/report/components/option';
|
import Option from 'flavours/glitch/features/report/components/option';
|
||||||
import { languages as preloadedLanguages } from 'flavours/glitch/initial_state';
|
import { languages as preloadedLanguages } from 'flavours/glitch/initial_state';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { injectIntl, FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
@@ -9,6 +9,7 @@ import { Link } from 'react-router-dom';
|
|||||||
|
|
||||||
import { Button } from 'flavours/glitch/components/button';
|
import { Button } from 'flavours/glitch/components/button';
|
||||||
import Column from 'flavours/glitch/components/column';
|
import Column from 'flavours/glitch/components/column';
|
||||||
|
import { injectIntl } from '@/flavours/glitch/components/intl';
|
||||||
import { GIF } from 'flavours/glitch/components/gif';
|
import { GIF } from 'flavours/glitch/components/gif';
|
||||||
|
|
||||||
class CopyButton extends PureComponent {
|
class CopyButton extends PureComponent {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user