commit
eae50b2cb3
323 changed files with 5604 additions and 1568 deletions
.env.testidentity_context.tsxinitial_state.js
.github
.nvmrc.rubocop.yml.rubocop_todo.yml.simplecovGemfileGemfile.lockREADME.mdapp
controllers
accounts_controller.rbapplication_controller.rb
activitypub
admin
api/v1
accounts
accounts_controller.rbadmin
apps
apps_controller.rbbookmarks_controller.rbfavourites_controller.rbnotifications
notifications_controller.rbpush
statuses_controller.rbtimelines
trends
concerns
settings
tags_controller.rbwell_known
helpers
javascript
entrypoints
mastodon
actions
components
containers
features
account/components
community_timeline
compose/components
explore
firehose
getting_started
hashtag_timeline
home_timeline
notifications
picture_in_picture/components
public_timeline
status
ui
locales
|
@ -4,7 +4,8 @@ NODE_ENV=production
|
|||
LOCAL_DOMAIN=cb6e6126.ngrok.io
|
||||
LOCAL_HTTPS=true
|
||||
|
||||
# Required by ActiveRecord encryption feature
|
||||
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=fkSxKD2bF396kdQbrP1EJ7WbU7ZgNokR
|
||||
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=r0hvVmzBVsjxC7AMlwhOzmtc36ZCOS1E
|
||||
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=PhdFyyfy5xJ7WVd2lWBpcPScRQHzRTNr
|
||||
# Secret values required by ActiveRecord encryption feature
|
||||
# Use `bin/rails db:encryption:init` to generate fresh secrets
|
||||
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=test_determinist_key_DO_NOT_USE_IN_PRODUCTION
|
||||
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=test_salt_DO_NOT_USE_IN_PRODUCTION
|
||||
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=test_primary_key_DO_NOT_USE_IN_PRODUCTION
|
||||
|
|
7
.github/renovate.json5
vendored
7
.github/renovate.json5
vendored
|
@ -141,6 +141,13 @@
|
|||
matchUpdateTypes: ['patch', 'minor'],
|
||||
groupName: 'RSpec (non-major)',
|
||||
},
|
||||
{
|
||||
// Group all opentelemetry-ruby packages in the same PR
|
||||
matchManagers: ['bundler'],
|
||||
matchPackagePrefixes: ['opentelemetry-'],
|
||||
matchUpdateTypes: ['patch', 'minor'],
|
||||
groupName: 'opentelemetry-ruby (non-major)',
|
||||
},
|
||||
// Add labels depending on package manager
|
||||
{ matchManagers: ['npm', 'nvm'], addLabels: ['javascript'] },
|
||||
{ matchManagers: ['bundler', 'ruby-version'], addLabels: ['ruby'] },
|
||||
|
|
21
.github/stylelint-matcher.json
vendored
21
.github/stylelint-matcher.json
vendored
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "stylelint",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^([^\\s].*)$",
|
||||
"file": 1
|
||||
},
|
||||
{
|
||||
"regexp": "^\\s+((\\d+):(\\d+))?\\s+(✖|×)\\s+(.*)\\s{2,}(.*)$",
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"message": 5,
|
||||
"code": 6,
|
||||
"loop": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
6
.github/workflows/lint-css.yml
vendored
6
.github/workflows/lint-css.yml
vendored
|
@ -38,9 +38,5 @@ jobs:
|
|||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
||||
- uses: xt0rted/stylelint-problem-matcher@v1
|
||||
|
||||
- run: echo "::add-matcher::.github/stylelint-matcher.json"
|
||||
|
||||
- name: Stylelint
|
||||
run: yarn lint:css
|
||||
run: yarn lint:css -f github
|
||||
|
|
26
.github/workflows/test-ruby.yml
vendored
26
.github/workflows/test-ruby.yml
vendored
|
@ -145,6 +145,8 @@ jobs:
|
|||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: coverage/lcov/mastodon.lcov
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
test-e2e:
|
||||
name: End to End testing
|
||||
|
@ -184,6 +186,8 @@ jobs:
|
|||
DISABLE_SIMPLECOV: true
|
||||
RAILS_ENV: test
|
||||
BUNDLE_WITH: test
|
||||
LOCAL_DOMAIN: localhost:3000
|
||||
LOCAL_HTTPS: false
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
@ -213,7 +217,7 @@ jobs:
|
|||
- name: Load database schema
|
||||
run: './bin/rails db:create db:schema:load db:seed'
|
||||
|
||||
- run: bundle exec rake spec:system
|
||||
- run: bin/rspec spec/system --tag streaming --tag js
|
||||
|
||||
- name: Archive logs
|
||||
uses: actions/upload-artifact@v4
|
||||
|
@ -260,8 +264,8 @@ jobs:
|
|||
ports:
|
||||
- 6379:6379
|
||||
|
||||
search:
|
||||
image: ${{ matrix.search-image }}
|
||||
elasticsearch:
|
||||
image: ${{ contains(matrix.search-image, 'elasticsearch') && matrix.search-image || '' }}
|
||||
env:
|
||||
discovery.type: single-node
|
||||
xpack.security.enabled: false
|
||||
|
@ -273,6 +277,20 @@ jobs:
|
|||
ports:
|
||||
- 9200:9200
|
||||
|
||||
opensearch:
|
||||
image: ${{ contains(matrix.search-image, 'opensearch') && matrix.search-image || '' }}
|
||||
env:
|
||||
discovery.type: single-node
|
||||
DISABLE_INSTALL_DEMO_CONFIG: true
|
||||
DISABLE_SECURITY_PLUGIN: true
|
||||
options: >-
|
||||
--health-cmd "curl http://localhost:9200/_cluster/health"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 10
|
||||
ports:
|
||||
- 9200:9200
|
||||
|
||||
env:
|
||||
DB_HOST: localhost
|
||||
DB_USER: postgres
|
||||
|
@ -296,6 +314,8 @@ jobs:
|
|||
include:
|
||||
- ruby-version: '.ruby-version'
|
||||
search-image: docker.elastic.co/elasticsearch/elasticsearch:8.10.2
|
||||
- ruby-version: '.ruby-version'
|
||||
search-image: opensearchproject/opensearch:2
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
2
.nvmrc
2
.nvmrc
|
@ -1 +1 @@
|
|||
20.12
|
||||
20.13
|
||||
|
|
|
@ -211,6 +211,11 @@ Style/PercentLiteralDelimiters:
|
|||
Style/RedundantBegin:
|
||||
Enabled: false
|
||||
|
||||
# Reason: Prevailing style choice
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#styleredundantfetchblock
|
||||
Style/RedundantFetchBlock:
|
||||
Enabled: false
|
||||
|
||||
# Reason: Overridden to reduce implicit StandardError rescues
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#stylerescuestandarderror
|
||||
Style/RescueStandardError:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
|
||||
# using RuboCop version 1.62.1.
|
||||
# using RuboCop version 1.63.5.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
|
@ -42,14 +42,6 @@ RSpec/MultipleMemoizedHelpers:
|
|||
RSpec/NestedGroups:
|
||||
Max: 6
|
||||
|
||||
# Configuration parameters: Include.
|
||||
# Include: app/models/**/*.rb
|
||||
Rails/HasAndBelongsToMany:
|
||||
Exclude:
|
||||
- 'app/models/concerns/account/associations.rb'
|
||||
- 'app/models/status.rb'
|
||||
- 'app/models/tag.rb'
|
||||
|
||||
Rails/OutputSafety:
|
||||
Exclude:
|
||||
- 'config/initializers/simple_form.rb'
|
||||
|
@ -62,10 +54,6 @@ Style/ClassEqualityComparison:
|
|||
- 'app/helpers/jsonld_helper.rb'
|
||||
- 'app/serializers/activitypub/outbox_serializer.rb'
|
||||
|
||||
Style/ClassVars:
|
||||
Exclude:
|
||||
- 'config/initializers/devise.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: AllowedVars.
|
||||
Style/FetchEnvVar:
|
||||
|
@ -82,7 +70,7 @@ Style/FetchEnvVar:
|
|||
- 'config/initializers/vapid.rb'
|
||||
- 'lib/mastodon/redis_config.rb'
|
||||
- 'lib/tasks/repo.rake'
|
||||
- 'spec/features/profile_spec.rb'
|
||||
- 'spec/system/profile_spec.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: EnforcedStyle, MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns.
|
||||
|
@ -134,13 +122,6 @@ Style/HashTransformValues:
|
|||
- 'app/serializers/rest/web_push_subscription_serializer.rb'
|
||||
- 'app/services/import_service.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
Style/IfUnlessModifier:
|
||||
Exclude:
|
||||
- 'config/environments/production.rb'
|
||||
- 'config/initializers/devise.rb'
|
||||
- 'config/initializers/ffmpeg.rb'
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
Style/MapToHash:
|
||||
Exclude:
|
||||
|
@ -188,16 +169,6 @@ Style/RedundantConstantBase:
|
|||
- 'config/environments/production.rb'
|
||||
- 'config/initializers/sidekiq.rb'
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: SafeForConstants.
|
||||
Style/RedundantFetchBlock:
|
||||
Exclude:
|
||||
- 'config/initializers/1_hosts.rb'
|
||||
- 'config/initializers/chewy.rb'
|
||||
- 'config/initializers/devise.rb'
|
||||
- 'config/initializers/paperclip.rb'
|
||||
- 'config/puma.rb'
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
|
||||
# AllowedMethods: present?, blank?, presence, try, try!
|
||||
|
|
22
.simplecov
22
.simplecov
|
@ -1,22 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
if ENV['CI']
|
||||
require 'simplecov-lcov'
|
||||
SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
|
||||
SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter
|
||||
else
|
||||
SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
|
||||
end
|
||||
|
||||
SimpleCov.start 'rails' do
|
||||
enable_coverage :branch
|
||||
|
||||
add_filter 'lib/linter'
|
||||
|
||||
add_group 'Libraries', 'lib'
|
||||
add_group 'Policies', 'app/policies'
|
||||
add_group 'Presenters', 'app/presenters'
|
||||
add_group 'Serializers', 'app/serializers'
|
||||
add_group 'Services', 'app/services'
|
||||
add_group 'Validators', 'app/validators'
|
||||
end
|
26
Gemfile
26
Gemfile
|
@ -31,7 +31,7 @@ gem 'browser'
|
|||
gem 'charlock_holmes', '~> 0.7.7'
|
||||
gem 'chewy', '~> 7.3'
|
||||
gem 'devise', '~> 4.9'
|
||||
gem 'devise-two-factor', '~> 4.1'
|
||||
gem 'devise-two-factor'
|
||||
|
||||
group :pam_authentication, optional: true do
|
||||
gem 'devise_pam_authenticatable2', '~> 9.2'
|
||||
|
@ -57,7 +57,7 @@ gem 'htmlentities', '~> 4.3'
|
|||
gem 'http', '~> 5.2.0'
|
||||
gem 'http_accept_language', '~> 2.1'
|
||||
gem 'httplog', '~> 1.6.2'
|
||||
gem 'i18n', '1.14.1' # TODO: Remove version when resolved: https://github.com/glebm/i18n-tasks/issues/552 / https://github.com/ruby-i18n/i18n/pull/688
|
||||
gem 'i18n'
|
||||
gem 'idn-ruby', require: 'idn'
|
||||
gem 'inline_svg'
|
||||
gem 'kaminari', '~> 1.2'
|
||||
|
@ -103,6 +103,24 @@ gem 'rdf-normalize', '~> 0.5'
|
|||
|
||||
gem 'private_address_check', '~> 0.5'
|
||||
|
||||
group :opentelemetry do
|
||||
gem 'opentelemetry-exporter-otlp', '~> 0.26.3', require: false
|
||||
gem 'opentelemetry-instrumentation-active_job', '~> 0.7.1', require: false
|
||||
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.20.1', require: false
|
||||
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.21.2', require: false
|
||||
gem 'opentelemetry-instrumentation-excon', '~> 0.22.0', require: false
|
||||
gem 'opentelemetry-instrumentation-faraday', '~> 0.24.1', require: false
|
||||
gem 'opentelemetry-instrumentation-http', '~> 0.23.2', require: false
|
||||
gem 'opentelemetry-instrumentation-http_client', '~> 0.22.3', require: false
|
||||
gem 'opentelemetry-instrumentation-net_http', '~> 0.22.4', require: false
|
||||
gem 'opentelemetry-instrumentation-pg', '~> 0.27.1', require: false
|
||||
gem 'opentelemetry-instrumentation-rack', '~> 0.24.1', require: false
|
||||
gem 'opentelemetry-instrumentation-rails', '~> 0.30.0', require: false
|
||||
gem 'opentelemetry-instrumentation-redis', '~> 0.25.3', require: false
|
||||
gem 'opentelemetry-instrumentation-sidekiq', '~> 0.25.2', require: false
|
||||
gem 'opentelemetry-sdk', '~> 1.4', require: false
|
||||
end
|
||||
|
||||
group :test do
|
||||
# Adds RSpec Error/Warning annotations to GitHub PRs on the Files tab
|
||||
gem 'rspec-github', '~> 2.4', require: false
|
||||
|
@ -114,7 +132,7 @@ group :test do
|
|||
gem 'email_spec'
|
||||
|
||||
# Extra RSpec extension methods and helpers for sidekiq
|
||||
gem 'rspec-sidekiq', '~> 4.0'
|
||||
gem 'rspec-sidekiq', '~> 5.0'
|
||||
|
||||
# Browser integration testing
|
||||
gem 'capybara', '~> 3.39'
|
||||
|
@ -160,7 +178,7 @@ group :development do
|
|||
|
||||
# Preview mail in the browser
|
||||
gem 'letter_opener', '~> 1.8'
|
||||
gem 'letter_opener_web', '~> 2.0'
|
||||
gem 'letter_opener_web', '~> 3.0'
|
||||
|
||||
# Security analysis CLI tools
|
||||
gem 'brakeman', '~> 6.0', require: false
|
||||
|
|
325
Gemfile.lock
325
Gemfile.lock
|
@ -10,35 +10,35 @@ GIT
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (7.1.3.2)
|
||||
actionpack (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
actioncable (7.1.3.3)
|
||||
actionpack (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
zeitwerk (~> 2.6)
|
||||
actionmailbox (7.1.3.2)
|
||||
actionpack (= 7.1.3.2)
|
||||
activejob (= 7.1.3.2)
|
||||
activerecord (= 7.1.3.2)
|
||||
activestorage (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
actionmailbox (7.1.3.3)
|
||||
actionpack (= 7.1.3.3)
|
||||
activejob (= 7.1.3.3)
|
||||
activerecord (= 7.1.3.3)
|
||||
activestorage (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
mail (>= 2.7.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
actionmailer (7.1.3.2)
|
||||
actionpack (= 7.1.3.2)
|
||||
actionview (= 7.1.3.2)
|
||||
activejob (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
actionmailer (7.1.3.3)
|
||||
actionpack (= 7.1.3.3)
|
||||
actionview (= 7.1.3.3)
|
||||
activejob (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
rails-dom-testing (~> 2.2)
|
||||
actionpack (7.1.3.2)
|
||||
actionview (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
actionpack (7.1.3.3)
|
||||
actionview (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
nokogiri (>= 1.8.5)
|
||||
racc
|
||||
rack (>= 2.2.4)
|
||||
|
@ -46,15 +46,15 @@ GEM
|
|||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
actiontext (7.1.3.2)
|
||||
actionpack (= 7.1.3.2)
|
||||
activerecord (= 7.1.3.2)
|
||||
activestorage (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
actiontext (7.1.3.3)
|
||||
actionpack (= 7.1.3.3)
|
||||
activerecord (= 7.1.3.3)
|
||||
activestorage (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
actionview (7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.11)
|
||||
rails-dom-testing (~> 2.2)
|
||||
|
@ -64,22 +64,22 @@ GEM
|
|||
activemodel (>= 4.1)
|
||||
case_transform (>= 0.2)
|
||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||
activejob (7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
activejob (7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
activerecord (7.1.3.2)
|
||||
activemodel (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
activemodel (7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
activerecord (7.1.3.3)
|
||||
activemodel (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
timeout (>= 0.4.0)
|
||||
activestorage (7.1.3.2)
|
||||
actionpack (= 7.1.3.2)
|
||||
activejob (= 7.1.3.2)
|
||||
activerecord (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
activestorage (7.1.3.3)
|
||||
actionpack (= 7.1.3.3)
|
||||
activejob (= 7.1.3.3)
|
||||
activerecord (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
marcel (~> 1.0)
|
||||
activesupport (7.1.3.2)
|
||||
activesupport (7.1.3.3)
|
||||
base64
|
||||
bigdecimal
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
|
@ -97,22 +97,20 @@ GEM
|
|||
activerecord (>= 3.2, < 8.0)
|
||||
rake (>= 10.4, < 14.0)
|
||||
ast (2.4.2)
|
||||
attr_encrypted (4.0.0)
|
||||
encryptor (~> 3.0.0)
|
||||
attr_required (1.0.2)
|
||||
awrence (1.2.1)
|
||||
aws-eventstream (1.3.0)
|
||||
aws-partitions (1.920.0)
|
||||
aws-sdk-core (3.193.0)
|
||||
aws-partitions (1.929.0)
|
||||
aws-sdk-core (3.196.1)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.8)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.80.0)
|
||||
aws-sdk-kms (1.81.0)
|
||||
aws-sdk-core (~> 3, >= 3.193.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.148.0)
|
||||
aws-sdk-core (~> 3, >= 3.193.0)
|
||||
aws-sdk-s3 (1.151.0)
|
||||
aws-sdk-core (~> 3, >= 3.194.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.8)
|
||||
aws-sigv4 (1.8.0)
|
||||
|
@ -132,14 +130,7 @@ GEM
|
|||
erubi (>= 1.0.0)
|
||||
rack (>= 0.9.0)
|
||||
rouge (>= 1.0.0)
|
||||
better_html (2.1.1)
|
||||
actionview (>= 6.0)
|
||||
activesupport (>= 6.0)
|
||||
ast (~> 2.0)
|
||||
erubi (~> 1.4)
|
||||
parser (>= 2.4)
|
||||
smart_properties
|
||||
bigdecimal (3.1.7)
|
||||
bigdecimal (3.1.8)
|
||||
bindata (2.5.0)
|
||||
binding_of_caller (1.0.1)
|
||||
debug_inspector (>= 1.2.0)
|
||||
|
@ -169,9 +160,9 @@ GEM
|
|||
activesupport
|
||||
cbor (0.5.9.8)
|
||||
charlock_holmes (0.7.7)
|
||||
chewy (7.5.1)
|
||||
chewy (7.6.0)
|
||||
activesupport (>= 5.2)
|
||||
elasticsearch (>= 7.12.0, < 7.14.0)
|
||||
elasticsearch (>= 7.14.0, < 8)
|
||||
elasticsearch-dsl
|
||||
chunky_png (1.4.0)
|
||||
climate_control (1.2.0)
|
||||
|
@ -204,9 +195,8 @@ GEM
|
|||
railties (>= 4.1.0)
|
||||
responders
|
||||
warden (~> 1.2.3)
|
||||
devise-two-factor (4.1.1)
|
||||
devise-two-factor (5.0.0)
|
||||
activesupport (~> 7.0)
|
||||
attr_encrypted (>= 1.3, < 5, != 2)
|
||||
devise (~> 4.0)
|
||||
railties (~> 7.0)
|
||||
rotp (~> 6.0)
|
||||
|
@ -220,23 +210,22 @@ GEM
|
|||
domain_name (0.6.20240107)
|
||||
doorkeeper (5.6.9)
|
||||
railties (>= 5)
|
||||
dotenv (3.1.0)
|
||||
dotenv (3.1.2)
|
||||
drb (2.2.1)
|
||||
ed25519 (1.3.0)
|
||||
elasticsearch (7.13.3)
|
||||
elasticsearch-api (= 7.13.3)
|
||||
elasticsearch-transport (= 7.13.3)
|
||||
elasticsearch-api (7.13.3)
|
||||
elasticsearch (7.17.10)
|
||||
elasticsearch-api (= 7.17.10)
|
||||
elasticsearch-transport (= 7.17.10)
|
||||
elasticsearch-api (7.17.10)
|
||||
multi_json
|
||||
elasticsearch-dsl (0.1.10)
|
||||
elasticsearch-transport (7.13.3)
|
||||
faraday (~> 1)
|
||||
elasticsearch-transport (7.17.10)
|
||||
faraday (>= 1, < 3)
|
||||
multi_json
|
||||
email_spec (2.2.2)
|
||||
htmlentities (~> 4.3.3)
|
||||
launchy (~> 2.1)
|
||||
mail (~> 2.7)
|
||||
encryptor (3.0.0)
|
||||
erubi (1.12.0)
|
||||
et-orbi (1.2.11)
|
||||
tzinfo
|
||||
|
@ -283,7 +272,7 @@ GEM
|
|||
fog-json (1.2.0)
|
||||
fog-core
|
||||
multi_json (~> 1.10)
|
||||
fog-openstack (1.1.0)
|
||||
fog-openstack (1.1.1)
|
||||
fog-core (~> 2.1)
|
||||
fog-json (>= 1.0)
|
||||
formatador (1.1.0)
|
||||
|
@ -295,6 +284,9 @@ GEM
|
|||
ruby-progressbar (~> 1.4)
|
||||
globalid (1.2.1)
|
||||
activesupport (>= 6.1)
|
||||
google-protobuf (3.25.3)
|
||||
googleapis-common-protos-types (1.14.0)
|
||||
google-protobuf (~> 3.18)
|
||||
haml (6.3.0)
|
||||
temple (>= 0.8.2)
|
||||
thor
|
||||
|
@ -332,12 +324,11 @@ GEM
|
|||
httplog (1.6.3)
|
||||
rack (>= 2.0)
|
||||
rainbow (>= 2.0.0)
|
||||
i18n (1.14.1)
|
||||
i18n (1.14.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-tasks (1.0.13)
|
||||
i18n-tasks (1.0.14)
|
||||
activesupport (>= 4.0.2)
|
||||
ast (>= 2.1.0)
|
||||
better_html (>= 1.0, < 3.0)
|
||||
erubi
|
||||
highline (>= 2.0.0)
|
||||
i18n
|
||||
|
@ -350,8 +341,8 @@ GEM
|
|||
activesupport (>= 3.0)
|
||||
nokogiri (>= 1.6)
|
||||
io-console (0.7.2)
|
||||
irb (1.12.0)
|
||||
rdoc
|
||||
irb (1.13.1)
|
||||
rdoc (>= 4.0.0)
|
||||
reline (>= 0.4.2)
|
||||
jmespath (1.6.2)
|
||||
json (2.7.2)
|
||||
|
@ -398,10 +389,10 @@ GEM
|
|||
addressable (~> 2.8)
|
||||
letter_opener (1.10.0)
|
||||
launchy (>= 2.2, < 4)
|
||||
letter_opener_web (2.0.0)
|
||||
actionmailer (>= 5.2)
|
||||
letter_opener (~> 1.7)
|
||||
railties (>= 5.2)
|
||||
letter_opener_web (3.0.0)
|
||||
actionmailer (>= 6.1)
|
||||
letter_opener (~> 1.9)
|
||||
railties (>= 6.1)
|
||||
rexml
|
||||
link_header (0.0.8)
|
||||
llhttp-ffi (0.5.0)
|
||||
|
@ -431,10 +422,10 @@ GEM
|
|||
memory_profiler (1.0.1)
|
||||
mime-types (3.5.2)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2024.0305)
|
||||
mime-types-data (3.2024.0507)
|
||||
mini_mime (1.1.5)
|
||||
mini_portile2 (2.8.6)
|
||||
minitest (5.22.3)
|
||||
minitest (5.23.0)
|
||||
msgpack (1.7.2)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.4.0)
|
||||
|
@ -443,7 +434,7 @@ GEM
|
|||
uri
|
||||
net-http-persistent (4.0.2)
|
||||
connection_pool (~> 2.2)
|
||||
net-imap (0.4.10)
|
||||
net-imap (0.4.11)
|
||||
date
|
||||
net-protocol
|
||||
net-ldap (0.19.0)
|
||||
|
@ -453,8 +444,8 @@ GEM
|
|||
timeout
|
||||
net-smtp (0.5.0)
|
||||
net-protocol
|
||||
nio4r (2.7.1)
|
||||
nokogiri (1.16.4)
|
||||
nio4r (2.7.3)
|
||||
nokogiri (1.16.5)
|
||||
mini_portile2 (~> 2.8.2)
|
||||
racc (~> 1.4)
|
||||
nsa (0.3.0)
|
||||
|
@ -495,6 +486,96 @@ GEM
|
|||
openssl (3.2.0)
|
||||
openssl-signature_algorithm (1.3.0)
|
||||
openssl (> 2.0)
|
||||
opentelemetry-api (1.2.5)
|
||||
opentelemetry-common (0.20.1)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-exporter-otlp (0.26.3)
|
||||
google-protobuf (~> 3.14)
|
||||
googleapis-common-protos-types (~> 1.3)
|
||||
opentelemetry-api (~> 1.1)
|
||||
opentelemetry-common (~> 0.20)
|
||||
opentelemetry-sdk (~> 1.2)
|
||||
opentelemetry-semantic_conventions
|
||||
opentelemetry-helpers-sql-obfuscation (0.1.0)
|
||||
opentelemetry-common (~> 0.20)
|
||||
opentelemetry-instrumentation-action_pack (0.9.0)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-instrumentation-rack (~> 0.21)
|
||||
opentelemetry-instrumentation-action_view (0.7.0)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-instrumentation-active_support (~> 0.1)
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-instrumentation-active_job (0.7.1)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-instrumentation-active_model_serializers (0.20.1)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-instrumentation-active_record (0.7.2)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-instrumentation-active_support (0.5.1)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-instrumentation-base (0.22.3)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-registry (~> 0.1)
|
||||
opentelemetry-instrumentation-concurrent_ruby (0.21.3)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-instrumentation-excon (0.22.1)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-common (~> 0.20.0)
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-instrumentation-faraday (0.24.2)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-common (~> 0.20.0)
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-instrumentation-http (0.23.3)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-instrumentation-http_client (0.22.4)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-common (~> 0.20.0)
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-instrumentation-net_http (0.22.4)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-common (~> 0.20.0)
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-instrumentation-pg (0.27.3)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-helpers-sql-obfuscation
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-instrumentation-rack (0.24.3)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-common (~> 0.20.0)
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-instrumentation-rails (0.30.1)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-instrumentation-action_pack (~> 0.9.0)
|
||||
opentelemetry-instrumentation-action_view (~> 0.7.0)
|
||||
opentelemetry-instrumentation-active_job (~> 0.7.0)
|
||||
opentelemetry-instrumentation-active_record (~> 0.7.0)
|
||||
opentelemetry-instrumentation-active_support (~> 0.5.0)
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-instrumentation-redis (0.25.4)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-common (~> 0.20.0)
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-instrumentation-sidekiq (0.25.3)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-common (~> 0.20.0)
|
||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||
opentelemetry-registry (0.3.1)
|
||||
opentelemetry-api (~> 1.1)
|
||||
opentelemetry-sdk (1.4.1)
|
||||
opentelemetry-api (~> 1.1)
|
||||
opentelemetry-common (~> 0.20)
|
||||
opentelemetry-registry (~> 0.2)
|
||||
opentelemetry-semantic_conventions
|
||||
opentelemetry-semantic_conventions (1.10.0)
|
||||
opentelemetry-api (~> 1.0)
|
||||
orm_adapter (0.5.0)
|
||||
ox (2.14.18)
|
||||
parallel (1.24.0)
|
||||
|
@ -526,10 +607,10 @@ GEM
|
|||
public_suffix (5.0.5)
|
||||
puma (6.4.2)
|
||||
nio4r (~> 2.0)
|
||||
pundit (2.3.1)
|
||||
pundit (2.3.2)
|
||||
activesupport (>= 3.0.0)
|
||||
raabro (1.4.0)
|
||||
racc (1.7.3)
|
||||
racc (1.8.0)
|
||||
rack (2.2.9)
|
||||
rack-attack (6.7.0)
|
||||
rack (>= 1.0, < 4)
|
||||
|
@ -553,20 +634,20 @@ GEM
|
|||
rackup (1.0.0)
|
||||
rack (< 3)
|
||||
webrick
|
||||
rails (7.1.3.2)
|
||||
actioncable (= 7.1.3.2)
|
||||
actionmailbox (= 7.1.3.2)
|
||||
actionmailer (= 7.1.3.2)
|
||||
actionpack (= 7.1.3.2)
|
||||
actiontext (= 7.1.3.2)
|
||||
actionview (= 7.1.3.2)
|
||||
activejob (= 7.1.3.2)
|
||||
activemodel (= 7.1.3.2)
|
||||
activerecord (= 7.1.3.2)
|
||||
activestorage (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
rails (7.1.3.3)
|
||||
actioncable (= 7.1.3.3)
|
||||
actionmailbox (= 7.1.3.3)
|
||||
actionmailer (= 7.1.3.3)
|
||||
actionpack (= 7.1.3.3)
|
||||
actiontext (= 7.1.3.3)
|
||||
actionview (= 7.1.3.3)
|
||||
activejob (= 7.1.3.3)
|
||||
activemodel (= 7.1.3.3)
|
||||
activerecord (= 7.1.3.3)
|
||||
activestorage (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 7.1.3.2)
|
||||
railties (= 7.1.3.3)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
actionview (>= 5.0.1.rc1)
|
||||
|
@ -581,9 +662,9 @@ GEM
|
|||
rails-i18n (7.0.9)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 6.0.0, < 8)
|
||||
railties (7.1.3.2)
|
||||
actionpack (= 7.1.3.2)
|
||||
activesupport (= 7.1.3.2)
|
||||
railties (7.1.3.3)
|
||||
actionpack (= 7.1.3.3)
|
||||
activesupport (= 7.1.3.3)
|
||||
irb
|
||||
rackup (>= 1.0.0)
|
||||
rake (>= 12.2)
|
||||
|
@ -604,15 +685,16 @@ GEM
|
|||
redis (>= 4)
|
||||
redlock (1.3.2)
|
||||
redis (>= 3.0.0, < 6.0)
|
||||
regexp_parser (2.9.0)
|
||||
reline (0.5.2)
|
||||
regexp_parser (2.9.2)
|
||||
reline (0.5.7)
|
||||
io-console (~> 0.5)
|
||||
request_store (1.6.0)
|
||||
rack (>= 1.4)
|
||||
responders (3.1.1)
|
||||
actionpack (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
rexml (3.2.6)
|
||||
rexml (3.2.8)
|
||||
strscan (>= 3.0.9)
|
||||
rotp (6.3.0)
|
||||
rouge (4.2.1)
|
||||
rpam2 (4.0.2)
|
||||
|
@ -627,7 +709,7 @@ GEM
|
|||
rspec-support (~> 3.13.0)
|
||||
rspec-github (2.4.0)
|
||||
rspec-core (~> 3.0)
|
||||
rspec-mocks (3.13.0)
|
||||
rspec-mocks (3.13.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.13.0)
|
||||
rspec-rails (6.1.2)
|
||||
|
@ -638,13 +720,13 @@ GEM
|
|||
rspec-expectations (~> 3.13)
|
||||
rspec-mocks (~> 3.13)
|
||||
rspec-support (~> 3.13)
|
||||
rspec-sidekiq (4.2.0)
|
||||
rspec-sidekiq (5.0.0)
|
||||
rspec-core (~> 3.0)
|
||||
rspec-expectations (~> 3.0)
|
||||
rspec-mocks (~> 3.0)
|
||||
sidekiq (>= 5, < 8)
|
||||
rspec-support (3.13.1)
|
||||
rubocop (1.63.4)
|
||||
rubocop (1.63.5)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (>= 3.17.0)
|
||||
parallel (~> 1.10)
|
||||
|
@ -655,8 +737,8 @@ GEM
|
|||
rubocop-ast (>= 1.31.1, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 2.4.0, < 3.0)
|
||||
rubocop-ast (1.31.2)
|
||||
parser (>= 3.3.0.4)
|
||||
rubocop-ast (1.31.3)
|
||||
parser (>= 3.3.1.0)
|
||||
rubocop-capybara (2.20.0)
|
||||
rubocop (~> 1.41)
|
||||
rubocop-factory_bot (2.25.1)
|
||||
|
@ -664,12 +746,12 @@ GEM
|
|||
rubocop-performance (1.21.0)
|
||||
rubocop (>= 1.48.1, < 2.0)
|
||||
rubocop-ast (>= 1.31.1, < 2.0)
|
||||
rubocop-rails (2.24.1)
|
||||
rubocop-rails (2.25.0)
|
||||
activesupport (>= 4.2.0)
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 1.33.0, < 2.0)
|
||||
rubocop-ast (>= 1.31.1, < 2.0)
|
||||
rubocop-rspec (2.29.1)
|
||||
rubocop-rspec (2.29.2)
|
||||
rubocop (~> 1.40)
|
||||
rubocop-capybara (~> 2.17)
|
||||
rubocop-factory_bot (~> 2.22)
|
||||
|
@ -693,7 +775,7 @@ GEM
|
|||
scenic (1.8.0)
|
||||
activerecord (>= 4.0.0)
|
||||
railties (>= 4.0.0)
|
||||
selenium-webdriver (4.20.1)
|
||||
selenium-webdriver (4.21.1)
|
||||
base64 (~> 0.2)
|
||||
rexml (~> 3.2, >= 3.2.5)
|
||||
rubyzip (>= 1.2.2, < 3.0)
|
||||
|
@ -727,7 +809,6 @@ GEM
|
|||
simplecov-html (0.12.3)
|
||||
simplecov-lcov (0.8.0)
|
||||
simplecov_json_formatter (0.1.4)
|
||||
smart_properties (1.17.0)
|
||||
stackprof (0.2.26)
|
||||
statsd-ruby (1.5.0)
|
||||
stoplight (4.1.0)
|
||||
|
@ -735,6 +816,7 @@ GEM
|
|||
stringio (3.1.0)
|
||||
strong_migrations (1.8.0)
|
||||
activerecord (>= 5.2)
|
||||
strscan (3.1.0)
|
||||
swd (1.3.0)
|
||||
activesupport (>= 3)
|
||||
attr_required (>= 0.0.5)
|
||||
|
@ -813,7 +895,7 @@ GEM
|
|||
xorcist (1.1.3)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.6.13)
|
||||
zeitwerk (2.6.14)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
@ -842,7 +924,7 @@ DEPENDENCIES
|
|||
database_cleaner-active_record
|
||||
debug (~> 1.8)
|
||||
devise (~> 4.9)
|
||||
devise-two-factor (~> 4.1)
|
||||
devise-two-factor
|
||||
devise_pam_authenticatable2 (~> 9.2)
|
||||
discard (~> 1.2)
|
||||
doorkeeper (~> 5.6)
|
||||
|
@ -864,7 +946,7 @@ DEPENDENCIES
|
|||
http (~> 5.2.0)
|
||||
http_accept_language (~> 2.1)
|
||||
httplog (~> 1.6.2)
|
||||
i18n (= 1.14.1)
|
||||
i18n
|
||||
i18n-tasks (~> 1.0)
|
||||
idn-ruby
|
||||
inline_svg
|
||||
|
@ -875,7 +957,7 @@ DEPENDENCIES
|
|||
kaminari (~> 1.2)
|
||||
kt-paperclip (~> 7.2)
|
||||
letter_opener (~> 1.8)
|
||||
letter_opener_web (~> 2.0)
|
||||
letter_opener_web (~> 3.0)
|
||||
link_header (~> 0.0)
|
||||
lograge (~> 0.12)
|
||||
mail (~> 2.8)
|
||||
|
@ -893,6 +975,21 @@ DEPENDENCIES
|
|||
omniauth-rails_csrf_protection (~> 1.0)
|
||||
omniauth-saml (~> 2.0)
|
||||
omniauth_openid_connect (~> 0.6.1)
|
||||
opentelemetry-exporter-otlp (~> 0.26.3)
|
||||
opentelemetry-instrumentation-active_job (~> 0.7.1)
|
||||
opentelemetry-instrumentation-active_model_serializers (~> 0.20.1)
|
||||
opentelemetry-instrumentation-concurrent_ruby (~> 0.21.2)
|
||||
opentelemetry-instrumentation-excon (~> 0.22.0)
|
||||
opentelemetry-instrumentation-faraday (~> 0.24.1)
|
||||
opentelemetry-instrumentation-http (~> 0.23.2)
|
||||
opentelemetry-instrumentation-http_client (~> 0.22.3)
|
||||
opentelemetry-instrumentation-net_http (~> 0.22.4)
|
||||
opentelemetry-instrumentation-pg (~> 0.27.1)
|
||||
opentelemetry-instrumentation-rack (~> 0.24.1)
|
||||
opentelemetry-instrumentation-rails (~> 0.30.0)
|
||||
opentelemetry-instrumentation-redis (~> 0.25.3)
|
||||
opentelemetry-instrumentation-sidekiq (~> 0.25.2)
|
||||
opentelemetry-sdk (~> 1.4)
|
||||
ox (~> 2.14)
|
||||
parslet
|
||||
pg (~> 1.5)
|
||||
|
@ -917,7 +1014,7 @@ DEPENDENCIES
|
|||
rqrcode (~> 2.2)
|
||||
rspec-github (~> 2.4)
|
||||
rspec-rails (~> 6.0)
|
||||
rspec-sidekiq (~> 4.0)
|
||||
rspec-sidekiq (~> 5.0)
|
||||
rubocop
|
||||
rubocop-capybara
|
||||
rubocop-performance
|
||||
|
|
|
@ -70,7 +70,7 @@ Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Stre
|
|||
- **PostgreSQL** 12+
|
||||
- **Redis** 4+
|
||||
- **Ruby** 3.1+
|
||||
- **Node.js** 16+
|
||||
- **Node.js** 18+
|
||||
|
||||
The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation.
|
||||
|
||||
|
@ -91,10 +91,12 @@ A **Vagrant** configuration is included for development purposes. To use it, com
|
|||
To set up **MacOS** for native development, complete the following steps:
|
||||
|
||||
- Use a Ruby version manager to install the specified version from `.ruby-version`
|
||||
- Run `bundle` to install required gems
|
||||
- Run `brew install postgresql@14 redis imagemagick libidn` to install required dependencies
|
||||
- Navigate to Mastodon's root directory and run `brew install nvm` then `nvm use` to use the version from `.nvmrc`
|
||||
- Run `yarn` to install required packages
|
||||
- Run `corepack enable && corepack prepare`
|
||||
- Run `bundle exec rails db:setup` (optionally prepend `RAILS_ENV=development` to target the dev environment)
|
||||
- Run `RAILS_ENV=development bundle exec rails db:setup`
|
||||
- Finally, run `bin/dev` which will launch the local services via `overmind` (if installed) or `foreman`
|
||||
|
||||
### Docker
|
||||
|
|
|
@ -25,7 +25,7 @@ class AccountsController < ApplicationController
|
|||
|
||||
limit = params[:limit].present? ? [params[:limit].to_i, PAGE_SIZE_MAX].min : PAGE_SIZE
|
||||
@statuses = filtered_statuses.without_reblogs.limit(limit)
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
@statuses = preload_collection(@statuses, Status)
|
||||
end
|
||||
|
||||
format.json do
|
||||
|
|
|
@ -18,7 +18,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
|
|||
def set_items
|
||||
case params[:id]
|
||||
when 'featured'
|
||||
@items = for_signed_account { cache_collection(@account.pinned_statuses, Status) }
|
||||
@items = for_signed_account { preload_collection(@account.pinned_statuses, Status) }
|
||||
@items = @items.map { |item| item.distributable? ? item : ActivityPub::TagManager.instance.uri_for(item) }
|
||||
when 'tags'
|
||||
@items = for_signed_account { @account.featured_tags }
|
||||
|
|
|
@ -60,7 +60,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
|
|||
def set_statuses
|
||||
return unless page_requested?
|
||||
|
||||
@statuses = cache_collection_paginated_by_id(
|
||||
@statuses = preload_collection_paginated_by_id(
|
||||
AccountStatusesFilter.new(@account, signed_request_account).results,
|
||||
Status,
|
||||
LIMIT,
|
||||
|
|
|
@ -128,7 +128,7 @@ module Admin
|
|||
def unblock_email
|
||||
authorize @account, :unblock_email?
|
||||
|
||||
CanonicalEmailBlock.matching_account(@account).delete_all
|
||||
CanonicalEmailBlock.where(reference_account: @account).delete_all
|
||||
|
||||
log_action :unblock_email, @account
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ module Admin
|
|||
|
||||
@site_upload.destroy!
|
||||
|
||||
redirect_to admin_settings_path, notice: I18n.t('admin.site_uploads.destroyed_msg')
|
||||
redirect_back fallback_location: admin_settings_path, notice: I18n.t('admin.site_uploads.destroyed_msg')
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -19,11 +19,11 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
|||
end
|
||||
|
||||
def load_statuses
|
||||
@account.unavailable? ? [] : cached_account_statuses
|
||||
@account.unavailable? ? [] : preloaded_account_statuses
|
||||
end
|
||||
|
||||
def cached_account_statuses
|
||||
cache_collection_paginated_by_id(
|
||||
def preloaded_account_statuses
|
||||
preload_collection_paginated_by_id(
|
||||
AccountStatusesFilter.new(@account, current_account, params).results,
|
||||
Status,
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
|
|
|
@ -9,16 +9,22 @@ class Api::V1::AccountsController < Api::BaseController
|
|||
before_action -> { doorkeeper_authorize! :follow, :write, :'write:blocks' }, only: [:block, :unblock]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:create]
|
||||
|
||||
before_action :require_user!, except: [:show, :create]
|
||||
before_action :set_account, except: [:create]
|
||||
before_action :check_account_approval, except: [:create]
|
||||
before_action :check_account_confirmation, except: [:create]
|
||||
before_action :require_user!, except: [:index, :show, :create]
|
||||
before_action :set_account, except: [:index, :create]
|
||||
before_action :set_accounts, only: [:index]
|
||||
before_action :check_account_approval, except: [:index, :create]
|
||||
before_action :check_account_confirmation, except: [:index, :create]
|
||||
before_action :check_enabled_registrations, only: [:create]
|
||||
before_action :check_accounts_limit, only: [:index]
|
||||
|
||||
skip_before_action :require_authenticated_user!, only: :create
|
||||
|
||||
override_rate_limit_headers :follow, family: :follows
|
||||
|
||||
def index
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
cache_if_unauthenticated!
|
||||
render json: @account, serializer: REST::AccountSerializer
|
||||
|
@ -79,6 +85,10 @@ class Api::V1::AccountsController < Api::BaseController
|
|||
@account = Account.find(params[:id])
|
||||
end
|
||||
|
||||
def set_accounts
|
||||
@accounts = Account.where(id: account_ids).without_unapproved
|
||||
end
|
||||
|
||||
def check_account_approval
|
||||
raise(ActiveRecord::RecordNotFound) if @account.local? && @account.user_pending?
|
||||
end
|
||||
|
@ -87,10 +97,22 @@ class Api::V1::AccountsController < Api::BaseController
|
|||
raise(ActiveRecord::RecordNotFound) if @account.local? && !@account.user_confirmed?
|
||||
end
|
||||
|
||||
def check_accounts_limit
|
||||
raise(Mastodon::ValidationError) if account_ids.size > DEFAULT_ACCOUNTS_LIMIT
|
||||
end
|
||||
|
||||
def relationships(**options)
|
||||
AccountRelationshipsPresenter.new([@account], current_user.account_id, **options)
|
||||
end
|
||||
|
||||
def account_ids
|
||||
Array(accounts_params[:ids]).uniq.map(&:to_i)
|
||||
end
|
||||
|
||||
def accounts_params
|
||||
params.permit(ids: [])
|
||||
end
|
||||
|
||||
def account_params
|
||||
params.permit(:username, :email, :password, :agreement, :locale, :reason, :time_zone, :invite_code)
|
||||
end
|
||||
|
|
|
@ -29,10 +29,11 @@ class Api::V1::Admin::DomainBlocksController < Api::BaseController
|
|||
def create
|
||||
authorize :domain_block, :create?
|
||||
|
||||
@domain_block = DomainBlock.new(resource_params)
|
||||
existing_domain_block = resource_params[:domain].present? ? DomainBlock.rule_for(resource_params[:domain]) : nil
|
||||
return render json: existing_domain_block, serializer: REST::Admin::ExistingDomainBlockErrorSerializer, status: 422 if existing_domain_block.present?
|
||||
return render json: existing_domain_block, serializer: REST::Admin::ExistingDomainBlockErrorSerializer, status: 422 if conflicts_with_existing_block?(@domain_block, existing_domain_block)
|
||||
|
||||
@domain_block = DomainBlock.create!(resource_params)
|
||||
@domain_block.save!
|
||||
DomainBlockWorker.perform_async(@domain_block.id)
|
||||
log_action :create, @domain_block
|
||||
render json: @domain_block, serializer: REST::Admin::DomainBlockSerializer
|
||||
|
@ -55,6 +56,10 @@ class Api::V1::Admin::DomainBlocksController < Api::BaseController
|
|||
|
||||
private
|
||||
|
||||
def conflicts_with_existing_block?(domain_block, existing_domain_block)
|
||||
existing_domain_block.present? && (existing_domain_block.domain == TagManager.instance.normalize_domain(domain_block.domain) || !domain_block.stricter_than?(existing_domain_block))
|
||||
end
|
||||
|
||||
def set_domain_blocks
|
||||
@domain_blocks = filtered_domain_blocks.order(id: :desc).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
end
|
||||
|
|
|
@ -4,6 +4,6 @@ class Api::V1::Apps::CredentialsController < Api::BaseController
|
|||
def show
|
||||
return doorkeeper_render_error unless valid_doorkeeper_token?
|
||||
|
||||
render json: doorkeeper_token.application, serializer: REST::ApplicationSerializer, fields: %i(name website vapid_key client_id scopes)
|
||||
render json: doorkeeper_token.application, serializer: REST::ApplicationSerializer
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ class Api::V1::AppsController < Api::BaseController
|
|||
|
||||
def create
|
||||
@app = Doorkeeper::Application.create!(application_options)
|
||||
render json: @app, serializer: REST::ApplicationSerializer
|
||||
render json: @app, serializer: REST::CredentialApplicationSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -24,6 +24,6 @@ class Api::V1::AppsController < Api::BaseController
|
|||
end
|
||||
|
||||
def app_params
|
||||
params.permit(:client_name, :redirect_uris, :scopes, :website)
|
||||
params.permit(:client_name, :scopes, :website, :redirect_uris, redirect_uris: [])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,11 +13,11 @@ class Api::V1::BookmarksController < Api::BaseController
|
|||
private
|
||||
|
||||
def load_statuses
|
||||
cached_bookmarks
|
||||
preloaded_bookmarks
|
||||
end
|
||||
|
||||
def cached_bookmarks
|
||||
cache_collection(results.map(&:status), Status)
|
||||
def preloaded_bookmarks
|
||||
preload_collection(results.map(&:status), Status)
|
||||
end
|
||||
|
||||
def results
|
||||
|
|
|
@ -13,11 +13,11 @@ class Api::V1::FavouritesController < Api::BaseController
|
|||
private
|
||||
|
||||
def load_statuses
|
||||
cached_favourites
|
||||
preloaded_favourites
|
||||
end
|
||||
|
||||
def cached_favourites
|
||||
cache_collection(results.map(&:status), Status)
|
||||
def preloaded_favourites
|
||||
preload_collection(results.map(&:status), Status)
|
||||
end
|
||||
|
||||
def results
|
||||
|
|
|
@ -41,7 +41,7 @@ class Api::V1::Notifications::RequestsController < Api::BaseController
|
|||
)
|
||||
|
||||
NotificationRequest.preload_cache_collection(requests) do |statuses|
|
||||
cache_collection(statuses, Status)
|
||||
preload_collection(statuses, Status)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ class Api::V1::NotificationsController < Api::BaseController
|
|||
)
|
||||
|
||||
Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses|
|
||||
cache_collection(target_statuses, Status)
|
||||
preload_collection(target_statuses, Status)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Push::SubscriptionsController < Api::BaseController
|
||||
include Redisable
|
||||
include Lockable
|
||||
|
||||
before_action -> { doorkeeper_authorize! :push }
|
||||
before_action :require_user!
|
||||
before_action :set_push_subscription
|
||||
before_action :set_push_subscription, only: [:show, :update]
|
||||
before_action :check_push_subscription, only: [:show, :update]
|
||||
|
||||
def show
|
||||
|
@ -11,16 +14,18 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
|
|||
end
|
||||
|
||||
def create
|
||||
@push_subscription&.destroy!
|
||||
with_redis_lock("push_subscription:#{current_user.id}") do
|
||||
destroy_web_push_subscriptions!
|
||||
|
||||
@push_subscription = Web::PushSubscription.create!(
|
||||
endpoint: subscription_params[:endpoint],
|
||||
key_p256dh: subscription_params[:keys][:p256dh],
|
||||
key_auth: subscription_params[:keys][:auth],
|
||||
data: data_params,
|
||||
user_id: current_user.id,
|
||||
access_token_id: doorkeeper_token.id
|
||||
)
|
||||
@push_subscription = Web::PushSubscription.create!(
|
||||
endpoint: subscription_params[:endpoint],
|
||||
key_p256dh: subscription_params[:keys][:p256dh],
|
||||
key_auth: subscription_params[:keys][:auth],
|
||||
data: data_params,
|
||||
user_id: current_user.id,
|
||||
access_token_id: doorkeeper_token.id
|
||||
)
|
||||
end
|
||||
|
||||
render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer
|
||||
end
|
||||
|
@ -31,14 +36,18 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
|
|||
end
|
||||
|
||||
def destroy
|
||||
@push_subscription&.destroy!
|
||||
destroy_web_push_subscriptions!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def destroy_web_push_subscriptions!
|
||||
doorkeeper_token.web_push_subscriptions.destroy_all
|
||||
end
|
||||
|
||||
def set_push_subscription
|
||||
@push_subscription = Web::PushSubscription.find_by(access_token_id: doorkeeper_token.id)
|
||||
@push_subscription = doorkeeper_token.web_push_subscriptions.first
|
||||
end
|
||||
|
||||
def check_push_subscription
|
||||
|
|
|
@ -5,9 +5,11 @@ class Api::V1::StatusesController < Api::BaseController
|
|||
|
||||
before_action -> { authorize_if_got_token! :read, :'read:statuses' }, except: [:create, :update, :destroy]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:create, :update, :destroy]
|
||||
before_action :require_user!, except: [:show, :context]
|
||||
before_action :set_status, only: [:show, :context]
|
||||
before_action :set_thread, only: [:create]
|
||||
before_action :require_user!, except: [:index, :show, :context]
|
||||
before_action :set_statuses, only: [:index]
|
||||
before_action :set_status, only: [:show, :context]
|
||||
before_action :set_thread, only: [:create]
|
||||
before_action :check_statuses_limit, only: [:index]
|
||||
|
||||
override_rate_limit_headers :create, family: :statuses
|
||||
override_rate_limit_headers :update, family: :statuses
|
||||
|
@ -23,9 +25,14 @@ class Api::V1::StatusesController < Api::BaseController
|
|||
DESCENDANTS_LIMIT = 60
|
||||
DESCENDANTS_DEPTH_LIMIT = 20
|
||||
|
||||
def index
|
||||
@statuses = preload_collection(@statuses, Status)
|
||||
render json: @statuses, each_serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
cache_if_unauthenticated!
|
||||
@status = cache_collection([@status], Status).first
|
||||
@status = preload_collection([@status], Status).first
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
|
@ -44,8 +51,8 @@ class Api::V1::StatusesController < Api::BaseController
|
|||
|
||||
ancestors_results = @status.in_reply_to_id.nil? ? [] : @status.ancestors(ancestors_limit, current_account)
|
||||
descendants_results = @status.descendants(descendants_limit, current_account, descendants_depth_limit)
|
||||
loaded_ancestors = cache_collection(ancestors_results, Status)
|
||||
loaded_descendants = cache_collection(descendants_results, Status)
|
||||
loaded_ancestors = preload_collection(ancestors_results, Status)
|
||||
loaded_descendants = preload_collection(descendants_results, Status)
|
||||
|
||||
@context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
|
||||
statuses = [@status] + @context.ancestors + @context.descendants
|
||||
|
@ -111,6 +118,10 @@ class Api::V1::StatusesController < Api::BaseController
|
|||
|
||||
private
|
||||
|
||||
def set_statuses
|
||||
@statuses = Status.permitted_statuses_from_ids(status_ids, current_account)
|
||||
end
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:id])
|
||||
authorize @status, :show?
|
||||
|
@ -125,6 +136,18 @@ class Api::V1::StatusesController < Api::BaseController
|
|||
render json: { error: I18n.t('statuses.errors.in_reply_not_found') }, status: 404
|
||||
end
|
||||
|
||||
def check_statuses_limit
|
||||
raise(Mastodon::ValidationError) if status_ids.size > DEFAULT_STATUSES_LIMIT
|
||||
end
|
||||
|
||||
def status_ids
|
||||
Array(statuses_params[:ids]).uniq.map(&:to_i)
|
||||
end
|
||||
|
||||
def statuses_params
|
||||
params.permit(ids: [])
|
||||
end
|
||||
|
||||
def status_params
|
||||
params.permit(
|
||||
:status,
|
||||
|
|
|
@ -21,11 +21,11 @@ class Api::V1::Timelines::HomeController < Api::V1::Timelines::BaseController
|
|||
private
|
||||
|
||||
def load_statuses
|
||||
cached_home_statuses
|
||||
preloaded_home_statuses
|
||||
end
|
||||
|
||||
def cached_home_statuses
|
||||
cache_collection home_statuses, Status
|
||||
def preloaded_home_statuses
|
||||
preload_collection home_statuses, Status
|
||||
end
|
||||
|
||||
def home_statuses
|
||||
|
|
|
@ -21,11 +21,11 @@ class Api::V1::Timelines::ListController < Api::V1::Timelines::BaseController
|
|||
end
|
||||
|
||||
def set_statuses
|
||||
@statuses = cached_list_statuses
|
||||
@statuses = preloaded_list_statuses
|
||||
end
|
||||
|
||||
def cached_list_statuses
|
||||
cache_collection list_statuses, Status
|
||||
def preloaded_list_statuses
|
||||
preload_collection list_statuses, Status
|
||||
end
|
||||
|
||||
def list_statuses
|
||||
|
|
|
@ -18,11 +18,11 @@ class Api::V1::Timelines::PublicController < Api::V1::Timelines::BaseController
|
|||
end
|
||||
|
||||
def load_statuses
|
||||
cached_public_statuses_page
|
||||
preloaded_public_statuses_page
|
||||
end
|
||||
|
||||
def cached_public_statuses_page
|
||||
cache_collection(public_statuses, Status)
|
||||
def preloaded_public_statuses_page
|
||||
preload_collection(public_statuses, Status)
|
||||
end
|
||||
|
||||
def public_statuses
|
||||
|
|
|
@ -23,11 +23,11 @@ class Api::V1::Timelines::TagController < Api::V1::Timelines::BaseController
|
|||
end
|
||||
|
||||
def load_statuses
|
||||
cached_tagged_statuses
|
||||
preloaded_tagged_statuses
|
||||
end
|
||||
|
||||
def cached_tagged_statuses
|
||||
@tag.nil? ? [] : cache_collection(tag_timeline_statuses, Status)
|
||||
def preloaded_tagged_statuses
|
||||
@tag.nil? ? [] : preload_collection(tag_timeline_statuses, Status)
|
||||
end
|
||||
|
||||
def tag_timeline_statuses
|
||||
|
|
|
@ -20,7 +20,7 @@ class Api::V1::Trends::StatusesController < Api::BaseController
|
|||
|
||||
def set_statuses
|
||||
@statuses = if enabled?
|
||||
cache_collection(statuses_from_trends.offset(offset_param).limit(limit_param(DEFAULT_STATUSES_LIMIT)), Status)
|
||||
preload_collection(statuses_from_trends.offset(offset_param).limit(limit_param(DEFAULT_STATUSES_LIMIT)), Status)
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
|
|
@ -9,6 +9,7 @@ class ApplicationController < ActionController::Base
|
|||
include UserTrackingConcern
|
||||
include SessionTrackingConcern
|
||||
include CacheConcern
|
||||
include PreloadingConcern
|
||||
include DomainControlHelper
|
||||
include DatabaseHelper
|
||||
include AuthorizedFetchHelper
|
||||
|
|
|
@ -45,20 +45,4 @@ module CacheConcern
|
|||
Rails.cache.write(key, response.body, expires_in: expires_in, raw: true)
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Rename this method, as it does not perform any caching anymore.
|
||||
def cache_collection(raw, klass)
|
||||
return raw unless klass.respond_to?(:preload_cacheable_associations)
|
||||
|
||||
records = raw.to_a
|
||||
|
||||
klass.preload_cacheable_associations(records)
|
||||
|
||||
records
|
||||
end
|
||||
|
||||
# TODO: Rename this method, as it does not perform any caching anymore.
|
||||
def cache_collection_paginated_by_id(raw, klass, limit, options)
|
||||
cache_collection raw.to_a_paginated_by_id(limit, options), klass
|
||||
end
|
||||
end
|
||||
|
|
17
app/controllers/concerns/preloading_concern.rb
Normal file
17
app/controllers/concerns/preloading_concern.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module PreloadingConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def preload_collection(scope, klass)
|
||||
return scope unless klass.respond_to?(:preload_cacheable_associations)
|
||||
|
||||
scope.to_a.tap do |records|
|
||||
klass.preload_cacheable_associations(records)
|
||||
end
|
||||
end
|
||||
|
||||
def preload_collection_paginated_by_id(scope, klass, limit, options)
|
||||
preload_collection scope.to_a_paginated_by_id(limit, options), klass
|
||||
end
|
||||
end
|
|
@ -13,7 +13,7 @@ class Settings::ApplicationsController < Settings::BaseController
|
|||
def new
|
||||
@application = Doorkeeper::Application.new(
|
||||
redirect_uri: Doorkeeper.configuration.native_redirect_uri,
|
||||
scopes: 'read write follow'
|
||||
scopes: 'read:me'
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class TagsController < ApplicationController
|
|||
end
|
||||
|
||||
def set_statuses
|
||||
@statuses = cache_collection(TagFeed.new(@tag, nil, local: @local).get(limit_param), Status)
|
||||
@statuses = preload_collection(TagFeed.new(@tag, nil, local: @local).get(limit_param), Status)
|
||||
end
|
||||
|
||||
def limit_param
|
||||
|
|
23
app/controllers/well_known/oauth_metadata_controller.rb
Normal file
23
app/controllers/well_known/oauth_metadata_controller.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module WellKnown
|
||||
class OauthMetadataController < ActionController::Base # rubocop:disable Rails/ApplicationController
|
||||
include CacheConcern
|
||||
|
||||
# Prevent `active_model_serializer`'s `ActionController::Serialization` from calling `current_user`
|
||||
# and thus re-issuing session cookies
|
||||
serialization_scope nil
|
||||
|
||||
def show
|
||||
# Due to this document potentially changing between Mastodon versions (as
|
||||
# new OAuth scopes are added), we don't use expires_in to cache upstream,
|
||||
# instead just caching in the rails cache:
|
||||
render_with_cache(
|
||||
json: ::OauthMetadataPresenter.new,
|
||||
serializer: ::OauthMetadataSerializer,
|
||||
content_type: 'application/json',
|
||||
expires_in: 15.minutes
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -240,6 +240,22 @@ module ApplicationHelper
|
|||
EmojiFormatter.new(html, custom_emojis, other_options.merge(animate: prefers_autoplay?)).to_s
|
||||
end
|
||||
|
||||
def mascot_url
|
||||
full_asset_url(instance_presenter.mascot&.file&.url || frontend_asset_path('images/elephant_ui_plane.svg'))
|
||||
end
|
||||
|
||||
def instance_presenter
|
||||
@instance_presenter ||= InstancePresenter.new
|
||||
end
|
||||
|
||||
def favicon_path(size = '48')
|
||||
instance_presenter.favicon&.file&.url(size)
|
||||
end
|
||||
|
||||
def app_icon_path(size = '48')
|
||||
instance_presenter.app_icon&.file&.url(size)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def storage_host_var
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module MascotHelper
|
||||
def mascot_url
|
||||
full_asset_url(instance_presenter.mascot&.file&.url || frontend_asset_path('images/elephant_ui_plane.svg'))
|
||||
end
|
||||
|
||||
def instance_presenter
|
||||
@instance_presenter ||= InstancePresenter.new
|
||||
end
|
||||
end
|
|
@ -1,119 +0,0 @@
|
|||
import * as WebAuthnJSON from '@github/webauthn-json';
|
||||
import axios from 'axios';
|
||||
|
||||
import ready from '../mastodon/ready';
|
||||
import 'regenerator-runtime/runtime';
|
||||
|
||||
function getCSRFToken() {
|
||||
var CSRFSelector = document.querySelector('meta[name="csrf-token"]');
|
||||
if (CSRFSelector) {
|
||||
return CSRFSelector.getAttribute('content');
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function hideFlashMessages() {
|
||||
Array.from(document.getElementsByClassName('flash-message')).forEach(function(flashMessage) {
|
||||
flashMessage.classList.add('hidden');
|
||||
});
|
||||
}
|
||||
|
||||
function callback(url, body) {
|
||||
axios.post(url, JSON.stringify(body), {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'X-CSRF-Token': getCSRFToken(),
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
}).then(function(response) {
|
||||
window.location.replace(response.data.redirect_path);
|
||||
}).catch(function(error) {
|
||||
if (error.response.status === 422) {
|
||||
const errorMessage = document.getElementById('security-key-error-message');
|
||||
errorMessage.classList.remove('hidden');
|
||||
console.error(error.response.data.error);
|
||||
} else {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ready(() => {
|
||||
if (!WebAuthnJSON.supported()) {
|
||||
const unsupported_browser_message = document.getElementById('unsupported-browser-message');
|
||||
if (unsupported_browser_message) {
|
||||
unsupported_browser_message.classList.remove('hidden');
|
||||
document.querySelector('.btn.js-webauthn').disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const webAuthnCredentialRegistrationForm = document.getElementById('new_webauthn_credential');
|
||||
if (webAuthnCredentialRegistrationForm) {
|
||||
webAuthnCredentialRegistrationForm.addEventListener('submit', (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
var nickname = event.target.querySelector('input[name="new_webauthn_credential[nickname]"]');
|
||||
if (nickname.value) {
|
||||
axios.get('/settings/security_keys/options')
|
||||
.then((response) => {
|
||||
const credentialOptions = response.data;
|
||||
|
||||
WebAuthnJSON.create({ 'publicKey': credentialOptions }).then((credential) => {
|
||||
var params = { 'credential': credential, 'nickname': nickname.value };
|
||||
callback('/settings/security_keys', params);
|
||||
}).catch((error) => {
|
||||
const errorMessage = document.getElementById('security-key-error-message');
|
||||
errorMessage.classList.remove('hidden');
|
||||
console.error(error);
|
||||
});
|
||||
}).catch((error) => {
|
||||
console.error(error.response.data.error);
|
||||
});
|
||||
} else {
|
||||
nickname.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const webAuthnCredentialAuthenticationForm = document.getElementById('webauthn-form');
|
||||
if (webAuthnCredentialAuthenticationForm) {
|
||||
webAuthnCredentialAuthenticationForm.addEventListener('submit', (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
axios.get('sessions/security_key_options')
|
||||
.then((response) => {
|
||||
const credentialOptions = response.data;
|
||||
|
||||
WebAuthnJSON.get({ 'publicKey': credentialOptions }).then((credential) => {
|
||||
var params = { 'user': { 'credential': credential } };
|
||||
callback('sign_in', params);
|
||||
}).catch((error) => {
|
||||
const errorMessage = document.getElementById('security-key-error-message');
|
||||
errorMessage.classList.remove('hidden');
|
||||
console.error(error);
|
||||
});
|
||||
}).catch((error) => {
|
||||
console.error(error.response.data.error);
|
||||
});
|
||||
});
|
||||
|
||||
const otpAuthenticationForm = document.getElementById('otp-authentication-form');
|
||||
|
||||
const linkToOtp = document.getElementById('link-to-otp');
|
||||
linkToOtp.addEventListener('click', () => {
|
||||
webAuthnCredentialAuthenticationForm.classList.add('hidden');
|
||||
otpAuthenticationForm.classList.remove('hidden');
|
||||
hideFlashMessages();
|
||||
});
|
||||
|
||||
const linkToWebAuthn = document.getElementById('link-to-webauthn');
|
||||
linkToWebAuthn.addEventListener('click', () => {
|
||||
otpAuthenticationForm.classList.add('hidden');
|
||||
webAuthnCredentialAuthenticationForm.classList.remove('hidden');
|
||||
hideFlashMessages();
|
||||
});
|
||||
}
|
||||
});
|
197
app/javascript/entrypoints/two_factor_authentication.ts
Normal file
197
app/javascript/entrypoints/two_factor_authentication.ts
Normal file
|
@ -0,0 +1,197 @@
|
|||
import * as WebAuthnJSON from '@github/webauthn-json';
|
||||
import axios, { AxiosError } from 'axios';
|
||||
|
||||
import ready from '../mastodon/ready';
|
||||
|
||||
import 'regenerator-runtime/runtime';
|
||||
|
||||
type PublicKeyCredentialCreationOptionsJSON =
|
||||
WebAuthnJSON.CredentialCreationOptionsJSON['publicKey'];
|
||||
|
||||
function exceptionHasAxiosError(
|
||||
error: unknown,
|
||||
): error is AxiosError<{ error: unknown }> {
|
||||
return (
|
||||
error instanceof AxiosError &&
|
||||
typeof error.response?.data === 'object' &&
|
||||
'error' in error.response.data
|
||||
);
|
||||
}
|
||||
|
||||
function logAxiosResponseError(error: unknown) {
|
||||
if (exceptionHasAxiosError(error)) console.error(error);
|
||||
}
|
||||
|
||||
function getCSRFToken() {
|
||||
return document
|
||||
.querySelector<HTMLMetaElement>('meta[name="csrf-token"]')
|
||||
?.getAttribute('content');
|
||||
}
|
||||
|
||||
function hideFlashMessages() {
|
||||
document.querySelectorAll('.flash-message').forEach((flashMessage) => {
|
||||
flashMessage.classList.add('hidden');
|
||||
});
|
||||
}
|
||||
|
||||
async function callback(
|
||||
url: string,
|
||||
body:
|
||||
| {
|
||||
credential: WebAuthnJSON.PublicKeyCredentialWithAttestationJSON;
|
||||
nickname: string;
|
||||
}
|
||||
| {
|
||||
user: { credential: WebAuthnJSON.PublicKeyCredentialWithAssertionJSON };
|
||||
},
|
||||
) {
|
||||
try {
|
||||
const response = await axios.post<{ redirect_path: string }>(
|
||||
url,
|
||||
JSON.stringify(body),
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
'X-CSRF-Token': getCSRFToken(),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
window.location.replace(response.data.redirect_path);
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError && error.response?.status === 422) {
|
||||
const errorMessage = document.getElementById(
|
||||
'security-key-error-message',
|
||||
);
|
||||
errorMessage?.classList.remove('hidden');
|
||||
|
||||
logAxiosResponseError(error);
|
||||
} else {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleWebauthnCredentialRegistration(nickname: string) {
|
||||
try {
|
||||
const response = await axios.get<PublicKeyCredentialCreationOptionsJSON>(
|
||||
'/settings/security_keys/options',
|
||||
);
|
||||
|
||||
const credentialOptions = response.data;
|
||||
|
||||
try {
|
||||
const credential = await WebAuthnJSON.create({
|
||||
publicKey: credentialOptions,
|
||||
});
|
||||
|
||||
const params = {
|
||||
credential: credential,
|
||||
nickname: nickname,
|
||||
};
|
||||
|
||||
await callback('/settings/security_keys', params);
|
||||
} catch (error) {
|
||||
const errorMessage = document.getElementById(
|
||||
'security-key-error-message',
|
||||
);
|
||||
errorMessage?.classList.remove('hidden');
|
||||
console.error(error);
|
||||
}
|
||||
} catch (error) {
|
||||
logAxiosResponseError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleWebauthnCredentialAuthentication() {
|
||||
try {
|
||||
const response = await axios.get<PublicKeyCredentialCreationOptionsJSON>(
|
||||
'sessions/security_key_options',
|
||||
);
|
||||
|
||||
const credentialOptions = response.data;
|
||||
|
||||
try {
|
||||
const credential = await WebAuthnJSON.get({
|
||||
publicKey: credentialOptions,
|
||||
});
|
||||
|
||||
const params = { user: { credential: credential } };
|
||||
void callback('sign_in', params);
|
||||
} catch (error) {
|
||||
const errorMessage = document.getElementById(
|
||||
'security-key-error-message',
|
||||
);
|
||||
errorMessage?.classList.remove('hidden');
|
||||
console.error(error);
|
||||
}
|
||||
} catch (error) {
|
||||
logAxiosResponseError(error);
|
||||
}
|
||||
}
|
||||
|
||||
ready(() => {
|
||||
if (!WebAuthnJSON.supported()) {
|
||||
const unsupported_browser_message = document.getElementById(
|
||||
'unsupported-browser-message',
|
||||
);
|
||||
if (unsupported_browser_message) {
|
||||
unsupported_browser_message.classList.remove('hidden');
|
||||
const button = document.querySelector<HTMLButtonElement>(
|
||||
'button.btn.js-webauthn',
|
||||
);
|
||||
if (button) button.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
const webAuthnCredentialRegistrationForm =
|
||||
document.querySelector<HTMLFormElement>('form#new_webauthn_credential');
|
||||
if (webAuthnCredentialRegistrationForm) {
|
||||
webAuthnCredentialRegistrationForm.addEventListener('submit', (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (!(event.target instanceof HTMLFormElement)) return;
|
||||
|
||||
const nickname = event.target.querySelector<HTMLInputElement>(
|
||||
'input[name="new_webauthn_credential[nickname]"]',
|
||||
);
|
||||
|
||||
if (nickname?.value) {
|
||||
void handleWebauthnCredentialRegistration(nickname.value);
|
||||
} else {
|
||||
nickname?.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const webAuthnCredentialAuthenticationForm =
|
||||
document.getElementById('webauthn-form');
|
||||
if (webAuthnCredentialAuthenticationForm) {
|
||||
webAuthnCredentialAuthenticationForm.addEventListener('submit', (event) => {
|
||||
event.preventDefault();
|
||||
void handleWebauthnCredentialAuthentication();
|
||||
});
|
||||
|
||||
const otpAuthenticationForm = document.getElementById(
|
||||
'otp-authentication-form',
|
||||
);
|
||||
|
||||
const linkToOtp = document.getElementById('link-to-otp');
|
||||
|
||||
linkToOtp?.addEventListener('click', () => {
|
||||
webAuthnCredentialAuthenticationForm.classList.add('hidden');
|
||||
otpAuthenticationForm?.classList.remove('hidden');
|
||||
hideFlashMessages();
|
||||
});
|
||||
|
||||
const linkToWebAuthn = document.getElementById('link-to-webauthn');
|
||||
linkToWebAuthn?.addEventListener('click', () => {
|
||||
otpAuthenticationForm?.classList.add('hidden');
|
||||
webAuthnCredentialAuthenticationForm.classList.remove('hidden');
|
||||
hideFlashMessages();
|
||||
});
|
||||
}
|
||||
}).catch((e: unknown) => {
|
||||
throw e;
|
||||
});
|
|
@ -20,7 +20,7 @@ export function changeSetting(path, value) {
|
|||
}
|
||||
|
||||
const debouncedSave = debounce((dispatch, getState) => {
|
||||
if (getState().getIn(['settings', 'saved'])) {
|
||||
if (getState().getIn(['settings', 'saved']) || !getState().getIn(['meta', 'me'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -172,7 +172,6 @@ Account.propTypes = {
|
|||
onBlock: PropTypes.func,
|
||||
onMute: PropTypes.func,
|
||||
onMuteNotifications: PropTypes.func,
|
||||
intl: PropTypes.object.isRequired,
|
||||
hidden: PropTypes.bool,
|
||||
minimal: PropTypes.bool,
|
||||
defaultAction: PropTypes.string,
|
||||
|
|
|
@ -14,8 +14,10 @@ import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
|||
import SettingsIcon from '@/material-icons/400-24px/settings.svg?react';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { ButtonInTabsBar } from 'mastodon/features/ui/util/columns_context';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
|
||||
import { useAppHistory } from './router';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -51,12 +53,8 @@ BackButton.propTypes = {
|
|||
};
|
||||
|
||||
class ColumnHeader extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
intl: PropTypes.object.isRequired,
|
||||
title: PropTypes.node,
|
||||
icon: PropTypes.string,
|
||||
|
@ -171,7 +169,7 @@ class ColumnHeader extends PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
if (this.context.identity.signedIn && (children || (multiColumn && this.props.onPin))) {
|
||||
if (this.props.identity.signedIn && (children || (multiColumn && this.props.onPin))) {
|
||||
collapseButton = (
|
||||
<button
|
||||
className={collapsibleButtonClassName}
|
||||
|
@ -232,4 +230,4 @@ class ColumnHeader extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(withRouter(ColumnHeader));
|
||||
export default injectIntl(withIdentity(withRouter(ColumnHeader)));
|
||||
|
|
|
@ -14,6 +14,7 @@ import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
|||
import { Icon } from 'mastodon/components/icon';
|
||||
import emojify from 'mastodon/features/emoji/emoji';
|
||||
import Motion from 'mastodon/features/ui/util/optional_motion';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
|
||||
import { RelativeTimestamp } from './relative_timestamp';
|
||||
|
||||
|
@ -38,12 +39,8 @@ const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => {
|
|||
}, {});
|
||||
|
||||
class Poll extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
poll: ImmutablePropTypes.map,
|
||||
lang: PropTypes.string,
|
||||
intl: PropTypes.object.isRequired,
|
||||
|
@ -235,7 +232,7 @@ class Poll extends ImmutablePureComponent {
|
|||
</ul>
|
||||
|
||||
<div className='poll__footer'>
|
||||
{!showResults && <button className='button button-secondary' disabled={disabled || !this.context.identity.signedIn} onClick={this.handleVote}><FormattedMessage id='poll.vote' defaultMessage='Vote' /></button>}
|
||||
{!showResults && <button className='button button-secondary' disabled={disabled || !this.props.identity.signedIn} onClick={this.handleVote}><FormattedMessage id='poll.vote' defaultMessage='Vote' /></button>}
|
||||
{!showResults && <><button className='poll__link' onClick={this.handleReveal}><FormattedMessage id='poll.reveal' defaultMessage='See results' /></button> · </>}
|
||||
{showResults && !this.props.disabled && <><button className='poll__link' onClick={this.handleRefresh}><FormattedMessage id='poll.refresh' defaultMessage='Refresh' /></button> · </>}
|
||||
{votesCount}
|
||||
|
@ -247,4 +244,4 @@ class Poll extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(Poll);
|
||||
export default injectIntl(withIdentity(Poll));
|
||||
|
|
|
@ -22,6 +22,7 @@ import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
|
|||
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
|
||||
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
|
||||
import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
|
@ -74,12 +75,8 @@ const mapStateToProps = (state, { status }) => ({
|
|||
});
|
||||
|
||||
class StatusActionBar extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
relationship: ImmutablePropTypes.record,
|
||||
onReply: PropTypes.func,
|
||||
|
@ -118,7 +115,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
];
|
||||
|
||||
handleReplyClick = () => {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
this.props.onReply(this.props.status, this.props.history);
|
||||
|
@ -136,7 +133,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleFavouriteClick = () => {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
this.props.onFavourite(this.props.status);
|
||||
|
@ -146,7 +143,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleReblogClick = e => {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
this.props.onReblog(this.props.status, e);
|
||||
|
@ -250,7 +247,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
|
||||
render () {
|
||||
const { status, relationship, intl, withDismiss, withCounters, scrollKey } = this.props;
|
||||
const { signedIn, permissions } = this.context.identity;
|
||||
const { signedIn, permissions } = this.props.identity;
|
||||
|
||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
||||
const pinnableStatus = ['public', 'unlisted', 'private'].includes(status.get('visibility'));
|
||||
|
@ -410,4 +407,4 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(connect(mapStateToProps)(injectIntl(StatusActionBar)));
|
||||
export default withRouter(withIdentity(connect(mapStateToProps)(injectIntl(StatusActionBar))));
|
||||
|
|
|
@ -12,8 +12,10 @@ import { connect } from 'react-redux';
|
|||
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import PollContainer from 'mastodon/containers/poll_container';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state';
|
||||
|
||||
|
||||
const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
|
||||
|
||||
/**
|
||||
|
@ -67,12 +69,8 @@ const mapStateToProps = state => ({
|
|||
});
|
||||
|
||||
class StatusContent extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
statusContent: PropTypes.string,
|
||||
expanded: PropTypes.bool,
|
||||
|
@ -245,7 +243,7 @@ class StatusContent extends PureComponent {
|
|||
const renderReadMore = this.props.onClick && status.get('collapsed');
|
||||
const contentLocale = intl.locale.replace(/[_-].*/, '');
|
||||
const targetLanguages = this.props.languages?.get(status.get('language') || 'und');
|
||||
const renderTranslate = this.props.onTranslate && this.context.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('search_index').trim().length > 0 && targetLanguages?.includes(contentLocale);
|
||||
const renderTranslate = this.props.onTranslate && this.props.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('search_index').trim().length > 0 && targetLanguages?.includes(contentLocale);
|
||||
|
||||
const content = { __html: statusContent ?? getStatusContent(status) };
|
||||
const spoilerContent = { __html: status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml') };
|
||||
|
@ -328,4 +326,4 @@ class StatusContent extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(connect(mapStateToProps)(injectIntl(StatusContent)));
|
||||
export default withRouter(withIdentity(connect(mapStateToProps)(injectIntl(StatusContent))));
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
@ -14,6 +13,7 @@ import { connectUserStream } from 'mastodon/actions/streaming';
|
|||
import ErrorBoundary from 'mastodon/components/error_boundary';
|
||||
import { Router } from 'mastodon/components/router';
|
||||
import UI from 'mastodon/features/ui';
|
||||
import { IdentityContext, createIdentityContext } from 'mastodon/identity_context';
|
||||
import initialState, { title as siteTitle } from 'mastodon/initial_state';
|
||||
import { IntlProvider } from 'mastodon/locales';
|
||||
import { store } from 'mastodon/store';
|
||||
|
@ -28,33 +28,9 @@ if (initialState.meta.me) {
|
|||
store.dispatch(fetchCustomEmojis());
|
||||
}
|
||||
|
||||
const createIdentityContext = state => ({
|
||||
signedIn: !!state.meta.me,
|
||||
accountId: state.meta.me,
|
||||
disabledAccountId: state.meta.disabled_account_id,
|
||||
accessToken: state.meta.access_token,
|
||||
permissions: state.role ? state.role.permissions : 0,
|
||||
});
|
||||
|
||||
export default class Mastodon extends PureComponent {
|
||||
|
||||
static childContextTypes = {
|
||||
identity: PropTypes.shape({
|
||||
signedIn: PropTypes.bool.isRequired,
|
||||
accountId: PropTypes.string,
|
||||
disabledAccountId: PropTypes.string,
|
||||
accessToken: PropTypes.string,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
identity = createIdentityContext(initialState);
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
identity: this.identity,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.identity.signedIn) {
|
||||
this.disconnect = store.dispatch(connectUserStream());
|
||||
|
@ -74,19 +50,21 @@ export default class Mastodon extends PureComponent {
|
|||
|
||||
render () {
|
||||
return (
|
||||
<IntlProvider>
|
||||
<ReduxProvider store={store}>
|
||||
<ErrorBoundary>
|
||||
<Router>
|
||||
<ScrollContext shouldUpdateScroll={this.shouldUpdateScroll}>
|
||||
<Route path='/' component={UI} />
|
||||
</ScrollContext>
|
||||
</Router>
|
||||
<IdentityContext.Provider value={this.identity}>
|
||||
<IntlProvider>
|
||||
<ReduxProvider store={store}>
|
||||
<ErrorBoundary>
|
||||
<Router>
|
||||
<ScrollContext shouldUpdateScroll={this.shouldUpdateScroll}>
|
||||
<Route path='/' component={UI} />
|
||||
</ScrollContext>
|
||||
</Router>
|
||||
|
||||
<Helmet defaultTitle={title} titleTemplate={`%s - ${title}`} />
|
||||
</ErrorBoundary>
|
||||
</ReduxProvider>
|
||||
</IntlProvider>
|
||||
<Helmet defaultTitle={title} titleTemplate={`%s - ${title}`} />
|
||||
</ErrorBoundary>
|
||||
</ReduxProvider>
|
||||
</IntlProvider>
|
||||
</IdentityContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import { IconButton } from 'mastodon/components/icon_button';
|
|||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||
import { ShortNumber } from 'mastodon/components/short_number';
|
||||
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { autoPlayGif, me, domain as localDomain } from 'mastodon/initial_state';
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
@ -111,6 +112,7 @@ const dateFormatOptions = {
|
|||
class Header extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
account: ImmutablePropTypes.record,
|
||||
identity_props: ImmutablePropTypes.list,
|
||||
onFollow: PropTypes.func.isRequired,
|
||||
|
@ -136,10 +138,6 @@ class Header extends ImmutablePureComponent {
|
|||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
};
|
||||
|
@ -255,7 +253,7 @@ class Header extends ImmutablePureComponent {
|
|||
|
||||
render () {
|
||||
const { account, hidden, intl } = this.props;
|
||||
const { signedIn, permissions } = this.context.identity;
|
||||
const { signedIn, permissions } = this.props.identity;
|
||||
|
||||
if (!account) {
|
||||
return null;
|
||||
|
@ -516,4 +514,4 @@ class Header extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(injectIntl(Header));
|
||||
export default withRouter(withIdentity(injectIntl(Header)));
|
||||
|
|
|
@ -9,6 +9,7 @@ import { connect } from 'react-redux';
|
|||
|
||||
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
||||
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { domain } from 'mastodon/initial_state';
|
||||
|
||||
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||
|
@ -38,16 +39,12 @@ const mapStateToProps = (state, { columnId }) => {
|
|||
};
|
||||
|
||||
class CommunityTimeline extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
onlyMedia: false,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
columnId: PropTypes.string,
|
||||
intl: PropTypes.object.isRequired,
|
||||
|
@ -77,7 +74,7 @@ class CommunityTimeline extends PureComponent {
|
|||
|
||||
componentDidMount () {
|
||||
const { dispatch, onlyMedia } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
dispatch(expandCommunityTimeline({ onlyMedia }));
|
||||
|
||||
|
@ -87,7 +84,7 @@ class CommunityTimeline extends PureComponent {
|
|||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (prevProps.onlyMedia !== this.props.onlyMedia) {
|
||||
const { dispatch, onlyMedia } = this.props;
|
||||
|
@ -161,4 +158,4 @@ class CommunityTimeline extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(CommunityTimeline));
|
||||
export default withIdentity(connect(mapStateToProps)(injectIntl(CommunityTimeline)));
|
||||
|
|
|
@ -12,6 +12,7 @@ import CancelIcon from '@/material-icons/400-24px/cancel-fill.svg?react';
|
|||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { domain, searchEnabled } from 'mastodon/initial_state';
|
||||
import { HASHTAG_REGEX } from 'mastodon/utils/hashtags';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
@ -33,12 +34,8 @@ const labelForRecentSearch = search => {
|
|||
};
|
||||
|
||||
class Search extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
value: PropTypes.string.isRequired,
|
||||
recent: ImmutablePropTypes.orderedSet,
|
||||
submitted: PropTypes.bool,
|
||||
|
@ -276,7 +273,7 @@ class Search extends PureComponent {
|
|||
}
|
||||
|
||||
_calculateOptions (value) {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
const trimmedValue = value.trim();
|
||||
const options = [];
|
||||
|
||||
|
@ -318,7 +315,7 @@ class Search extends PureComponent {
|
|||
render () {
|
||||
const { intl, value, submitted, recent } = this.props;
|
||||
const { expanded, options, selectedOption } = this.state;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
const hasValue = value.length > 0 || submitted;
|
||||
|
||||
|
@ -402,4 +399,4 @@ class Search extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(injectIntl(Search));
|
||||
export default withRouter(withIdentity(injectIntl(Search)));
|
||||
|
|
|
@ -13,6 +13,7 @@ import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
|||
import Column from 'mastodon/components/column';
|
||||
import ColumnHeader from 'mastodon/components/column_header';
|
||||
import Search from 'mastodon/features/compose/containers/search_container';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { trendsEnabled } from 'mastodon/initial_state';
|
||||
|
||||
import Links from './links';
|
||||
|
@ -32,12 +33,8 @@ const mapStateToProps = state => ({
|
|||
});
|
||||
|
||||
class Explore extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
intl: PropTypes.object.isRequired,
|
||||
multiColumn: PropTypes.bool,
|
||||
isSearching: PropTypes.bool,
|
||||
|
@ -53,7 +50,7 @@ class Explore extends PureComponent {
|
|||
|
||||
render() {
|
||||
const { intl, multiColumn, isSearching } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||
|
@ -114,4 +111,4 @@ class Explore extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(Explore));
|
||||
export default withIdentity(connect(mapStateToProps)(injectIntl(Explore)));
|
||||
|
|
|
@ -6,13 +6,14 @@ import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
|
|||
import { Helmet } from 'react-helmet';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
|
||||
import { useIdentity } from '@/mastodon/identity_context';
|
||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||
import { addColumn } from 'mastodon/actions/columns';
|
||||
import { changeSetting } from 'mastodon/actions/settings';
|
||||
import { connectPublicStream, connectCommunityStream } from 'mastodon/actions/streaming';
|
||||
import { expandPublicTimeline, expandCommunityTimeline } from 'mastodon/actions/timelines';
|
||||
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
||||
import initialState, { domain } from 'mastodon/initial_state';
|
||||
import { domain } from 'mastodon/initial_state';
|
||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||
|
||||
import Column from '../../components/column';
|
||||
|
@ -24,15 +25,6 @@ const messages = defineMessages({
|
|||
title: { id: 'column.firehose', defaultMessage: 'Live feeds' },
|
||||
});
|
||||
|
||||
// TODO: use a proper React context later on
|
||||
const useIdentity = () => ({
|
||||
signedIn: !!initialState.meta.me,
|
||||
accountId: initialState.meta.me,
|
||||
disabledAccountId: initialState.meta.disabled_account_id,
|
||||
accessToken: initialState.meta.access_token,
|
||||
permissions: initialState.role ? initialState.role.permissions : 0,
|
||||
});
|
||||
|
||||
const ColumnSettings = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const settings = useAppSelector((state) => state.getIn(['settings', 'firehose']));
|
||||
|
|
|
@ -24,6 +24,7 @@ import { fetchFollowRequests } from 'mastodon/actions/accounts';
|
|||
import Column from 'mastodon/components/column';
|
||||
import ColumnHeader from 'mastodon/components/column_header';
|
||||
import LinkFooter from 'mastodon/features/ui/components/link_footer';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
|
||||
import { me, showTrends } from '../../initial_state';
|
||||
import { NavigationBar } from '../compose/components/navigation_bar';
|
||||
|
@ -75,12 +76,8 @@ const badgeDisplay = (number, limit) => {
|
|||
};
|
||||
|
||||
class GettingStarted extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
intl: PropTypes.object.isRequired,
|
||||
myAccount: ImmutablePropTypes.record,
|
||||
multiColumn: PropTypes.bool,
|
||||
|
@ -91,7 +88,7 @@ class GettingStarted extends ImmutablePureComponent {
|
|||
|
||||
componentDidMount () {
|
||||
const { fetchFollowRequests } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (!signedIn) {
|
||||
return;
|
||||
|
@ -102,7 +99,7 @@ class GettingStarted extends ImmutablePureComponent {
|
|||
|
||||
render () {
|
||||
const { intl, myAccount, multiColumn, unreadFollowRequests } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
const navItems = [];
|
||||
|
||||
|
@ -167,4 +164,4 @@ class GettingStarted extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(GettingStarted));
|
||||
export default withIdentity(connect(mapStateToProps, mapDispatchToProps)(injectIntl(GettingStarted)));
|
||||
|
|
|
@ -17,6 +17,7 @@ import { fetchHashtag, followHashtag, unfollowHashtag } from 'mastodon/actions/t
|
|||
import { expandHashtagTimeline, clearTimeline } from 'mastodon/actions/timelines';
|
||||
import Column from 'mastodon/components/column';
|
||||
import ColumnHeader from 'mastodon/components/column_header';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
|
||||
import StatusListContainer from '../ui/containers/status_list_container';
|
||||
|
||||
|
@ -29,14 +30,10 @@ const mapStateToProps = (state, props) => ({
|
|||
});
|
||||
|
||||
class HashtagTimeline extends PureComponent {
|
||||
|
||||
disconnects = [];
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
params: PropTypes.object.isRequired,
|
||||
columnId: PropTypes.string,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
|
@ -94,7 +91,7 @@ class HashtagTimeline extends PureComponent {
|
|||
};
|
||||
|
||||
_subscribe (dispatch, id, tags = {}, local) {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (!signedIn) {
|
||||
return;
|
||||
|
@ -168,7 +165,7 @@ class HashtagTimeline extends PureComponent {
|
|||
handleFollow = () => {
|
||||
const { dispatch, params, tag } = this.props;
|
||||
const { id } = params;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (!signedIn) {
|
||||
return;
|
||||
|
@ -185,7 +182,7 @@ class HashtagTimeline extends PureComponent {
|
|||
const { hasUnread, columnId, multiColumn, tag } = this.props;
|
||||
const { id, local } = this.props.params;
|
||||
const pinned = !!columnId;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={`#${id}`}>
|
||||
|
@ -225,4 +222,4 @@ class HashtagTimeline extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(HashtagTimeline);
|
||||
export default connect(mapStateToProps)(withIdentity(HashtagTimeline));
|
||||
|
|
|
@ -14,6 +14,7 @@ import { fetchAnnouncements, toggleShowAnnouncements } from 'mastodon/actions/an
|
|||
import { IconWithBadge } from 'mastodon/components/icon_with_badge';
|
||||
import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator';
|
||||
import AnnouncementsContainer from 'mastodon/features/getting_started/containers/announcements_container';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { criticalUpdatesPending } from 'mastodon/initial_state';
|
||||
|
||||
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||
|
@ -40,12 +41,8 @@ const mapStateToProps = state => ({
|
|||
});
|
||||
|
||||
class HomeTimeline extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
hasUnread: PropTypes.bool,
|
||||
|
@ -126,7 +123,7 @@ class HomeTimeline extends PureComponent {
|
|||
render () {
|
||||
const { intl, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props;
|
||||
const pinned = !!columnId;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
const banners = [];
|
||||
|
||||
let announcementsButton;
|
||||
|
@ -190,4 +187,4 @@ class HomeTimeline extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(HomeTimeline));
|
||||
export default connect(mapStateToProps)(withIdentity(injectIntl(HomeTimeline)));
|
||||
|
|
|
@ -5,6 +5,7 @@ import { FormattedMessage } from 'react-intl';
|
|||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_REPORTS } from 'mastodon/permissions';
|
||||
|
||||
import { CheckboxWithLabel } from './checkbox_with_label';
|
||||
|
@ -12,13 +13,9 @@ import ClearColumnButton from './clear_column_button';
|
|||
import GrantPermissionButton from './grant_permission_button';
|
||||
import SettingToggle from './setting_toggle';
|
||||
|
||||
export default class ColumnSettings extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
class ColumnSettings extends PureComponent {
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
settings: ImmutablePropTypes.map.isRequired,
|
||||
pushSettings: ImmutablePropTypes.map.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
|
@ -215,7 +212,7 @@ export default class ColumnSettings extends PureComponent {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
{((this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) && (
|
||||
{((this.props.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) && (
|
||||
<section role='group' aria-labelledby='notifications-admin-sign-up'>
|
||||
<h3 id='notifications-status'><FormattedMessage id='notifications.column_settings.admin.sign_up' defaultMessage='New sign-ups:' /></h3>
|
||||
|
||||
|
@ -228,7 +225,7 @@ export default class ColumnSettings extends PureComponent {
|
|||
</section>
|
||||
)}
|
||||
|
||||
{((this.context.identity.permissions & PERMISSION_MANAGE_REPORTS) === PERMISSION_MANAGE_REPORTS) && (
|
||||
{((this.props.identity.permissions & PERMISSION_MANAGE_REPORTS) === PERMISSION_MANAGE_REPORTS) && (
|
||||
<section role='group' aria-labelledby='notifications-admin-report'>
|
||||
<h3 id='notifications-status'><FormattedMessage id='notifications.column_settings.admin.report' defaultMessage='New reports:' /></h3>
|
||||
|
||||
|
@ -245,3 +242,5 @@ export default class ColumnSettings extends PureComponent {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
export default withIdentity(ColumnSettings);
|
||||
|
|
|
@ -41,7 +41,7 @@ const messages = defineMessages({
|
|||
adminSignUp: { id: 'notification.admin.sign_up', defaultMessage: '{name} signed up' },
|
||||
adminReport: { id: 'notification.admin.report', defaultMessage: '{name} reported {target}' },
|
||||
relationshipsSevered: { id: 'notification.relationships_severance_event', defaultMessage: 'Lost connections with {name}' },
|
||||
moderationWarning: { id: 'notification.moderation_warning', defaultMessage: 'Your have received a moderation warning' },
|
||||
moderationWarning: { id: 'notification.moderation_warning', defaultMessage: 'You have received a moderation warning' },
|
||||
});
|
||||
|
||||
const notificationForScreenReader = (intl, message, timestamp) => {
|
||||
|
|
|
@ -17,6 +17,7 @@ import NotificationsIcon from '@/material-icons/400-24px/notifications-fill.svg?
|
|||
import { compareId } from 'mastodon/compare_id';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
|
||||
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||
import { submitMarkers } from '../../actions/markers';
|
||||
|
@ -77,12 +78,8 @@ const mapStateToProps = state => ({
|
|||
});
|
||||
|
||||
class Notifications extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
columnId: PropTypes.string,
|
||||
notifications: ImmutablePropTypes.list.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
|
@ -190,7 +187,7 @@ class Notifications extends PureComponent {
|
|||
const { intl, notifications, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, lastReadId, canMarkAsRead, needsNotificationPermission } = this.props;
|
||||
const pinned = !!columnId;
|
||||
const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. When other people interact with you, you will see it here." />;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
let scrollableContent = null;
|
||||
|
||||
|
@ -299,4 +296,4 @@ class Notifications extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(Notifications));
|
||||
export default connect(mapStateToProps)(withIdentity(injectIntl(Notifications)));
|
||||
|
|
|
@ -18,6 +18,7 @@ import { replyCompose } from 'mastodon/actions/compose';
|
|||
import { reblog, favourite, unreblog, unfavourite } from 'mastodon/actions/interactions';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import { IconButton } from 'mastodon/components/icon_button';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { me, boostModal } from 'mastodon/initial_state';
|
||||
import { makeGetStatus } from 'mastodon/selectors';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
@ -47,12 +48,8 @@ const makeMapStateToProps = () => {
|
|||
};
|
||||
|
||||
class Footer extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
statusId: PropTypes.string.isRequired,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
|
@ -75,7 +72,7 @@ class Footer extends ImmutablePureComponent {
|
|||
|
||||
handleReplyClick = () => {
|
||||
const { dispatch, askReplyConfirmation, status, intl } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (askReplyConfirmation) {
|
||||
|
@ -104,7 +101,7 @@ class Footer extends ImmutablePureComponent {
|
|||
|
||||
handleFavouriteClick = () => {
|
||||
const { dispatch, status } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (status.get('favourited')) {
|
||||
|
@ -131,7 +128,7 @@ class Footer extends ImmutablePureComponent {
|
|||
|
||||
handleReblogClick = e => {
|
||||
const { dispatch, status } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (status.get('reblogged')) {
|
||||
|
@ -209,4 +206,4 @@ class Footer extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(makeMapStateToProps)(withRouter(injectIntl(Footer)));
|
||||
export default connect(makeMapStateToProps)(withIdentity(withRouter(injectIntl(Footer))));
|
||||
|
|
|
@ -9,6 +9,7 @@ import { connect } from 'react-redux';
|
|||
|
||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { domain } from 'mastodon/initial_state';
|
||||
|
||||
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||
|
@ -40,16 +41,12 @@ const mapStateToProps = (state, { columnId }) => {
|
|||
};
|
||||
|
||||
class PublicTimeline extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
onlyMedia: false,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
columnId: PropTypes.string,
|
||||
|
@ -80,7 +77,7 @@ class PublicTimeline extends PureComponent {
|
|||
|
||||
componentDidMount () {
|
||||
const { dispatch, onlyMedia, onlyRemote } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
dispatch(expandPublicTimeline({ onlyMedia, onlyRemote }));
|
||||
|
||||
|
@ -90,7 +87,7 @@ class PublicTimeline extends PureComponent {
|
|||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (prevProps.onlyMedia !== this.props.onlyMedia || prevProps.onlyRemote !== this.props.onlyRemote) {
|
||||
const { dispatch, onlyMedia, onlyRemote } = this.props;
|
||||
|
@ -164,4 +161,4 @@ class PublicTimeline extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(PublicTimeline));
|
||||
export default connect(mapStateToProps)(withIdentity(injectIntl(PublicTimeline)));
|
||||
|
|
|
@ -21,6 +21,7 @@ import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
|
|||
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
|
||||
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
|
||||
import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
|
@ -67,12 +68,8 @@ const mapStateToProps = (state, { status }) => ({
|
|||
});
|
||||
|
||||
class ActionBar extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
relationship: ImmutablePropTypes.record,
|
||||
onReply: PropTypes.func.isRequired,
|
||||
|
@ -198,7 +195,7 @@ class ActionBar extends PureComponent {
|
|||
|
||||
render () {
|
||||
const { status, relationship, intl } = this.props;
|
||||
const { signedIn, permissions } = this.context.identity;
|
||||
const { signedIn, permissions } = this.props.identity;
|
||||
|
||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
||||
const pinnableStatus = ['public', 'unlisted', 'private'].includes(status.get('visibility'));
|
||||
|
@ -326,4 +323,4 @@ class ActionBar extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(connect(mapStateToProps)(injectIntl(ActionBar)));
|
||||
export default withRouter(connect(mapStateToProps)(withIdentity(injectIntl(ActionBar))));
|
||||
|
|
|
@ -20,6 +20,7 @@ import { Icon } from 'mastodon/components/icon';
|
|||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||
import ScrollContainer from 'mastodon/containers/scroll_container';
|
||||
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
import {
|
||||
|
@ -189,12 +190,8 @@ const titleFromStatus = (intl, status) => {
|
|||
};
|
||||
|
||||
class Status extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
status: ImmutablePropTypes.map,
|
||||
|
@ -244,7 +241,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
handleFavouriteClick = (status) => {
|
||||
const { dispatch } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (status.get('favourited')) {
|
||||
|
@ -274,7 +271,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
handleReplyClick = (status) => {
|
||||
const { askReplyConfirmation, dispatch, intl } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (askReplyConfirmation) {
|
||||
|
@ -307,7 +304,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
handleReblogClick = (status, e) => {
|
||||
const { dispatch } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
if (status.get('reblogged')) {
|
||||
|
@ -745,4 +742,4 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default withRouter(injectIntl(connect(makeMapStateToProps)(Status)));
|
||||
export default withRouter(injectIntl(connect(makeMapStateToProps)(withIdentity(Status))));
|
||||
|
|
|
@ -7,16 +7,13 @@ import { changeComposing, mountCompose, unmountCompose } from 'mastodon/actions/
|
|||
import ServerBanner from 'mastodon/components/server_banner';
|
||||
import ComposeFormContainer from 'mastodon/features/compose/containers/compose_form_container';
|
||||
import SearchContainer from 'mastodon/features/compose/containers/search_container';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
|
||||
import LinkFooter from './link_footer';
|
||||
|
||||
class ComposePanel extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
@ -41,7 +38,7 @@ class ComposePanel extends PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
return (
|
||||
<div className='compose-panel' onFocus={this.onFocus}>
|
||||
|
@ -65,4 +62,4 @@ class ComposePanel extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect()(ComposePanel);
|
||||
export default connect()(withIdentity(ComposePanel));
|
||||
|
|
|
@ -13,6 +13,7 @@ import { fetchServer } from 'mastodon/actions/server';
|
|||
import { Avatar } from 'mastodon/components/avatar';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { WordmarkLogo, SymbolLogo } from 'mastodon/components/logo';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { registrationsOpen, me, sso_redirect } from 'mastodon/initial_state';
|
||||
|
||||
const Account = connect(state => ({
|
||||
|
@ -41,12 +42,8 @@ const mapDispatchToProps = (dispatch) => ({
|
|||
});
|
||||
|
||||
class Header extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
openClosedRegistrationsModal: PropTypes.func,
|
||||
location: PropTypes.object,
|
||||
signupUrl: PropTypes.string.isRequired,
|
||||
|
@ -60,7 +57,7 @@ class Header extends PureComponent {
|
|||
}
|
||||
|
||||
render () {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
const { location, openClosedRegistrationsModal, signupUrl, intl } = this.props;
|
||||
|
||||
let content;
|
||||
|
@ -121,4 +118,4 @@ class Header extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(withRouter(connect(mapStateToProps, mapDispatchToProps)(Header)));
|
||||
export default injectIntl(withRouter(withIdentity(connect(mapStateToProps, mapDispatchToProps)(Header))));
|
||||
|
|
|
@ -8,6 +8,7 @@ import { Link } from 'react-router-dom';
|
|||
import { connect } from 'react-redux';
|
||||
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { domain, version, source_url, statusPageUrl, profile_directory as profileDirectory } from 'mastodon/initial_state';
|
||||
import { PERMISSION_INVITE_USERS } from 'mastodon/permissions';
|
||||
import { logOut } from 'mastodon/utils/log_out';
|
||||
|
@ -32,12 +33,8 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
});
|
||||
|
||||
class LinkFooter extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
multiColumn: PropTypes.bool,
|
||||
onLogout: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
|
@ -53,7 +50,7 @@ class LinkFooter extends PureComponent {
|
|||
};
|
||||
|
||||
render () {
|
||||
const { signedIn, permissions } = this.context.identity;
|
||||
const { signedIn, permissions } = this.props.identity;
|
||||
const { multiColumn } = this.props;
|
||||
|
||||
const canInvite = signedIn && ((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS);
|
||||
|
@ -113,4 +110,4 @@ class LinkFooter extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(connect(null, mapDispatchToProps)(LinkFooter));
|
||||
export default injectIntl(withIdentity(connect(null, mapDispatchToProps)(LinkFooter)));
|
||||
|
|
|
@ -32,6 +32,7 @@ import { fetchFollowRequests } from 'mastodon/actions/accounts';
|
|||
import { IconWithBadge } from 'mastodon/components/icon_with_badge';
|
||||
import { WordmarkLogo } from 'mastodon/components/logo';
|
||||
import { NavigationPortal } from 'mastodon/components/navigation_portal';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { timelinePreview, trendsEnabled } from 'mastodon/initial_state';
|
||||
import { transientSingleColumn } from 'mastodon/is_mobile';
|
||||
|
||||
|
@ -99,12 +100,8 @@ const FollowRequestsLink = () => {
|
|||
};
|
||||
|
||||
class NavigationPanel extends Component {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
|
@ -114,7 +111,7 @@ class NavigationPanel extends Component {
|
|||
|
||||
render () {
|
||||
const { intl } = this.props;
|
||||
const { signedIn, disabledAccountId } = this.context.identity;
|
||||
const { signedIn, disabledAccountId } = this.props.identity;
|
||||
|
||||
let banner = undefined;
|
||||
|
||||
|
@ -194,4 +191,4 @@ class NavigationPanel extends Component {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(NavigationPanel);
|
||||
export default injectIntl(withIdentity(NavigationPanel));
|
||||
|
|
|
@ -15,6 +15,7 @@ import { focusApp, unfocusApp, changeLayout } from 'mastodon/actions/app';
|
|||
import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'mastodon/actions/markers';
|
||||
import { INTRODUCTION_VERSION } from 'mastodon/actions/onboarding';
|
||||
import { PictureInPicture } from 'mastodon/features/picture_in_picture';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { layoutFromWindow } from 'mastodon/is_mobile';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
|
@ -120,12 +121,8 @@ const keyMap = {
|
|||
};
|
||||
|
||||
class SwitchingColumnsArea extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
children: PropTypes.node,
|
||||
location: PropTypes.object,
|
||||
singleColumn: PropTypes.bool,
|
||||
|
@ -160,7 +157,7 @@ class SwitchingColumnsArea extends PureComponent {
|
|||
|
||||
render () {
|
||||
const { children, singleColumn } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
const pathName = this.props.location.pathname;
|
||||
|
||||
let redirect;
|
||||
|
@ -252,12 +249,8 @@ class SwitchingColumnsArea extends PureComponent {
|
|||
}
|
||||
|
||||
class UI extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
children: PropTypes.node,
|
||||
isComposing: PropTypes.bool,
|
||||
|
@ -309,7 +302,7 @@ class UI extends PureComponent {
|
|||
this.dragTargets.push(e.target);
|
||||
}
|
||||
|
||||
if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore && this.context.identity.signedIn) {
|
||||
if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore && this.props.identity.signedIn) {
|
||||
this.setState({ draggingOver: true });
|
||||
}
|
||||
};
|
||||
|
@ -337,7 +330,7 @@ class UI extends PureComponent {
|
|||
this.setState({ draggingOver: false });
|
||||
this.dragTargets = [];
|
||||
|
||||
if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore && this.context.identity.signedIn) {
|
||||
if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore && this.props.identity.signedIn) {
|
||||
this.props.dispatch(uploadCompose(e.dataTransfer.files));
|
||||
}
|
||||
};
|
||||
|
@ -389,7 +382,7 @@ class UI extends PureComponent {
|
|||
};
|
||||
|
||||
componentDidMount () {
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
window.addEventListener('focus', this.handleWindowFocus, false);
|
||||
window.addEventListener('blur', this.handleWindowBlur, false);
|
||||
|
@ -586,7 +579,7 @@ class UI extends PureComponent {
|
|||
<div className={classNames('ui', { 'is-composing': isComposing })} ref={this.setRef}>
|
||||
<Header />
|
||||
|
||||
<SwitchingColumnsArea location={location} singleColumn={layout === 'mobile' || layout === 'single-column'}>
|
||||
<SwitchingColumnsArea identity={this.props.identity} location={location} singleColumn={layout === 'mobile' || layout === 'single-column'}>
|
||||
{children}
|
||||
</SwitchingColumnsArea>
|
||||
|
||||
|
@ -602,4 +595,4 @@ class UI extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(withRouter(UI)));
|
||||
export default connect(mapStateToProps)(injectIntl(withRouter(withIdentity(UI))));
|
||||
|
|
74
app/javascript/mastodon/identity_context.tsx
Normal file
74
app/javascript/mastodon/identity_context.tsx
Normal file
|
@ -0,0 +1,74 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { createContext, useContext } from 'react';
|
||||
|
||||
import hoistStatics from 'hoist-non-react-statics';
|
||||
|
||||
import type { InitialState } from 'mastodon/initial_state';
|
||||
|
||||
export interface IdentityContextType {
|
||||
signedIn: boolean;
|
||||
accountId: string | undefined;
|
||||
disabledAccountId: string | undefined;
|
||||
accessToken: string | undefined;
|
||||
permissions: number;
|
||||
}
|
||||
|
||||
export const identityContextPropShape = PropTypes.shape({
|
||||
signedIn: PropTypes.bool.isRequired,
|
||||
accountId: PropTypes.string,
|
||||
disabledAccountId: PropTypes.string,
|
||||
accessToken: PropTypes.string,
|
||||
}).isRequired;
|
||||
|
||||
export const createIdentityContext = (state: InitialState) => ({
|
||||
signedIn: !!state.meta.me,
|
||||
accountId: state.meta.me,
|
||||
disabledAccountId: state.meta.disabled_account_id,
|
||||
accessToken: state.meta.access_token,
|
||||
permissions: state.role?.permissions ?? 0,
|
||||
});
|
||||
|
||||
export const IdentityContext = createContext<IdentityContextType>({
|
||||
signedIn: false,
|
||||
permissions: 0,
|
||||
accountId: undefined,
|
||||
disabledAccountId: undefined,
|
||||
accessToken: undefined,
|
||||
});
|
||||
|
||||
export const useIdentity = () => useContext(IdentityContext);
|
||||
|
||||
export interface IdentityProps {
|
||||
ref?: unknown;
|
||||
wrappedComponentRef?: unknown;
|
||||
}
|
||||
|
||||
/* Injects an `identity` props into the wrapped component to be able to use the new context in class components */
|
||||
export function withIdentity<
|
||||
ComponentType extends React.ComponentType<IdentityProps>,
|
||||
>(Component: ComponentType) {
|
||||
const displayName = `withIdentity(${Component.displayName ?? Component.name})`;
|
||||
const C = (props: React.ComponentProps<ComponentType>) => {
|
||||
const { wrappedComponentRef, ...remainingProps } = props;
|
||||
|
||||
return (
|
||||
<IdentityContext.Consumer>
|
||||
{(context) => {
|
||||
return (
|
||||
// @ts-expect-error - Dynamic covariant generic components are tough to type.
|
||||
<Component
|
||||
{...remainingProps}
|
||||
identity={context}
|
||||
ref={wrappedComponentRef}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</IdentityContext.Consumer>
|
||||
);
|
||||
};
|
||||
|
||||
C.displayName = displayName;
|
||||
C.WrappedComponent = Component;
|
||||
|
||||
return hoistStatics(C, Component);
|
||||
}
|
|
@ -44,12 +44,22 @@
|
|||
* @property {string} sso_redirect
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef Role
|
||||
* @property {string} id
|
||||
* @property {string} name
|
||||
* @property {string} permissions
|
||||
* @property {string} color
|
||||
* @property {boolean} highlighted
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef InitialState
|
||||
* @property {Record<string, import("./api_types/accounts").ApiAccountJSON>} accounts
|
||||
* @property {InitialStateLanguage[]} languages
|
||||
* @property {boolean=} critical_updates_pending
|
||||
* @property {InitialStateMeta} meta
|
||||
* @property {Role?} role
|
||||
*/
|
||||
|
||||
const element = document.getElementById('initial-state');
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"about.domain_blocks.no_reason_available": "السبب غير متوفر",
|
||||
"about.domain_blocks.preamble": "يسمح لك ماستدون عموماً بعرض المحتوى من المستخدمين من أي خادم آخر في الفدرالية والتفاعل معهم. وهذه هي الاستثناءات التي وضعت على هذا الخادم بالذات.",
|
||||
"about.domain_blocks.silenced.explanation": "عموماً، لن ترى ملفات التعريف والمحتوى من هذا الخادم، إلا إذا كنت تبحث عنه بشكل صريح أو تختار أن تتابعه.",
|
||||
"about.domain_blocks.silenced.title": "تم كتمه",
|
||||
"about.domain_blocks.silenced.title": "محدود",
|
||||
"about.domain_blocks.suspended.explanation": "لن يتم معالجة أي بيانات من هذا الخادم أو تخزينها أو تبادلها، مما يجعل أي تفاعل أو اتصال مع المستخدمين من هذا الخادم مستحيلا.",
|
||||
"about.domain_blocks.suspended.title": "مُعلّق",
|
||||
"about.not_available": "لم يتم توفير هذه المعلومات على هذا الخادم.",
|
||||
|
@ -21,7 +21,7 @@
|
|||
"account.blocked": "محظور",
|
||||
"account.browse_more_on_origin_server": "تصفح المزيد في الملف الشخصي الأصلي",
|
||||
"account.cancel_follow_request": "إلغاء طلب المتابعة",
|
||||
"account.copy": "نسخ الرابط إلى الملف الشخصي",
|
||||
"account.copy": "نسخ الرابط إلى الحساب",
|
||||
"account.direct": "إشارة خاصة لـ @{name}",
|
||||
"account.disable_notifications": "توقف عن إشعاري عندما ينشر @{name}",
|
||||
"account.domain_blocked": "اسم النِّطاق محظور",
|
||||
|
@ -32,7 +32,7 @@
|
|||
"account.featured_tags.last_status_never": "لا توجد رسائل",
|
||||
"account.featured_tags.title": "وسوم {name} المميَّزة",
|
||||
"account.follow": "متابعة",
|
||||
"account.follow_back": "تابعه بدورك",
|
||||
"account.follow_back": "رد المتابعة",
|
||||
"account.followers": "مُتابِعون",
|
||||
"account.followers.empty": "لا أحدَ يُتابع هذا المُستخدم إلى حد الآن.",
|
||||
"account.followers_counter": "{count, plural, zero{لا مُتابع} one {مُتابعٌ واحِد} two {مُتابعانِ اِثنان} few {{counter} مُتابِعين} many {{counter} مُتابِعًا} other {{counter} مُتابع}}",
|
||||
|
@ -89,12 +89,12 @@
|
|||
"announcement.announcement": "إعلان",
|
||||
"attachments_list.unprocessed": "(غير معالَج)",
|
||||
"audio.hide": "إخفاء المقطع الصوتي",
|
||||
"block_modal.remote_users_caveat": "Do t’i kërkojmë shërbyesit {domain} të respektojë vendimin tuaj. Por, pajtimi s’është i garantuar, ngaqë disa shërbyes mund t’i trajtojnë ndryshe bllokimet. Psotimet publike mundet të jenë ende të dukshme për përdorues pa bërë hyrje në llogari.",
|
||||
"block_modal.show_less": "اعرض أقلّ",
|
||||
"block_modal.remote_users_caveat": "سوف نطلب من الخادم {domain} أن يحترم قرارك، لكن الالتزام غير مضمون لأن بعض الخواديم قد تتعامل مع نصوص الكتل بشكل مختلف. قد تظل المنشورات العامة مرئية للمستخدمين غير المسجلين الدخول.",
|
||||
"block_modal.show_less": "أظهر الأقل",
|
||||
"block_modal.show_more": "أظهر المزيد",
|
||||
"block_modal.they_cant_mention": "لن يستطيع ذِكرك أو متابعتك.",
|
||||
"block_modal.they_cant_see_posts": "لن يستطيع رؤية منشوراتك ولن ترى منشوراته.",
|
||||
"block_modal.they_will_know": "يمكنه أن يرى أنه قد تم حجبه.",
|
||||
"block_modal.they_will_know": "يمكنه أن يرى أنه قد تم حظره.",
|
||||
"block_modal.title": "أتريد حظر المستخدم؟",
|
||||
"block_modal.you_wont_see_mentions": "لن تر المنشورات التي يُشار فيهم إليه.",
|
||||
"boost_modal.combo": "يُمكنك الضّغط على {combo} لتخطي هذا في المرة المُقبلة",
|
||||
|
@ -220,7 +220,7 @@
|
|||
"domain_pill.activitypub_lets_connect": "يتيح لك التواصل والتفاعل مع الناس ليس فقط على ماستدون، ولكن عبر تطبيقات اجتماعية مختلفة أيضا.",
|
||||
"domain_pill.activitypub_like_language": "إنّ ActivityPub مثل لغة ماستدون التي يتحدث بها مع شبكات اجتماعية أخرى.",
|
||||
"domain_pill.server": "الخادِم",
|
||||
"domain_pill.their_handle": "مُعرِّفُه:",
|
||||
"domain_pill.their_handle": "مُعرفه:",
|
||||
"domain_pill.their_server": "بيتهم الرقمي، حيث تُستضاف كافة منشوراتهم.",
|
||||
"domain_pill.their_username": "مُعرّفُهم الفريد على الخادم. من الممكن العثور على مستخدمين بنفس اسم المستخدم على خوادم مختلفة.",
|
||||
"domain_pill.username": "اسم المستخدم",
|
||||
|
@ -308,6 +308,8 @@
|
|||
"follow_suggestions.hints.similar_to_recently_followed": "هذا الملف الشخصي مشابه للملفات الشخصية التي تابعتها مؤخرا.",
|
||||
"follow_suggestions.personalized_suggestion": "توصية مخصصة",
|
||||
"follow_suggestions.popular_suggestion": "توصية رائجة",
|
||||
"follow_suggestions.popular_suggestion_longer": "رائج على {domain}",
|
||||
"follow_suggestions.similar_to_recently_followed_longer": "مشابهة لمواصفات الملفات الشخصية التي تابعتَها حديثًا",
|
||||
"follow_suggestions.view_all": "عرض الكل",
|
||||
"follow_suggestions.who_to_follow": "حسابات للمُتابَعة",
|
||||
"followed_tags": "الوسوم المتابَعة",
|
||||
|
@ -360,8 +362,8 @@
|
|||
"interaction_modal.title.reply": "الرد على منشور {name}",
|
||||
"intervals.full.days": "{number, plural, one {# يوم} other {# أيام}}",
|
||||
"intervals.full.hours": "{number, plural, one {# ساعة} other {# ساعات}}",
|
||||
"intervals.full.minutes": "{number, plural, one {# دقيقة} other {# دقائق}}",
|
||||
"keyboard_shortcuts.back": "للعودة",
|
||||
"intervals.full.minutes": "{number, plural, one {دقيقة واحدة}two {دقيقتان} other {# دقائق}}",
|
||||
"keyboard_shortcuts.back": "للرجوع",
|
||||
"keyboard_shortcuts.blocked": "لفتح قائمة المستخدمين المحظورين",
|
||||
"keyboard_shortcuts.boost": "لإعادة النشر",
|
||||
"keyboard_shortcuts.column": "للتركيز على منشور على أحد الأعمدة",
|
||||
|
@ -421,7 +423,9 @@
|
|||
"loading_indicator.label": "جاري التحميل…",
|
||||
"media_gallery.toggle_visible": "{number, plural, zero {} one {اخف الصورة} two {اخف الصورتين} few {اخف الصور} many {اخف الصور} other {اخف الصور}}",
|
||||
"moved_to_account_banner.text": "حسابك {disabledAccount} معطل حاليًا لأنك انتقلت إلى {movedToAccount}.",
|
||||
"mute_modal.hide_from_notifications": "إخفاء من قائمة الإشعارات",
|
||||
"mute_modal.hide_options": "إخفاء الخيارات",
|
||||
"mute_modal.indefinite": "إلى أن أفسخ كتمها",
|
||||
"mute_modal.show_options": "إظهار الخيارات",
|
||||
"mute_modal.they_can_mention_and_follow": "سيكون بإمكانه الإشارة إليك ومتابعتك، لكنك لن تره.",
|
||||
"mute_modal.they_wont_know": "لن يَعرف أنه قد تم كتمه.",
|
||||
|
@ -460,10 +464,20 @@
|
|||
"notification.follow": "يتابعك {name}",
|
||||
"notification.follow_request": "لقد طلب {name} متابعتك",
|
||||
"notification.mention": "{name} ذكرك",
|
||||
"notification.moderation-warning.learn_more": "اعرف المزيد",
|
||||
"notification.moderation_warning.action_disable": "تم تعطيل حسابك.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "بعض من منشوراتك تم تصنيفها على أنها حساسة.",
|
||||
"notification.moderation_warning.action_none": "لقد تلقى حسابك تحذيرا بالإشراف.",
|
||||
"notification.moderation_warning.action_sensitive": "سيتم وضع علامة على منشوراتك على أنها حساسة من الآن فصاعدا.",
|
||||
"notification.moderation_warning.action_suspend": "لقد تم تعليق حسابك.",
|
||||
"notification.own_poll": "انتهى استطلاعك للرأي",
|
||||
"notification.poll": "لقد انتهى استطلاع رأي شاركتَ فيه",
|
||||
"notification.reblog": "قام {name} بمشاركة منشورك",
|
||||
"notification.relationships_severance_event": "فقدت الاتصالات مع {name}",
|
||||
"notification.relationships_severance_event.account_suspension": "قام مشرف من {from} بتعليق {target}، مما يعني أنك لم يعد بإمكانك تلقي التحديثات منهم أو التفاعل معهم.",
|
||||
"notification.relationships_severance_event.domain_block": "قام مشرف من {from} بحظر {target}، بما في ذلك {followersCount} من متابعينك و {followingCount, plural, one {# حساب} other {# حسابات}} تتابعها.",
|
||||
"notification.relationships_severance_event.learn_more": "اعرف المزيد",
|
||||
"notification.relationships_severance_event.user_domain_block": "لقد قمت بحظر {target}، مما أدى إلى إزالة {followersCount} من متابعينك و {followingCount, plural, one {# حساب} other {# حسابات}} تتابعها.",
|
||||
"notification.status": "{name} نشر للتو",
|
||||
"notification.update": "عدّلَ {name} منشورًا",
|
||||
"notification_requests.accept": "موافقة",
|
||||
|
@ -503,10 +517,15 @@
|
|||
"notifications.permission_denied": "تنبيهات سطح المكتب غير متوفرة بسبب رفض أذونات المتصفح مسبقاً",
|
||||
"notifications.permission_denied_alert": "لا يمكن تفعيل إشعارات سطح المكتب، لأن إذن المتصفح قد تم رفضه سابقاً",
|
||||
"notifications.permission_required": "إشعارات سطح المكتب غير متوفرة لأنه لم يتم منح الإذن المطلوب.",
|
||||
"notifications.policy.filter_new_accounts.hint": "تم إنشاؤها منذ {days, plural, zero {}one {يوم واحد} two {يومان} few {# أيام} many {# أيام} other {# أيام}}",
|
||||
"notifications.policy.filter_new_accounts_title": "حسابات جديدة",
|
||||
"notifications.policy.filter_not_followers_hint": "بما في ذلك الأشخاص الذين يتابعونك أقل من {days, plural, zero {}one {يوم واحد} two {يومان} few {# أيام} many {# أيام} other {# أيام}}",
|
||||
"notifications.policy.filter_not_followers_title": "أشخاص لا يتابعونك",
|
||||
"notifications.policy.filter_not_following_hint": "حتى توافق عليهم يدويا",
|
||||
"notifications.policy.filter_not_following_title": "أشخاص لا تتابعهم",
|
||||
"notifications.policy.filter_private_mentions_hint": "تمت تصفيته إلا إذا أن يكون ردًا على ذكرك أو إذا كنت تتابع الحساب",
|
||||
"notifications.policy.filter_private_mentions_title": "إشارات خاصة غير مرغوب فيها",
|
||||
"notifications.policy.title": "تصفية الإشعارات من…",
|
||||
"notifications_permission_banner.enable": "تفعيل إشعارات سطح المكتب",
|
||||
"notifications_permission_banner.how_to_control": "لتلقي الإشعارات عندما لا يكون ماستدون مفتوح، قم بتفعيل إشعارات سطح المكتب، يمكنك التحكم بدقة في أنواع التفاعلات التي تولد إشعارات سطح المكتب من خلال زر الـ{icon} أعلاه بمجرد تفعيلها.",
|
||||
"notifications_permission_banner.title": "لا تفوت شيئاً أبداً",
|
||||
|
@ -687,6 +706,7 @@
|
|||
"status.edited_x_times": "عُدّل {count, plural, zero {} one {مرةً واحدة} two {مرّتان} few {{count} مرات} many {{count} مرة} other {{count} مرة}}",
|
||||
"status.embed": "إدماج",
|
||||
"status.favourite": "فضّل",
|
||||
"status.favourites": "{count, plural, zero {}one {مفضلة واحدة} two {مفضلتان} few {# مفضلات} many {# مفضلات} other {# مفضلات}}",
|
||||
"status.filter": "تصفية هذه الرسالة",
|
||||
"status.filtered": "مُصفّى",
|
||||
"status.hide": "إخفاء المنشور",
|
||||
|
@ -707,6 +727,7 @@
|
|||
"status.reblog": "إعادة النشر",
|
||||
"status.reblog_private": "إعادة النشر إلى الجمهور الأصلي",
|
||||
"status.reblogged_by": "شارَكَه {name}",
|
||||
"status.reblogs": "{count, plural, one {تعزيز واحد} two {تعزيزتان} few {# تعزيزات} many {# تعزيزات} other {# تعزيزات}}",
|
||||
"status.reblogs.empty": "لم يقم أي أحد بمشاركة هذا المنشور بعد. عندما يقوم أحدهم بذلك سوف يظهر هنا.",
|
||||
"status.redraft": "إزالة وإعادة الصياغة",
|
||||
"status.remove_bookmark": "احذفه مِن الفواصل المرجعية",
|
||||
|
|
|
@ -318,7 +318,7 @@
|
|||
"follow_suggestions.personalized_suggestion": "Suggeriment personalitzat",
|
||||
"follow_suggestions.popular_suggestion": "Suggeriment popular",
|
||||
"follow_suggestions.popular_suggestion_longer": "Popular a {domain}",
|
||||
"follow_suggestions.similar_to_recently_followed_longer": "Semblant a perfils que seguiu fa poc",
|
||||
"follow_suggestions.similar_to_recently_followed_longer": "Semblant a perfils que seguiu de fa poc",
|
||||
"follow_suggestions.view_all": "Mostra-ho tot",
|
||||
"follow_suggestions.who_to_follow": "A qui seguir",
|
||||
"followed_tags": "Etiquetes seguides",
|
||||
|
@ -473,6 +473,7 @@
|
|||
"notification.follow": "{name} et segueix",
|
||||
"notification.follow_request": "{name} ha sol·licitat de seguir-te",
|
||||
"notification.mention": "{name} t'ha esmentat",
|
||||
"notification.moderation-warning.learn_more": "Per a saber-ne més",
|
||||
"notification.moderation_warning": "Heu rebut un avís de moderació",
|
||||
"notification.moderation_warning.action_delete_statuses": "S'han eliminat algunes de les vostres publicacions.",
|
||||
"notification.moderation_warning.action_disable": "S'ha desactivat el vostre compte.",
|
||||
|
|
|
@ -474,7 +474,7 @@
|
|||
"notification.follow_request": "Mae {name} wedi gwneud cais i'ch dilyn",
|
||||
"notification.mention": "Crybwyllodd {name} amdanoch chi",
|
||||
"notification.moderation-warning.learn_more": "Dysgu mwy",
|
||||
"notification.moderation_warning": "Rydych wedi derbyn rhybudd cymedroli",
|
||||
"notification.moderation_warning": "Rydych wedi derbyn rhybudd gan gymedrolwr",
|
||||
"notification.moderation_warning.action_delete_statuses": "Mae rhai o'ch postiadau wedi'u dileu.",
|
||||
"notification.moderation_warning.action_disable": "Mae eich cyfrif wedi'i analluogi.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Mae rhai o'ch postiadau wedi'u marcio'n sensitif.",
|
||||
|
|
|
@ -331,7 +331,7 @@
|
|||
"footer.source_code": "Quellcode anzeigen",
|
||||
"footer.status": "Status",
|
||||
"generic.saved": "Gespeichert",
|
||||
"getting_started.heading": "Auf geht’s!",
|
||||
"getting_started.heading": "Auf gehts!",
|
||||
"hashtag.column_header.tag_mode.all": "und {additional}",
|
||||
"hashtag.column_header.tag_mode.any": "oder {additional}",
|
||||
"hashtag.column_header.tag_mode.none": "ohne {additional}",
|
||||
|
@ -400,7 +400,7 @@
|
|||
"keyboard_shortcuts.requests": "Liste der Follower-Anfragen aufrufen",
|
||||
"keyboard_shortcuts.search": "Suchleiste fokussieren",
|
||||
"keyboard_shortcuts.spoilers": "Feld für Inhaltswarnung anzeigen/ausblenden",
|
||||
"keyboard_shortcuts.start": "„Auf geht’s!“ öffnen",
|
||||
"keyboard_shortcuts.start": "„Auf gehts!“ öffnen",
|
||||
"keyboard_shortcuts.toggle_hidden": "Beitragstext hinter der Inhaltswarnung anzeigen/ausblenden",
|
||||
"keyboard_shortcuts.toggle_sensitivity": "Medien anzeigen/ausblenden",
|
||||
"keyboard_shortcuts.toot": "Neuen Beitrag erstellen",
|
||||
|
|
|
@ -308,6 +308,8 @@
|
|||
"follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
|
||||
"follow_suggestions.curated_suggestion": "Staff pick",
|
||||
"follow_suggestions.dismiss": "Don't show again",
|
||||
"follow_suggestions.featured_longer": "Hand-picked by the {domain} team",
|
||||
"follow_suggestions.friends_of_friends_longer": "Popular among people you follow",
|
||||
"follow_suggestions.hints.featured": "This profile has been hand-picked by the {domain} team.",
|
||||
"follow_suggestions.hints.friends_of_friends": "This profile is popular among the people you follow.",
|
||||
"follow_suggestions.hints.most_followed": "This profile is one of the most followed on {domain}.",
|
||||
|
@ -315,6 +317,8 @@
|
|||
"follow_suggestions.hints.similar_to_recently_followed": "This profile is similar to the profiles you have most recently followed.",
|
||||
"follow_suggestions.personalized_suggestion": "Personalised suggestion",
|
||||
"follow_suggestions.popular_suggestion": "Popular suggestion",
|
||||
"follow_suggestions.popular_suggestion_longer": "Popular on {domain}",
|
||||
"follow_suggestions.similar_to_recently_followed_longer": "Similar to profiles you recently followed",
|
||||
"follow_suggestions.view_all": "View all",
|
||||
"follow_suggestions.who_to_follow": "Who to follow",
|
||||
"followed_tags": "Followed hashtags",
|
||||
|
@ -469,6 +473,15 @@
|
|||
"notification.follow": "{name} followed you",
|
||||
"notification.follow_request": "{name} has requested to follow you",
|
||||
"notification.mention": "{name} mentioned you",
|
||||
"notification.moderation-warning.learn_more": "Learn more",
|
||||
"notification.moderation_warning": "You have received a moderation warning",
|
||||
"notification.moderation_warning.action_delete_statuses": "Some of your posts have been removed.",
|
||||
"notification.moderation_warning.action_disable": "Your account has been disabled.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Some of your posts have been marked as sensitive.",
|
||||
"notification.moderation_warning.action_none": "Your account has received a moderation warning.",
|
||||
"notification.moderation_warning.action_sensitive": "Your posts will be marked as sensitive from now on.",
|
||||
"notification.moderation_warning.action_silence": "Your account has been limited.",
|
||||
"notification.moderation_warning.action_suspend": "Your account has been suspended.",
|
||||
"notification.own_poll": "Your poll has ended",
|
||||
"notification.poll": "A poll you have voted in has ended",
|
||||
"notification.reblog": "{name} boosted your status",
|
||||
|
|
|
@ -474,7 +474,7 @@
|
|||
"notification.follow_request": "{name} has requested to follow you",
|
||||
"notification.mention": "{name} mentioned you",
|
||||
"notification.moderation-warning.learn_more": "Learn more",
|
||||
"notification.moderation_warning": "Your have received a moderation warning",
|
||||
"notification.moderation_warning": "You have received a moderation warning",
|
||||
"notification.moderation_warning.action_delete_statuses": "Some of your posts have been removed.",
|
||||
"notification.moderation_warning.action_disable": "Your account has been disabled.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Some of your posts have been marked as sensitive.",
|
||||
|
|
|
@ -476,12 +476,12 @@
|
|||
"notification.moderation-warning.learn_more": "Saber más",
|
||||
"notification.moderation_warning": "Has recibido una advertencia de moderación",
|
||||
"notification.moderation_warning.action_delete_statuses": "Se han eliminado algunas de tus publicaciones.",
|
||||
"notification.moderation_warning.action_disable": "Se ha desactivado su cuenta.",
|
||||
"notification.moderation_warning.action_disable": "Tu cuenta ha sido desactivada.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Se han marcado como sensibles algunas de tus publicaciones.",
|
||||
"notification.moderation_warning.action_none": "Tu cuenta ha recibido un aviso de moderación.",
|
||||
"notification.moderation_warning.action_sensitive": "De ahora en adelante, todas tus publicaciones se marcarán como sensibles.",
|
||||
"notification.moderation_warning.action_silence": "Se ha limitado tu cuenta.",
|
||||
"notification.moderation_warning.action_suspend": "Se ha suspendido tu cuenta.",
|
||||
"notification.moderation_warning.action_silence": "Tu cuenta ha sido limitada.",
|
||||
"notification.moderation_warning.action_suspend": "Tu cuenta ha sido suspendida.",
|
||||
"notification.own_poll": "Tu encuesta ha terminado",
|
||||
"notification.poll": "Una encuesta en la que has votado ha terminado",
|
||||
"notification.reblog": "{name} ha retooteado tu estado",
|
||||
|
|
|
@ -476,12 +476,12 @@
|
|||
"notification.moderation-warning.learn_more": "Saber más",
|
||||
"notification.moderation_warning": "Has recibido una advertencia de moderación",
|
||||
"notification.moderation_warning.action_delete_statuses": "Se han eliminado algunas de tus publicaciones.",
|
||||
"notification.moderation_warning.action_disable": "Se ha desactivado su cuenta.",
|
||||
"notification.moderation_warning.action_disable": "Tu cuenta ha sido desactivada.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Se han marcado como sensibles algunas de tus publicaciones.",
|
||||
"notification.moderation_warning.action_none": "Tu cuenta ha recibido un aviso de moderación.",
|
||||
"notification.moderation_warning.action_sensitive": "De ahora en adelante, todas tus publicaciones se marcarán como sensibles.",
|
||||
"notification.moderation_warning.action_silence": "Se ha limitado tu cuenta.",
|
||||
"notification.moderation_warning.action_suspend": "Se ha suspendido tu cuenta.",
|
||||
"notification.moderation_warning.action_silence": "Tu cuenta ha sido limitada.",
|
||||
"notification.moderation_warning.action_suspend": "Tu cuenta ha sido suspendida.",
|
||||
"notification.own_poll": "Tu encuesta ha terminado",
|
||||
"notification.poll": "Una encuesta en la que has votado ha terminado",
|
||||
"notification.reblog": "{name} ha impulsado tu publicación",
|
||||
|
|
|
@ -298,7 +298,7 @@
|
|||
"filter_modal.select_filter.title": "Suodata tämä julkaisu",
|
||||
"filter_modal.title.status": "Suodata julkaisu",
|
||||
"filtered_notifications_banner.mentions": "{count, plural, one {maininta} other {mainintaa}}",
|
||||
"filtered_notifications_banner.pending_requests": "Sinulle on ilmoituksia mahdollisesti tuntemiltasi henkilöiltä seuraavasti: {count, plural, =0 {Ei keltään} one {Yhdeltä henkilöltä} other {# henkilöltä}}",
|
||||
"filtered_notifications_banner.pending_requests": "Ilmoituksia {count, plural, =0 {ei ole} one {1 henkilöltä} other {# henkilöltä}}, jonka saatat tuntea",
|
||||
"filtered_notifications_banner.title": "Suodatetut ilmoitukset",
|
||||
"firehose.all": "Kaikki",
|
||||
"firehose.local": "Tämä palvelin",
|
||||
|
@ -308,7 +308,7 @@
|
|||
"follow_requests.unlocked_explanation": "Vaikkei tiliäsi ole lukittu, palvelimen {domain} ylläpito on arvioinut, että saatat olla halukas tarkistamaan nämä seuraamispyynnöt erikseen.",
|
||||
"follow_suggestions.curated_suggestion": "Ehdotus ylläpidolta",
|
||||
"follow_suggestions.dismiss": "Älä näytä uudelleen",
|
||||
"follow_suggestions.featured_longer": "Käsin valinnut palvelimen {domain} tiimi",
|
||||
"follow_suggestions.featured_longer": "Valinnut käsin palvelimen {domain} tiimi",
|
||||
"follow_suggestions.friends_of_friends_longer": "Suosittu seuraamiesi ihmisten keskuudessa",
|
||||
"follow_suggestions.hints.featured": "Tämän profiilin on valinnut palvelimen {domain} tiimi.",
|
||||
"follow_suggestions.hints.friends_of_friends": "Seuraamasi käyttäjät suosivat tätä profiilia.",
|
||||
|
|
|
@ -50,6 +50,8 @@
|
|||
"admin.dashboard.retention.cohort_size": "Mga bagong tagagamit",
|
||||
"alert.rate_limited.message": "Mangyaring subukan muli pagkatapos ng {retry_time, time, medium}.",
|
||||
"audio.hide": "Itago ang tunog",
|
||||
"block_modal.show_less": "Magpakita ng mas kaunti",
|
||||
"block_modal.show_more": "Magpakita ng higit pa",
|
||||
"block_modal.title": "Harangan ang tagagamit?",
|
||||
"bundle_column_error.error.title": "Naku!",
|
||||
"bundle_column_error.network.body": "Nagkaroon ng kamalian habang sinusubukang i-karga ang pahinang ito. Maaaring dahil ito sa pansamantalang problema ng iyong koneksyon sa internet o ang server na ito.",
|
||||
|
@ -102,6 +104,7 @@
|
|||
"compose_form.encryption_warning": "Ang mga post sa Mastodon ay hindi naka-encrypt nang dulo-dulo. Huwag magbahagi ng anumang sensitibong impormasyon sa Mastodon.",
|
||||
"compose_form.hashtag_warning": "Hindi maililista ang post na ito sa anumang hashtag dahil hindi ito nakapubliko. Mga nakapublikong post lamang ang mahahanap ayon sa hashtag.",
|
||||
"compose_form.placeholder": "Anong nangyari?",
|
||||
"compose_form.poll.duration": "Tagal ng botohan",
|
||||
"compose_form.poll.multiple": "Maraming pagpipilian",
|
||||
"compose_form.poll.single": "Piliin ang isa",
|
||||
"compose_form.reply": "Tumugon",
|
||||
|
@ -173,6 +176,7 @@
|
|||
"empty_column.list": "Wala pang laman ang listahang ito. Kapag naglathala ng mga bagong post ang mga miyembro ng listahang ito, makikita iyon dito.",
|
||||
"empty_column.lists": "Wala ka pang mga listahan. Kapag gumawa ka ng isa, makikita yun dito.",
|
||||
"explore.search_results": "Mga resulta ng paghahanap",
|
||||
"explore.suggested_follows": "Mga tao",
|
||||
"explore.title": "Tuklasin",
|
||||
"explore.trending_links": "Mga balita",
|
||||
"filter_modal.select_filter.search": "Hanapin o gumawa",
|
||||
|
@ -186,9 +190,13 @@
|
|||
"follow_suggestions.who_to_follow": "Sinong maaaring sundan",
|
||||
"footer.about": "Tungkol dito",
|
||||
"footer.get_app": "Kunin ang app",
|
||||
"footer.status": "Katayuan",
|
||||
"generic.saved": "Nakaimbak",
|
||||
"hashtag.column_header.tag_mode.all": "at {additional}",
|
||||
"hashtag.column_header.tag_mode.any": "o {additional}",
|
||||
"hashtag.column_settings.tag_mode.all": "Lahat ng nandito",
|
||||
"hashtag.column_settings.tag_mode.any": "Ilan sa nandito",
|
||||
"hashtag.column_settings.tag_mode.none": "Wala dito",
|
||||
"home.column_settings.show_replies": "Ipakita ang mga tugon",
|
||||
"home.pending_critical_update.body": "Mangyaring i-update ang iyong serbiro ng Mastodon sa lalong madaling panahon!",
|
||||
"interaction_modal.login.action": "Iuwi mo ako",
|
||||
|
@ -199,6 +207,7 @@
|
|||
"intervals.full.days": "{number, plural, one {# araw} other {# na araw}}",
|
||||
"intervals.full.hours": "{number, plural, one {# oras} other {# na oras}}",
|
||||
"intervals.full.minutes": "{number, plural, one {# minuto} other {# na minuto}}",
|
||||
"keyboard_shortcuts.blocked": "Buksan ang talaan ng mga nakaharang na mga tagagamit",
|
||||
"keyboard_shortcuts.description": "Paglalarawan",
|
||||
"keyboard_shortcuts.down": "Ilipat pababa sa talaan",
|
||||
"keyboard_shortcuts.mention": "Banggitin ang may-akda",
|
||||
|
@ -218,7 +227,10 @@
|
|||
"navigation_bar.about": "Tungkol dito",
|
||||
"navigation_bar.blocks": "Nakaharang na mga tagagamit",
|
||||
"navigation_bar.direct": "Mga palihim na banggit",
|
||||
"navigation_bar.discover": "Tuklasin",
|
||||
"navigation_bar.explore": "Tuklasin",
|
||||
"navigation_bar.favourites": "Mga paborito",
|
||||
"navigation_bar.follow_requests": "Mga hiling sa pagsunod",
|
||||
"navigation_bar.follows_and_followers": "Mga sinusundan at tagasunod",
|
||||
"navigation_bar.lists": "Mga listahan",
|
||||
"navigation_bar.search": "Maghanap",
|
||||
|
@ -226,6 +238,7 @@
|
|||
"notification.follow": "Sinundan ka ni {name}",
|
||||
"notification.follow_request": "Hinihiling ni {name} na sundan ka",
|
||||
"notification.mention": "Binanggit ka ni {name}",
|
||||
"notification.moderation_warning": "Mayroong kang natanggap na babala sa pagtitimpi",
|
||||
"notification.relationships_severance_event.learn_more": "Matuto nang higit pa",
|
||||
"notification_requests.accept": "Tanggapin",
|
||||
"notification_requests.notifications_from": "Mga abiso mula kay/sa {name}",
|
||||
|
@ -246,6 +259,7 @@
|
|||
"onboarding.profile.note_hint": "Maaari mong @bangitin ang ibang mga tao o mga #hashtag…",
|
||||
"onboarding.profile.save_and_continue": "Iimbak at magpatuloy",
|
||||
"onboarding.share.next_steps": "Mga posibleng susunod na hakbang:",
|
||||
"picture_in_picture.restore": "Ilagay ito pabalik",
|
||||
"poll.closed": "Sarado",
|
||||
"poll.reveal": "Ipakita ang mga resulta",
|
||||
"poll.voted": "Binoto mo para sa sagot na ito",
|
||||
|
|
|
@ -474,11 +474,11 @@
|
|||
"notification.follow_request": "{name} biður um at fylgja tær",
|
||||
"notification.mention": "{name} nevndi teg",
|
||||
"notification.moderation-warning.learn_more": "Lær meira",
|
||||
"notification.moderation_warning": "Tú hevur móttikið eina umsjónarávarðing",
|
||||
"notification.moderation_warning": "Tú hevur móttikið eina umsjónarávaring",
|
||||
"notification.moderation_warning.action_delete_statuses": "Onkrir av tínum postum eru strikaðir.",
|
||||
"notification.moderation_warning.action_disable": "Konta tín er gjørd óvirkin.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Nakrir av postum tínum eru merktir sum viðkvæmir.",
|
||||
"notification.moderation_warning.action_none": "Konta tín hevur móttikið eina umsjónarávarðing.",
|
||||
"notification.moderation_warning.action_none": "Konta tín hevur móttikið eina umsjónarávaring.",
|
||||
"notification.moderation_warning.action_sensitive": "Postar tínir verða merktir sum viðkvæmir frá nú av.",
|
||||
"notification.moderation_warning.action_silence": "Konta tín er avmarkað.",
|
||||
"notification.moderation_warning.action_suspend": "Konta tín er ógildað.",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"about.blocks": "Servidores suxeitos a moderación",
|
||||
"about.contact": "Contacto:",
|
||||
"about.disclaimer": "Mastodon é software libre, de código aberto, e unha marca comercial de Mastodon gGmbH.",
|
||||
"about.domain_blocks.no_reason_available": "Motivo non indicado. ",
|
||||
"about.domain_blocks.no_reason_available": "Motivo non indicado",
|
||||
"about.domain_blocks.preamble": "Mastodon de xeito xeral permíteche ver contidos doutros servidores do fediverso e interactuar coas súas usuarias. Estas son as excepcións que se estabeleceron neste servidor en particular.",
|
||||
"about.domain_blocks.silenced.explanation": "Por defecto non verás perfís e contido desde este servidor, a menos que mires de xeito explícito ou optes por seguir ese contido ou usuaria.",
|
||||
"about.domain_blocks.silenced.title": "Limitado",
|
||||
|
@ -92,7 +92,7 @@
|
|||
"block_modal.remote_users_caveat": "Ímoslle pedir ao servidor {domain} que respecte a túa decisión. Emporiso, non hai garantía de que atenda a petición xa que os servidores xestionan os bloqueos de formas diferentes. As publicacións públicas poderían aínda ser visibles para usuarias que non iniciaron sesión.",
|
||||
"block_modal.show_less": "Mostrar menos",
|
||||
"block_modal.show_more": "Mostrar máis",
|
||||
"block_modal.they_cant_mention": "Non te pode seguir nin mencionar.",
|
||||
"block_modal.they_cant_mention": "Non te poden seguir nin mencionar.",
|
||||
"block_modal.they_cant_see_posts": "Non pode ver as túas publicacións nin ti as de ela.",
|
||||
"block_modal.they_will_know": "Pode ver que a bloqueaches.",
|
||||
"block_modal.title": "Bloquear usuaria?",
|
||||
|
@ -115,7 +115,7 @@
|
|||
"closed_registrations_modal.find_another_server": "Atopa outro servidor",
|
||||
"closed_registrations_modal.preamble": "Mastodon é descentralizado, así que non importa onde crees a conta, poderás seguir e interactuar con calquera conta deste servidor. Incluso podes ter o teu servidor!",
|
||||
"closed_registrations_modal.title": "Crear conta en Mastodon",
|
||||
"column.about": "Acerca de",
|
||||
"column.about": "Sobre",
|
||||
"column.blocks": "Usuarias bloqueadas",
|
||||
"column.bookmarks": "Marcadores",
|
||||
"column.community": "Cronoloxía local",
|
||||
|
@ -322,7 +322,7 @@
|
|||
"follow_suggestions.view_all": "Ver todas",
|
||||
"follow_suggestions.who_to_follow": "A quen seguir",
|
||||
"followed_tags": "Cancelos seguidos",
|
||||
"footer.about": "Acerca de",
|
||||
"footer.about": "Sobre",
|
||||
"footer.directory": "Directorio de perfís",
|
||||
"footer.get_app": "Descarga a app",
|
||||
"footer.invite": "Convidar persoas",
|
||||
|
@ -441,7 +441,7 @@
|
|||
"mute_modal.title": "Acalar usuaria?",
|
||||
"mute_modal.you_wont_see_mentions": "Non verás as publicacións que a mencionen.",
|
||||
"mute_modal.you_wont_see_posts": "Seguirá podendo ler as túas publicacións, pero non verás as súas.",
|
||||
"navigation_bar.about": "Acerca de",
|
||||
"navigation_bar.about": "Sobre",
|
||||
"navigation_bar.advanced_interface": "Abrir coa interface web avanzada",
|
||||
"navigation_bar.blocks": "Usuarias bloqueadas",
|
||||
"navigation_bar.bookmarks": "Marcadores",
|
||||
|
|
|
@ -205,6 +205,10 @@
|
|||
"dismissable_banner.dismiss": "डिसमिस",
|
||||
"dismissable_banner.explore_links": "इन समाचारों के बारे में लोगों द्वारा इस पर और डेसेंट्रलीसेड नेटवर्क के अन्य सर्वरों पर अभी बात की जा रही है।",
|
||||
"dismissable_banner.explore_tags": "ये हैशटैग अभी इस पर और डेसेंट्रलीसेड नेटवर्क के अन्य सर्वरों पर लोगों के बीच कर्षण प्राप्त कर रहे हैं।",
|
||||
"domain_block_modal.block": "सर्वर ब्लॉक करें",
|
||||
"domain_block_modal.title": "डोमेन ब्लॉक करें",
|
||||
"domain_pill.server": "सर्वर",
|
||||
"domain_pill.username": "यूज़रनेम",
|
||||
"embed.instructions": "अपने वेबसाइट पर, निचे दिए कोड को कॉपी करके, इस स्टेटस को एम्बेड करें",
|
||||
"embed.preview": "यह ऐसा दिखेगा :",
|
||||
"emoji_button.activity": "गतिविधि",
|
||||
|
@ -274,6 +278,7 @@
|
|||
"follow_request.authorize": "अधिकार दें",
|
||||
"follow_request.reject": "अस्वीकार करें",
|
||||
"follow_requests.unlocked_explanation": "हालाँकि आपका खाता लॉक नहीं है, फिर भी {domain} डोमेन स्टाफ ने सोचा कि आप इन खातों के मैन्युअल अनुरोधों की समीक्षा करना चाहते हैं।",
|
||||
"follow_suggestions.dismiss": "दोबारा न दिखाएं",
|
||||
"followed_tags": "फॉलो किए गए हैशटैग्स",
|
||||
"footer.about": "अबाउट",
|
||||
"footer.directory": "प्रोफाइल्स डायरेक्टरी",
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
"account.block_domain": "Blocar dominio {domain}",
|
||||
"account.block_short": "Blocar",
|
||||
"account.blocked": "Blocate",
|
||||
"account.browse_more_on_origin_server": "Navigar plus sur le profilo original",
|
||||
"account.browse_more_on_origin_server": "Percurrer plus sur le profilo original",
|
||||
"account.cancel_follow_request": "Cancellar sequimento",
|
||||
"account.copy": "Copiar ligamine a profilo",
|
||||
"account.direct": "Mentionar privatemente @{name}",
|
||||
|
@ -122,7 +122,7 @@
|
|||
"column.direct": "Mentiones private",
|
||||
"column.directory": "Navigar profilos",
|
||||
"column.domain_blocks": "Dominios blocate",
|
||||
"column.favourites": "Favoritos",
|
||||
"column.favourites": "Favorites",
|
||||
"column.firehose": "Fluxos in directo",
|
||||
"column.follow_requests": "Requestas de sequimento",
|
||||
"column.home": "Initio",
|
||||
|
@ -204,7 +204,7 @@
|
|||
"disabled_account_banner.account_settings": "Parametros de conto",
|
||||
"disabled_account_banner.text": "Tu conto {disabledAccount} es actualmente disactivate.",
|
||||
"dismissable_banner.community_timeline": "Ecce le messages public le plus recente del personas con contos sur {domain}.",
|
||||
"dismissable_banner.dismiss": "Dimitter",
|
||||
"dismissable_banner.dismiss": "Clauder",
|
||||
"dismissable_banner.explore_links": "Istes es le articulos de novas que se condivide le plus sur le rete social hodie. Le articulos de novas le plus recente, publicate per plus personas differente, se classifica plus in alto.",
|
||||
"dismissable_banner.explore_statuses": "Ecce le messages de tote le rete social que gania popularitate hodie. Le messages plus nove con plus impulsos e favorites se classifica plus in alto.",
|
||||
"dismissable_banner.explore_tags": "Ecce le hashtags que gania popularitate sur le rete social hodie. Le hashtags usate per plus personas differente se classifica plus in alto.",
|
||||
|
@ -212,8 +212,8 @@
|
|||
"domain_block_modal.block": "Blocar le servitor",
|
||||
"domain_block_modal.block_account_instead": "Blocar @{name} in su loco",
|
||||
"domain_block_modal.they_can_interact_with_old_posts": "Le personas de iste servitor pote interager con tu messages ancian.",
|
||||
"domain_block_modal.they_cant_follow": "Nulle persona ab iste servitor pote sequer te.",
|
||||
"domain_block_modal.they_wont_know": "Illes non sapera que illes ha essite blocate.",
|
||||
"domain_block_modal.they_cant_follow": "Necuno de iste servitor pote sequer te.",
|
||||
"domain_block_modal.they_wont_know": "Ille non sapera que ille ha essite blocate.",
|
||||
"domain_block_modal.title": "Blocar dominio?",
|
||||
"domain_block_modal.you_will_lose_followers": "Omne sequitores ab iste servitor essera removite.",
|
||||
"domain_block_modal.you_wont_see_posts": "Tu non videra messages e notificationes ab usatores sur iste servitor.",
|
||||
|
@ -307,7 +307,9 @@
|
|||
"follow_request.reject": "Rejectar",
|
||||
"follow_requests.unlocked_explanation": "Benque tu conto non es serrate, le personal de {domain} pensa que es un bon idea que tu revide manualmente le sequente requestas de iste contos.",
|
||||
"follow_suggestions.curated_suggestion": "Selection del equipa",
|
||||
"follow_suggestions.dismiss": "Non monstrar novemente",
|
||||
"follow_suggestions.dismiss": "Non monstrar de novo",
|
||||
"follow_suggestions.featured_longer": "Seligite con cura per le equipa de {domain}",
|
||||
"follow_suggestions.friends_of_friends_longer": "Popular inter le gente que tu seque",
|
||||
"follow_suggestions.hints.featured": "Iste profilo ha essite seligite manualmente per le equipa de {domain}.",
|
||||
"follow_suggestions.hints.friends_of_friends": "Iste profilo es popular inter le gente que tu seque.",
|
||||
"follow_suggestions.hints.most_followed": "Iste profilo es un del plus sequites sur {domain}.",
|
||||
|
@ -315,6 +317,8 @@
|
|||
"follow_suggestions.hints.similar_to_recently_followed": "Iste profilo es similar al profilos que tu ha recentemente sequite.",
|
||||
"follow_suggestions.personalized_suggestion": "Suggestion personalisate",
|
||||
"follow_suggestions.popular_suggestion": "Suggestion personalisate",
|
||||
"follow_suggestions.popular_suggestion_longer": "Popular sur {domain}",
|
||||
"follow_suggestions.similar_to_recently_followed_longer": "Similar al profilos que tu ha sequite recentemente",
|
||||
"follow_suggestions.view_all": "Vider toto",
|
||||
"follow_suggestions.who_to_follow": "Qui sequer",
|
||||
"followed_tags": "Hashtags sequite",
|
||||
|
@ -408,7 +412,7 @@
|
|||
"lightbox.next": "Sequente",
|
||||
"lightbox.previous": "Precedente",
|
||||
"limited_account_hint.action": "Monstrar profilo in omne caso",
|
||||
"limited_account_hint.title": "Iste profilo esseva celate per le moderatores de {domain}.",
|
||||
"limited_account_hint.title": "Iste profilo ha essite celate per le moderatores de {domain}.",
|
||||
"link_preview.author": "Per {name}",
|
||||
"lists.account.add": "Adder al lista",
|
||||
"lists.account.remove": "Remover del lista",
|
||||
|
@ -428,12 +432,12 @@
|
|||
"loading_indicator.label": "Cargante…",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Celar imagine} other {Celar imagines}}",
|
||||
"moved_to_account_banner.text": "Tu conto {disabledAccount} es actualmente disactivate perque tu ha cambiate de conto a {movedToAccount}.",
|
||||
"mute_modal.hide_from_notifications": "Celar ab notificationes",
|
||||
"mute_modal.hide_from_notifications": "Celar in notificationes",
|
||||
"mute_modal.hide_options": "Celar optiones",
|
||||
"mute_modal.indefinite": "Usque io dissilentia iste persona",
|
||||
"mute_modal.show_options": "Monstrar optiones",
|
||||
"mute_modal.they_can_mention_and_follow": "Illes pote mentionar te e sequer te, ma tu non potera vider los.",
|
||||
"mute_modal.they_wont_know": "Illes non sapera que illes ha essite silentiate.",
|
||||
"mute_modal.they_can_mention_and_follow": "Ille pote mentionar te e sequer te, ma tu non potera vider le.",
|
||||
"mute_modal.they_wont_know": "Ille non sapera que ille ha essite silentiate.",
|
||||
"mute_modal.title": "Silentiar le usator?",
|
||||
"mute_modal.you_wont_see_mentions": "Tu non videra le messages que mentiona iste persona.",
|
||||
"mute_modal.you_wont_see_posts": "Iste persona pote totevia vider tu messages, ma tu non videra le sues.",
|
||||
|
@ -447,13 +451,13 @@
|
|||
"navigation_bar.discover": "Discoperir",
|
||||
"navigation_bar.domain_blocks": "Dominios blocate",
|
||||
"navigation_bar.explore": "Explorar",
|
||||
"navigation_bar.favourites": "Favoritos",
|
||||
"navigation_bar.favourites": "Favorites",
|
||||
"navigation_bar.filters": "Parolas silentiate",
|
||||
"navigation_bar.follow_requests": "Requestas de sequimento",
|
||||
"navigation_bar.followed_tags": "Hashtags sequite",
|
||||
"navigation_bar.follows_and_followers": "Sequites e sequitores",
|
||||
"navigation_bar.lists": "Listas",
|
||||
"navigation_bar.logout": "Clauder le session",
|
||||
"navigation_bar.logout": "Clauder session",
|
||||
"navigation_bar.mutes": "Usatores silentiate",
|
||||
"navigation_bar.opened_in_classic_interface": "Messages, contos e altere paginas specific es aperite per predefinition in le interfacie web classic.",
|
||||
"navigation_bar.personal": "Personal",
|
||||
|
@ -470,6 +474,14 @@
|
|||
"notification.follow_request": "{name} ha requestate de sequer te",
|
||||
"notification.mention": "{name} te ha mentionate",
|
||||
"notification.moderation-warning.learn_more": "Apprender plus",
|
||||
"notification.moderation_warning": "Tu ha recepite un aviso de moderation",
|
||||
"notification.moderation_warning.action_delete_statuses": "Alcunes de tu messages ha essite removite.",
|
||||
"notification.moderation_warning.action_disable": "Tu conto ha essite disactivate.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Alcunes de tu messages ha essite marcate como sensibile.",
|
||||
"notification.moderation_warning.action_none": "Tu conto ha recipite un advertimento de moderation.",
|
||||
"notification.moderation_warning.action_sensitive": "Tu messages essera marcate como sensibile a partir de ora.",
|
||||
"notification.moderation_warning.action_silence": "Tu conto ha essite limitate.",
|
||||
"notification.moderation_warning.action_suspend": "Tu conto ha essite suspendite.",
|
||||
"notification.own_poll": "Tu sondage ha finite",
|
||||
"notification.poll": "Un sondage in le qual tu ha votate ha finite",
|
||||
"notification.reblog": "{name} ha impulsate tu message",
|
||||
|
@ -489,7 +501,7 @@
|
|||
"notifications.column_settings.admin.report": "Nove signalationes:",
|
||||
"notifications.column_settings.admin.sign_up": "Nove inscriptiones:",
|
||||
"notifications.column_settings.alert": "Notificationes de scriptorio",
|
||||
"notifications.column_settings.favourite": "Favoritos:",
|
||||
"notifications.column_settings.favourite": "Favorites:",
|
||||
"notifications.column_settings.filter_bar.advanced": "Monstrar tote le categorias",
|
||||
"notifications.column_settings.filter_bar.category": "Barra de filtro rapide",
|
||||
"notifications.column_settings.follow": "Nove sequitores:",
|
||||
|
@ -506,7 +518,7 @@
|
|||
"notifications.column_settings.update": "Modificationes:",
|
||||
"notifications.filter.all": "Toto",
|
||||
"notifications.filter.boosts": "Impulsos",
|
||||
"notifications.filter.favourites": "Favoritos",
|
||||
"notifications.filter.favourites": "Favorites",
|
||||
"notifications.filter.follows": "Sequites",
|
||||
"notifications.filter.mentions": "Mentiones",
|
||||
"notifications.filter.polls": "Resultatos del sondage",
|
||||
|
@ -705,7 +717,7 @@
|
|||
"status.edited": "Ultime modification le {date}",
|
||||
"status.edited_x_times": "Modificate {count, plural, one {{count} vice} other {{count} vices}}",
|
||||
"status.embed": "Incastrar",
|
||||
"status.favourite": "Adder al favoritos",
|
||||
"status.favourite": "Adder al favorites",
|
||||
"status.favourites": "{count, plural, one {favorite} other {favorites}}",
|
||||
"status.filter": "Filtrar iste message",
|
||||
"status.filtered": "Filtrate",
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
"announcement.announcement": "Proclamation",
|
||||
"attachments_list.unprocessed": "(íntractat)",
|
||||
"audio.hide": "Celar audio",
|
||||
"block_modal.remote_users_caveat": "Noi va petir que li servitor {domain} mey respecter tui decision. Támen, obedientie ne es garantit pro que chascun servitor gere bloccas diferentmen. Possibilmen public postas va restar visibil a usatores de inloggat.",
|
||||
"block_modal.show_less": "Monstrar minu",
|
||||
"block_modal.show_more": "Monstrar plu",
|
||||
"block_modal.they_cant_mention": "Ne posse mentionar ni sequer te.",
|
||||
|
@ -212,13 +213,23 @@
|
|||
"domain_block_modal.block_account_instead": "Altrimen, bloccar @{name}",
|
||||
"domain_block_modal.they_can_interact_with_old_posts": "Persones de ti servitor posse interacter con tui old postas.",
|
||||
"domain_block_modal.they_cant_follow": "Nequi de ti-ci servitor posse sequer te.",
|
||||
"domain_block_modal.they_wont_know": "Ne va esser conscient pri li bloccada.",
|
||||
"domain_block_modal.title": "Bloccar dominia?",
|
||||
"domain_block_modal.you_will_lose_followers": "Omni tui sequitores de ti-ci servitor va esser efaciat.",
|
||||
"domain_block_modal.you_wont_see_posts": "Tu ne va vider postas ni notificationes de usatores sur ti-ci servitor.",
|
||||
"domain_pill.activitypub_lets_connect": "It possibilisa tui conexiones e interactiones con persones ne solmen sur Mastodon, ma anc tra diferent social aplis.",
|
||||
"domain_pill.activitypub_like_language": "ActivityPub es li lingue usat de Mastodon por parlar con altri social retages.",
|
||||
"domain_pill.server": "Servitor",
|
||||
"domain_pill.their_handle": "Identificator:",
|
||||
"domain_pill.their_server": "Su digital hem e omni su postas.",
|
||||
"domain_pill.their_username": "Su unic identificator sur su servitor. It es possibil que altri servitores va haver usatores con li sam nómine.",
|
||||
"domain_pill.username": "Usator-nómine",
|
||||
"domain_pill.whats_in_a_handle": "Ex quo consiste un identificator?",
|
||||
"domain_pill.who_they_are": "Pro que identificatores informa qui e u un person is, tu posse interacter con persones tra li rete social de <button>ActivityPub-usant platformes</button>.",
|
||||
"domain_pill.who_you_are": "Pro que tui identificator informa qui e u tu es, persones posse interacter con te tra li rete social de <button>ActivityPub-usant platformes</button>.",
|
||||
"domain_pill.your_handle": "Tui identificator:",
|
||||
"domain_pill.your_server": "Tui digital hem, u trova se omni tui postas. Si it ne plese te, tu posse transferer ad un altri servitor quandecunc e tui sequitores con te.",
|
||||
"domain_pill.your_username": "Tui unic identificator sur ti-ci servitor. It es possibil que altri servitores va haver usatores con li sam nómine.",
|
||||
"embed.instructions": "Inbedar ti-ci posta per copiar li code in infra.",
|
||||
"embed.preview": "Vi qualmen it va aspecter:",
|
||||
"emoji_button.activity": "Activitá",
|
||||
|
@ -286,6 +297,7 @@
|
|||
"filter_modal.select_filter.subtitle": "Usar un existent categorie o crear nov",
|
||||
"filter_modal.select_filter.title": "Filtrar ti-ci posta",
|
||||
"filter_modal.title.status": "Filtrar un posta",
|
||||
"filtered_notifications_banner.mentions": "{count, plural, one {mention} other {mentiones}}",
|
||||
"filtered_notifications_banner.pending_requests": "Notificationes de {count, plural, =0 {nequi} one {un person} other {# persones}} quel tu possibilmen conosse",
|
||||
"filtered_notifications_banner.title": "Filtrat notificationes",
|
||||
"firehose.all": "Omno",
|
||||
|
@ -296,6 +308,8 @@
|
|||
"follow_requests.unlocked_explanation": "Benque tu conto ne es cludet, li administratores de {domain} pensat que tu fórsan vell voler tractar seque-petitiones de tis-ci contos manualmen.",
|
||||
"follow_suggestions.curated_suggestion": "Selection del employates",
|
||||
"follow_suggestions.dismiss": "Ne monstrar plu",
|
||||
"follow_suggestions.featured_longer": "Selectet manualmen del equip de {domain}",
|
||||
"follow_suggestions.friends_of_friends_longer": "Populari ínter li persones queles tu seque",
|
||||
"follow_suggestions.hints.featured": "Ti-ci profil ha esset selectet directmen del equip de {domain}.",
|
||||
"follow_suggestions.hints.friends_of_friends": "Ti-ci profil es populari ínter tis qui tu seque.",
|
||||
"follow_suggestions.hints.most_followed": "Ti-ci profil es un del max sequet sur {domain}.",
|
||||
|
@ -303,6 +317,8 @@
|
|||
"follow_suggestions.hints.similar_to_recently_followed": "Ti-ci profil es simil al profiles queles tu ha recentmen sequet.",
|
||||
"follow_suggestions.personalized_suggestion": "Personalisat suggestion",
|
||||
"follow_suggestions.popular_suggestion": "Populari suggestion",
|
||||
"follow_suggestions.popular_suggestion_longer": "Populari sur {domain}",
|
||||
"follow_suggestions.similar_to_recently_followed_longer": "Simil a profiles queles tu sequet recentmen",
|
||||
"follow_suggestions.view_all": "Vider omnicos",
|
||||
"follow_suggestions.who_to_follow": "Persones a sequer",
|
||||
"followed_tags": "Sequet hashtags",
|
||||
|
@ -423,6 +439,8 @@
|
|||
"mute_modal.they_can_mention_and_follow": "Posse mentionar e sequer te, ma va esser ínvisibil a te.",
|
||||
"mute_modal.they_wont_know": "Ne va esser conscient pri li silentation.",
|
||||
"mute_modal.title": "Silentiar usator?",
|
||||
"mute_modal.you_wont_see_mentions": "Tu ne va vider postas mentionant li usator.",
|
||||
"mute_modal.you_wont_see_posts": "Ne posse vider tui postas e inversi.",
|
||||
"navigation_bar.about": "Information",
|
||||
"navigation_bar.advanced_interface": "Aperter in li web-interfacie avansat",
|
||||
"navigation_bar.blocks": "Bloccat usatores",
|
||||
|
@ -455,10 +473,23 @@
|
|||
"notification.follow": "{name} sequet te",
|
||||
"notification.follow_request": "{name} ha petit sequer te",
|
||||
"notification.mention": "{name} mentionat te",
|
||||
"notification.moderation-warning.learn_more": "Aprender plu",
|
||||
"notification.moderation_warning": "Tu ha recivet un moderatori advertiment",
|
||||
"notification.moderation_warning.action_delete_statuses": "Alcun de tui postas ha esset efaciat.",
|
||||
"notification.moderation_warning.action_disable": "Tui conto ha esset desactivisat.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Alcun de tui postas ha esset marcat quam sensitiv.",
|
||||
"notification.moderation_warning.action_none": "Tui conto ha recivet un moderatori advertiment.",
|
||||
"notification.moderation_warning.action_sensitive": "Desde nu tui postas va esser marcat quam sensitiv.",
|
||||
"notification.moderation_warning.action_silence": "Tui conto ha esset limitat.",
|
||||
"notification.moderation_warning.action_suspend": "Tui conto ha esset suspendet.",
|
||||
"notification.own_poll": "Tui balotation ha finit",
|
||||
"notification.poll": "Un balotation in quel tu votat ha finit",
|
||||
"notification.reblog": "{name} boostat tui posta",
|
||||
"notification.relationships_severance_event": "Perdit conexiones con {name}",
|
||||
"notification.relationships_severance_event.account_suspension": "Un admin de {from} ha suspendet {target}, dunc con ti person tu ne plu posse reciver actualisationes ni far interactiones.",
|
||||
"notification.relationships_severance_event.domain_block": "Un admin de {from} ha bloccat {target}, includente {followersCount} de tui sequitores e {followingCount, plural, one {# conto} other {# contos}} sequet de te.",
|
||||
"notification.relationships_severance_event.learn_more": "Aprender plu",
|
||||
"notification.relationships_severance_event.user_domain_block": "Tu ha bloccat {target}, efaciante {followersCount} de tui sequitores e {followingCount, plural, one {# conto} other {# contos}} sequet de te.",
|
||||
"notification.status": "{name} just postat",
|
||||
"notification.update": "{name} modificat un posta",
|
||||
"notification_requests.accept": "Acceptar",
|
||||
|
@ -472,6 +503,7 @@
|
|||
"notifications.column_settings.alert": "Notificationes sur li computator",
|
||||
"notifications.column_settings.favourite": "Favorites:",
|
||||
"notifications.column_settings.filter_bar.advanced": "Monstrar omni categories",
|
||||
"notifications.column_settings.filter_bar.category": "Rapid filtre-barre",
|
||||
"notifications.column_settings.follow": "Nov sequitores:",
|
||||
"notifications.column_settings.follow_request": "Nov petitiones de sequer:",
|
||||
"notifications.column_settings.mention": "Mentiones:",
|
||||
|
@ -707,6 +739,7 @@
|
|||
"status.reblog": "Boostar",
|
||||
"status.reblog_private": "Boostar con li original visibilitá",
|
||||
"status.reblogged_by": "{name} boostat",
|
||||
"status.reblogs": "{count, plural, one {boost} other {boosts}}",
|
||||
"status.reblogs.empty": "Ancor nequi ha boostat ti-ci posta. Quande alqui fa it, ilu va aparir ci.",
|
||||
"status.redraft": "Deleter & redacter",
|
||||
"status.remove_bookmark": "Remover marcator",
|
||||
|
|
|
@ -473,6 +473,15 @@
|
|||
"notification.follow": "{name}さんにフォローされました",
|
||||
"notification.follow_request": "{name}さんがあなたにフォローリクエストしました",
|
||||
"notification.mention": "{name}さんがあなたに返信しました",
|
||||
"notification.moderation-warning.learn_more": "さらに詳しく",
|
||||
"notification.moderation_warning": "管理者から警告が来ています",
|
||||
"notification.moderation_warning.action_delete_statuses": "あなたによるいくつかの投稿が削除されました。",
|
||||
"notification.moderation_warning.action_disable": "あなたのアカウントは無効になりました。",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "あなたの投稿のいくつかは閲覧注意として判定されています。",
|
||||
"notification.moderation_warning.action_none": "あなたのアカウントは管理者からの警告を受けています。",
|
||||
"notification.moderation_warning.action_sensitive": "あなたの投稿はこれから閲覧注意としてマークされます。",
|
||||
"notification.moderation_warning.action_silence": "あなたのアカウントは制限されています。",
|
||||
"notification.moderation_warning.action_suspend": "あなたのアカウントは停止されました。",
|
||||
"notification.own_poll": "アンケートが終了しました",
|
||||
"notification.poll": "アンケートが終了しました",
|
||||
"notification.reblog": "{name}さんがあなたの投稿をブーストしました",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"about.blocks": "Prižiūrimi serveriai",
|
||||
"about.contact": "Kontaktai:",
|
||||
"about.disclaimer": "Mastodon – nemokama atvirojo kodo programa ir Mastodon gGmbH prekės ženklas.",
|
||||
"about.disclaimer": "Mastodon – tai nemokama atvirojo kodo programinė įranga ir Mastodon gGmbH prekės ženklas.",
|
||||
"about.domain_blocks.no_reason_available": "Priežastis nepateikta",
|
||||
"about.domain_blocks.preamble": "Mastodon paprastai leidžia peržiūrėti turinį ir bendrauti su naudotojais iš bet kurio kito fediverse esančio serverio. Šios yra išimtys, kurios buvo padarytos šiame konkrečiame serveryje.",
|
||||
"about.domain_blocks.silenced.explanation": "Paprastai nematysi profilių ir turinio iš šio serverio, nebent jį aiškiai ieškosi arba pasirinksi jį sekdamas (-a).",
|
||||
|
@ -30,7 +30,7 @@
|
|||
"account.endorse": "Rodyti profilyje",
|
||||
"account.featured_tags.last_status_at": "Paskutinis įrašas {date}",
|
||||
"account.featured_tags.last_status_never": "Nėra įrašų",
|
||||
"account.featured_tags.title": "{name} rekomenduojami saitažodžiai",
|
||||
"account.featured_tags.title": "{name} rodomi saitažodžiai",
|
||||
"account.follow": "Sekti",
|
||||
"account.follow_back": "Sekti atgal",
|
||||
"account.followers": "Sekėjai",
|
||||
|
@ -38,13 +38,13 @@
|
|||
"account.followers_counter": "{count, plural, one {{counter} sekėjas} few {{counter} sekėjai} many {{counter} sekėjo} other {{counter} sekėjų}}",
|
||||
"account.following": "Sekama",
|
||||
"account.following_counter": "{count, plural, one {{counter} sekimas} few {{counter} sekimai} many {{counter} sekimo} other {{counter} sekimų}}",
|
||||
"account.follows.empty": "Šis (-i) naudotojas (-a) dar nieko neseka.",
|
||||
"account.follows.empty": "Šis naudotojas dar nieko neseka.",
|
||||
"account.go_to_profile": "Eiti į profilį",
|
||||
"account.hide_reblogs": "Slėpti pakėlimus iš @{name}",
|
||||
"account.in_memoriam": "Atminimui.",
|
||||
"account.joined_short": "Prisijungė",
|
||||
"account.languages": "Keisti prenumeruojamas kalbas",
|
||||
"account.link_verified_on": "Šios nuorodos nuosavybė buvo patikrinta {date}.",
|
||||
"account.link_verified_on": "Šios nuorodos nuosavybė buvo patikrinta {date}",
|
||||
"account.locked_info": "Šios paskyros privatumo būsena nustatyta kaip užrakinta. Savininkas (-ė) rankiniu būdu peržiūri, kas gali sekti.",
|
||||
"account.media": "Medija",
|
||||
"account.mention": "Paminėti @{name}",
|
||||
|
@ -59,7 +59,7 @@
|
|||
"account.posts": "Įrašai",
|
||||
"account.posts_with_replies": "Įrašai ir atsakymai",
|
||||
"account.report": "Pranešti apie @{name}",
|
||||
"account.requested": "Laukiama patvirtinimo. Spustelėk, jei nori atšaukti sekimo prašymą.",
|
||||
"account.requested": "Laukiama patvirtinimo. Spustelėk, jei nori atšaukti sekimo prašymą",
|
||||
"account.requested_follow": "{name} paprašė tave sekti",
|
||||
"account.share": "Bendrinti @{name} profilį",
|
||||
"account.show_reblogs": "Rodyti pakėlimus iš @{name}",
|
||||
|
@ -82,7 +82,7 @@
|
|||
"admin.impact_report.instance_followers": "Sekėjai, kuriuos prarastų mūsų naudotojai",
|
||||
"admin.impact_report.instance_follows": "Sekėjai, kuriuos prarastų jų naudotojai",
|
||||
"admin.impact_report.title": "Poveikio apibendrinimas",
|
||||
"alert.rate_limited.message": "Pabandyk vėliau po {retry_time, time, medium}.",
|
||||
"alert.rate_limited.message": "Bandyk vėliau po {retry_time, time, medium}.",
|
||||
"alert.rate_limited.title": "Sparta ribota.",
|
||||
"alert.unexpected.message": "Įvyko netikėta klaida.",
|
||||
"alert.unexpected.title": "Ups!",
|
||||
|
@ -92,7 +92,12 @@
|
|||
"block_modal.remote_users_caveat": "Paprašysime serverio {domain} gerbti tavo sprendimą. Tačiau atitiktis negarantuojama, nes kai kurie serveriai gali skirtingai tvarkyti blokavimus. Vieši įrašai vis tiek gali būti matomi neprisijungusiems naudotojams.",
|
||||
"block_modal.show_less": "Rodyti mažiau",
|
||||
"block_modal.show_more": "Rodyti daugiau",
|
||||
"boost_modal.combo": "Galima paspausti {combo}, kad praleisti kitą kartą.",
|
||||
"block_modal.they_cant_mention": "Jie negali tave paminėti ar sekti.",
|
||||
"block_modal.they_cant_see_posts": "Jie negali matyti tavo įrašus, o tu nematysi jų.",
|
||||
"block_modal.they_will_know": "Jie mato, kad yra užblokuoti.",
|
||||
"block_modal.title": "Blokuoti naudotoją?",
|
||||
"block_modal.you_wont_see_mentions": "Nematysi įrašus, kuriuose jie paminimi.",
|
||||
"boost_modal.combo": "Galima paspausti {combo}, kad praleisti tai kitą kartą",
|
||||
"bundle_column_error.copy_stacktrace": "Kopijuoti klaidos ataskaitą",
|
||||
"bundle_column_error.error.body": "Paprašytos puslapio nepavyko atvaizduoti. Tai gali būti dėl mūsų kodo klaidos arba naršyklės suderinamumo problemos.",
|
||||
"bundle_column_error.error.title": "O, ne!",
|
||||
|
@ -117,7 +122,7 @@
|
|||
"column.direct": "Privatūs paminėjimai",
|
||||
"column.directory": "Naršyti profilius",
|
||||
"column.domain_blocks": "Užblokuoti domenai",
|
||||
"column.favourites": "Mėgstamiausi",
|
||||
"column.favourites": "Mėgstami",
|
||||
"column.firehose": "Tiesioginiai srautai",
|
||||
"column.follow_requests": "Sekimo prašymai",
|
||||
"column.home": "Pagrindinis",
|
||||
|
@ -143,8 +148,8 @@
|
|||
"compose.published.open": "Atidaryti",
|
||||
"compose.saved.body": "Įrašas išsaugotas.",
|
||||
"compose_form.direct_message_warning_learn_more": "Sužinoti daugiau",
|
||||
"compose_form.encryption_warning": "Mastodon įrašai nėra šifruojami nuo galo iki galo. Per Mastodon nesidalyk jokia slapta informacija.",
|
||||
"compose_form.hashtag_warning": "Šis įrašas nebus įtraukta į jokį saitažodį, nes ji nėra vieša. Tik viešų įrašų galima ieškoti pagal saitažodį.",
|
||||
"compose_form.encryption_warning": "Mastodon įrašai nėra visapusiškai šifruojami. Per Mastodon nesidalyk jokia slapta informacija.",
|
||||
"compose_form.hashtag_warning": "Šis įrašas nebus įtrauktas į jokį saitažodį, nes ji nėra vieša. Tik viešų įrašų galima ieškoti pagal saitažodį.",
|
||||
"compose_form.lock_disclaimer": "Tavo paskyra nėra {locked}. Bet kas gali sekti tave ir peržiūrėti tik sekėjams skirtus įrašus.",
|
||||
"compose_form.lock_disclaimer.lock": "užrakinta",
|
||||
"compose_form.placeholder": "Kas tavo mintyse?",
|
||||
|
@ -152,7 +157,7 @@
|
|||
"compose_form.poll.multiple": "Keli pasirinkimai",
|
||||
"compose_form.poll.option_placeholder": "{number} parinktis",
|
||||
"compose_form.poll.single": "Pasirinkti vieną",
|
||||
"compose_form.poll.switch_to_multiple": "Keisti apklausą, kad būtų galima pasirinkti kelis pasirinkimus.",
|
||||
"compose_form.poll.switch_to_multiple": "Keisti apklausą, kad būtų galima pasirinkti kelis pasirinkimus",
|
||||
"compose_form.poll.switch_to_single": "Keisti apklausą, kad būtų galima pasirinkti vieną pasirinkimą",
|
||||
"compose_form.poll.type": "Stilius",
|
||||
"compose_form.publish": "Skelbti",
|
||||
|
@ -172,16 +177,17 @@
|
|||
"confirmations.delete_list.message": "Ar tikrai nori visam laikui ištrinti šį sąrašą?",
|
||||
"confirmations.discard_edit_media.confirm": "Atmesti",
|
||||
"confirmations.discard_edit_media.message": "Turi neišsaugotų medijos aprašymo ar peržiūros pakeitimų, vis tiek juos atmesti?",
|
||||
"confirmations.domain_block.confirm": "Blokuoti serverį",
|
||||
"confirmations.domain_block.message": "Ar tikrai, tikrai nori užblokuoti visą {domain}? Daugeliu atvejų užtenka kelių tikslinių blokavimų arba nutildymų. Šio domeno turinio nematysi jokiose viešose laiko skalėse ar pranešimuose. Tavo sekėjai iš to domeno bus pašalinti.",
|
||||
"confirmations.edit.confirm": "Redaguoti",
|
||||
"confirmations.edit.message": "Redaguojant dabar, bus perrašyta šiuo metu kuriama žinutė. Ar tikrai nori tęsti?",
|
||||
"confirmations.logout.confirm": "Atsijungti",
|
||||
"confirmations.logout.message": "Ar tikrai nori atsijungti?",
|
||||
"confirmations.mute.confirm": "Nutildyti",
|
||||
"confirmations.redraft.confirm": "Ištrinti ir parengti iš naujo",
|
||||
"confirmations.redraft.message": "Ar tikrai nori ištrinti šį įrašą ir parengti jį iš naujo kaip juodraštį? Bus prarastos mėgstamiausios ir pakėlimai, o atsakymai į originalinį įrašą taps liekamojais.",
|
||||
"confirmations.redraft.confirm": "Ištrinti ir perrašyti",
|
||||
"confirmations.redraft.message": "Ar tikrai nori ištrinti šį įrašą ir parašyti jį iš naujo? Bus prarastos mėgstamai ir pakėlimai, o atsakymai į originalinį įrašą taps liekamojais.",
|
||||
"confirmations.reply.confirm": "Atsakyti",
|
||||
"confirmations.reply.message": "Atsakant dabar, bus perrašyta metu kuriama žinutė. Ar tikrai nori tęsti?",
|
||||
"confirmations.reply.message": "Atsakant dabar, bus perrašyta šiuo metu kuriama žinutė. Ar tikrai nori tęsti?",
|
||||
"confirmations.unfollow.confirm": "Nebesekti",
|
||||
"confirmations.unfollow.message": "Ar tikrai nori nebesekti {name}?",
|
||||
"conversation.delete": "Ištrinti pokalbį",
|
||||
|
@ -196,34 +202,42 @@
|
|||
"directory.new_arrivals": "Nauji atvykėliai",
|
||||
"directory.recently_active": "Neseniai aktyvus (-i)",
|
||||
"disabled_account_banner.account_settings": "Paskyros nustatymai",
|
||||
"disabled_account_banner.text": "Tavo paskyra {disabledAccount} šiuo metu išjungta.",
|
||||
"dismissable_banner.community_timeline": "Tai – naujausi vieši įrašai, kuriuos paskelbė žmonės, kurių paskyros talpinamos {domain}.",
|
||||
"disabled_account_banner.text": "Tavo paskyra {disabledAccount} šiuo metu yra išjungta.",
|
||||
"dismissable_banner.community_timeline": "Tai – naujausi vieši įrašai iš žmonių, kurių paskyros talpinamos {domain}.",
|
||||
"dismissable_banner.dismiss": "Atmesti",
|
||||
"dismissable_banner.explore_links": "Tai – naujienos, kuriomis šiandien daugiausiai bendrinamasi socialiniame žiniatinklyje. Naujesnės naujienų istorijos, kurias paskelbė daugiau skirtingų žmonių, vertinamos aukščiau.",
|
||||
"dismissable_banner.explore_statuses": "Tai – įrašai iš viso socialinio žiniatinklio, kurie šiandien sulaukia daug dėmesio. Naujesni įrašai, turintys daugiau pakėlimų ir mėgstamų, vertinami aukščiau.",
|
||||
"dismissable_banner.explore_tags": "Tai – saitažodžiai, kurie šiandien sulaukia daug dėmesio socialiniame žiniatinklyje. Saitažodžiai, kuriuos naudoja daugiau skirtingų žmonių, vertinami aukščiau.",
|
||||
"dismissable_banner.public_timeline": "Tai – naujausi vieši įrašai, kuriuos socialiniame žiniatinklyje paskelbė žmonės, sekantys {domain}.",
|
||||
"domain_pill.activitypub_lets_connect": "Tai leidžia tau bendrauti su žmonėmis ne tik Mastodon, bet ir įvairiose socialinėse programėlėse.",
|
||||
"domain_pill.activitypub_like_language": "ActivityPub – tarsi kalba, kuria Mastodon kalba su kitais socialiniais tinklais.",
|
||||
"dismissable_banner.public_timeline": "Tai – naujausi vieši įrašai iš žmonių socialiniame žiniatinklyje, kuriuos seka {domain} žmonės.",
|
||||
"domain_block_modal.block": "Blokuoti serverį",
|
||||
"domain_block_modal.block_account_instead": "Blokuoti {name} vietoj to",
|
||||
"domain_block_modal.they_can_interact_with_old_posts": "Žmonės iš šio serverio gali sąveikauti su tavo senomis įrašomis.",
|
||||
"domain_block_modal.they_cant_follow": "Niekas iš šio serverio negali tavęs sekti.",
|
||||
"domain_block_modal.they_wont_know": "Jie nežinos, kad buvo užblokuoti.",
|
||||
"domain_block_modal.title": "Blokuoti domeną?",
|
||||
"domain_block_modal.you_will_lose_followers": "Visi tavo sekėjai iš šio serverio bus pašalinti.",
|
||||
"domain_block_modal.you_wont_see_posts": "Nematysi naudotojų įrašų ar pranešimų šiame serveryje.",
|
||||
"domain_pill.activitypub_lets_connect": "Tai leidžia tau sąveikauti su žmonėmis ne tik Mastodon, bet ir įvairiose socialinėse programėlėse.",
|
||||
"domain_pill.activitypub_like_language": "ActivityPub – tai tarsi kalba, kuria Mastodon kalba su kitais socialiniais tinklais.",
|
||||
"domain_pill.server": "Serveris",
|
||||
"domain_pill.their_handle": "Jų socialinis medijos vardas:",
|
||||
"domain_pill.their_server": "Jų skaitmeniniai namai, kuriuose saugomi visi jų įrašai.",
|
||||
"domain_pill.their_username": "Jų unikalus identifikatorius jų serveryje. Skirtinguose serveriuose galima rasti naudotojų, turinčių tą patį naudotojo vardą.",
|
||||
"domain_pill.username": "Naudotojo vardas",
|
||||
"domain_pill.whats_in_a_handle": "Kas yra socialiniame medijos varde?",
|
||||
"domain_pill.who_they_are": "Kadangi socialines medijos vardai nurodo, kas ir kur jie yra, galima bendrauti su žmonėmis visame socialiniame tinkle, kuriame yra <button> ActivityPub valdomos platformos</button>.",
|
||||
"domain_pill.who_you_are": "Kadangi tavo socialinis medijos vardas nurodo, kas esi ir kur esi, žmonės gali bendrauti su tavimi visame socialiniame tinkle, kurį sudaro <button> ActivityPub valdomos platformos</button>.",
|
||||
"domain_pill.who_they_are": "Kadangi socialines medijos vardai nurodo, kas žmogus yra ir kur jie yra, gali sąveikauti su žmonėmis visame socialiniame žiniatinklyje, kurį sudaro <button>ActivityPub veikiančios platformos</button>.",
|
||||
"domain_pill.who_you_are": "Kadangi tavo socialinis medijos vardas nurodo, kas esi ir kur esi, žmonės gali sąveikauti su tavimi visame socialiniame tinkle, kurį sudaro <button>ActivityPub veikiančios platformos</button>.",
|
||||
"domain_pill.your_handle": "Tavo socialinis medijos vardas:",
|
||||
"domain_pill.your_server": "Tavo skaitmeniniai namai, kuriuose saugomi visi tavo įrašai. Nepatinka šis? Bet kada perkelk serverius ir atsivesk ir savo sekėjus.",
|
||||
"domain_pill.your_username": "Tavo unikalus identifikatorius šiame serveryje. Skirtinguose serveriuose galima rasti naudotojų, turinčių tą patį naudotojo vardą.",
|
||||
"domain_pill.your_username": "Tavo unikalus identifikatorius šiame serveryje. Skirtinguose serveriuose galima rasti naudotojų su tuo pačiu naudotojo vardu.",
|
||||
"embed.instructions": "Įterpk šį įrašą į savo svetainę nukopijavus (-usi) toliau pateiktą kodą.",
|
||||
"embed.preview": "Štai, kaip tai atrodys:",
|
||||
"embed.preview": "Štai kaip tai atrodys:",
|
||||
"emoji_button.activity": "Veikla",
|
||||
"emoji_button.clear": "Išvalyti",
|
||||
"emoji_button.custom": "Pasirinktinis",
|
||||
"emoji_button.flags": "Vėliavos",
|
||||
"emoji_button.food": "Maistas ir gėrimai",
|
||||
"emoji_button.label": "Įterpti veidelius",
|
||||
"emoji_button.label": "Įterpti jaustuką",
|
||||
"emoji_button.nature": "Gamta",
|
||||
"emoji_button.not_found": "Nerasta jokių tinkamų jaustukų.",
|
||||
"emoji_button.objects": "Objektai",
|
||||
|
@ -234,26 +248,27 @@
|
|||
"emoji_button.symbols": "Simboliai",
|
||||
"emoji_button.travel": "Kelionės ir vietos",
|
||||
"empty_column.account_hides_collections": "Šis (-i) naudotojas (-a) pasirinko nepadaryti šią informaciją prieinamą.",
|
||||
"empty_column.account_suspended": "Paskyra sustabdyta.",
|
||||
"empty_column.account_timeline": "Nėra įrašų čia.",
|
||||
"empty_column.account_suspended": "Paskyra pristabdyta.",
|
||||
"empty_column.account_timeline": "Nėra čia įrašų.",
|
||||
"empty_column.account_unavailable": "Profilis neprieinamas.",
|
||||
"empty_column.blocks": "Dar neužblokavai nė vieno naudotojo.",
|
||||
"empty_column.bookmarked_statuses": "Dar neturi nė vienos įrašo žymės. Kai vieną iš jų pridėsi į žymes, jis bus rodomas čia.",
|
||||
"empty_column.community": "Vietinė laiko skalė tuščia. Parašyk ką nors viešai, kad pradėtum bendrauti!",
|
||||
"empty_column.bookmarked_statuses": "Dar neturi nė vienos įrašo pridėtos žymės. Kai vieną iš jų pridėsi į žymes, jis bus rodomas čia.",
|
||||
"empty_column.community": "Vietinė laiko skalė yra tuščia. Parašyk ką nors viešai, kad pradėtum sąveikauti.",
|
||||
"empty_column.direct": "Dar neturi jokių privačių paminėjimų. Kai išsiųsi arba gausi vieną iš jų, jis bus rodomas čia.",
|
||||
"empty_column.domain_blocks": "Dar nėra užblokuotų domenų.",
|
||||
"empty_column.explore_statuses": "Šiuo metu niekas nėra tendencinga. Patikrink vėliau.",
|
||||
"empty_column.explore_statuses": "Šiuo metu niekas nėra tendencinga. Patikrink vėliau!",
|
||||
"empty_column.favourited_statuses": "Dar neturi mėgstamų įrašų. Kai vieną iš jų pamėgsi, jis bus rodomas čia.",
|
||||
"empty_column.favourites": "Šio įrašo dar niekas nepamėgo. Kai kas nors tai padarys, jie bus rodomi čia.",
|
||||
"empty_column.follow_requests": "Dar neturi jokių sekimo prašymų. Kai gausi tokį prašymą, jis bus rodomas čia.",
|
||||
"empty_column.followed_tags": "Dar neseki jokių saitažodžių. Kai tai padarysi, jie bus rodomi čia.",
|
||||
"empty_column.hashtag": "Nėra nieko šiame saitažodyje kol kas.",
|
||||
"empty_column.home": "Tavo pagrindinio laiko skalė tuščia! Sek daugiau žmonių, kad ją užpildytum.",
|
||||
"empty_column.home": "Tavo pagrindinio laiko skalė tuščia. Sek daugiau žmonių, kad ją užpildytum.",
|
||||
"empty_column.list": "Nėra nieko šiame sąraše kol kas. Kai šio sąrašo nariai paskelbs naujų įrašų, jie bus rodomi čia.",
|
||||
"empty_column.lists": "Dar neturi jokių sąrašų. Kai jį sukursi, jis bus rodomas čia.",
|
||||
"empty_column.mutes": "Dar nesi nutildęs (-usi) nė vieno naudotojo.",
|
||||
"empty_column.notifications": "Dar neturi jokių pranešimų. Kai kiti žmonės su tavimi bendraus, matysi tai čia.",
|
||||
"empty_column.public": "Čia nieko nėra! Parašyk ką nors viešai arba rankiniu būdu sek naudotojus iš kitų serverių, kad jį užpildytum.",
|
||||
"empty_column.notification_requests": "Viskas švaru! Čia nieko nėra. Kai gausi naujų pranešimų, jie bus rodomi čia pagal tavo nustatymus.",
|
||||
"empty_column.notifications": "Dar neturi jokių pranešimų. Kai kiti žmonės su tavimi sąveikaus, matysi tai čia.",
|
||||
"empty_column.public": "Čia nieko nėra. Parašyk ką nors viešai arba rankiniu būdu sek naudotojus iš kitų serverių, kad jį užpildytum.",
|
||||
"error.unexpected_crash.explanation": "Dėl mūsų kodo riktos arba naršyklės suderinamumo problemos šis puslapis negalėjo būti rodomas teisingai.",
|
||||
"error.unexpected_crash.explanation_addons": "Šį puslapį nepavyko parodyti teisingai. Šią klaidą greičiausiai sukėlė naršyklės priedas arba automatinio vertimo įrankiai.",
|
||||
"error.unexpected_crash.next_steps": "Pabandyk atnaujinti puslapį. Jei tai nepadeda, galbūt vis dar galėsi naudotis Mastodon per kitą naršyklę arba savąją programėlę.",
|
||||
|
@ -270,9 +285,9 @@
|
|||
"filter_modal.added.context_mismatch_title": "Konteksto neatitikimas.",
|
||||
"filter_modal.added.expired_explanation": "Ši filtro kategorija nustojo galioti. Kad ji būtų taikoma, turėsi pakeisti galiojimo datą.",
|
||||
"filter_modal.added.expired_title": "Baigėsi filtro galiojimas.",
|
||||
"filter_modal.added.review_and_configure": "Norint peržiūrėti ir toliau konfigūruoti šią filtro kategoriją, eik į nuorodą {settings_link}.",
|
||||
"filter_modal.added.review_and_configure": "Norint peržiūrėti ir toliau konfigūruoti šią filtro kategoriją, eik į {settings_link}.",
|
||||
"filter_modal.added.review_and_configure_title": "Filtro nustatymai",
|
||||
"filter_modal.added.settings_link": "nustatymų puslapis",
|
||||
"filter_modal.added.settings_link": "nustatymų puslapį",
|
||||
"filter_modal.added.short_explanation": "Šis įrašas buvo pridėtas į šią filtro kategoriją: {title}.",
|
||||
"filter_modal.added.title": "Pridėtas filtras.",
|
||||
"filter_modal.select_filter.context_mismatch": "netaikoma šiame kontekste.",
|
||||
|
@ -283,6 +298,8 @@
|
|||
"filter_modal.select_filter.title": "Filtruoti šį įrašą",
|
||||
"filter_modal.title.status": "Filtruoti įrašą",
|
||||
"filtered_notifications_banner.mentions": "{count, plural, one {paminėjimas} few {paminėjimai} many {paminėjimo} other {paminėjimų}}",
|
||||
"filtered_notifications_banner.pending_requests": "Pranešimai iš {count, plural, =0 {nė vieno} one {vienos žmogaus} few {# žmonių} many {# žmonių} other {# žmonių}}, kuriuos galbūt pažįsti",
|
||||
"filtered_notifications_banner.title": "Filtruojami pranešimai",
|
||||
"firehose.all": "Visi",
|
||||
"firehose.local": "Šis serveris",
|
||||
"firehose.remote": "Kiti serveriai",
|
||||
|
@ -295,8 +312,8 @@
|
|||
"follow_suggestions.friends_of_friends_longer": "Populiarus tarp žmonių, kurių seki",
|
||||
"follow_suggestions.hints.featured": "Šį profilį atrinko {domain} komanda.",
|
||||
"follow_suggestions.hints.friends_of_friends": "Šis profilis yra populiarus tarp žmonių, kuriuos seki.",
|
||||
"follow_suggestions.hints.most_followed": "Šis profilis yra vienas iš labiausiai sekamų {domain}.",
|
||||
"follow_suggestions.hints.most_interactions": "Pastaruoju metu šis profilis sulaukia daug dėmesio šiame {domain}.",
|
||||
"follow_suggestions.hints.most_followed": "Šis profilis yra vienas iš labiausiai sekamų domene {domain}.",
|
||||
"follow_suggestions.hints.most_interactions": "Pastaruoju metu šis profilis sulaukia daug dėmesio domane {domain}.",
|
||||
"follow_suggestions.hints.similar_to_recently_followed": "Šis profilis panašus į profilius, kuriuos neseniai sekei.",
|
||||
"follow_suggestions.personalized_suggestion": "Suasmenintas pasiūlymas",
|
||||
"follow_suggestions.popular_suggestion": "Populiarus pasiūlymas",
|
||||
|
@ -312,8 +329,8 @@
|
|||
"footer.keyboard_shortcuts": "Spartieji klavišai",
|
||||
"footer.privacy_policy": "Privatumo politika",
|
||||
"footer.source_code": "Peržiūrėti šaltinio kodą",
|
||||
"footer.status": "Būsena",
|
||||
"generic.saved": "Išsaugoti",
|
||||
"footer.status": "Statusas",
|
||||
"generic.saved": "Išsaugota",
|
||||
"getting_started.heading": "Kaip pradėti",
|
||||
"hashtag.column_header.tag_mode.all": "ir {additional}",
|
||||
"hashtag.column_header.tag_mode.any": "ar {additional}",
|
||||
|
@ -333,7 +350,7 @@
|
|||
"home.column_settings.show_reblogs": "Rodyti pakėlimus",
|
||||
"home.column_settings.show_replies": "Rodyti atsakymus",
|
||||
"home.hide_announcements": "Slėpti skelbimus",
|
||||
"home.pending_critical_update.body": "Kuo greičiau atnaujink savo Mastodon serverį!",
|
||||
"home.pending_critical_update.body": "Kuo greičiau atnaujink savo Mastodon serverį.",
|
||||
"home.pending_critical_update.link": "Žiūrėti naujinimus",
|
||||
"home.pending_critical_update.title": "Galimas kritinis saugumo naujinimas.",
|
||||
"home.show_announcements": "Rodyti skelbimus",
|
||||
|
@ -415,6 +432,7 @@
|
|||
"loading_indicator.label": "Kraunama…",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Slėpti vaizdą} few {Slėpti vaizdus} many {Slėpti vaizdo} other {Slėpti vaizdų}}",
|
||||
"moved_to_account_banner.text": "Tavo paskyra {disabledAccount} šiuo metu išjungta, nes persikėlei į {movedToAccount}.",
|
||||
"mute_modal.show_options": "Rodyti parinktis",
|
||||
"navigation_bar.about": "Apie",
|
||||
"navigation_bar.advanced_interface": "Atidaryti išplėstinę žiniatinklio sąsają",
|
||||
"navigation_bar.blocks": "Užblokuoti naudotojai",
|
||||
|
@ -448,7 +466,6 @@
|
|||
"notification.follow_request": "{name} paprašė tave sekti",
|
||||
"notification.mention": "{name} paminėjo tave",
|
||||
"notification.moderation-warning.learn_more": "Sužinoti daugiau",
|
||||
"notification.moderation_warning": "Gavai prižiūrėjimo įspėjimą",
|
||||
"notification.moderation_warning.action_delete_statuses": "Kai kurie tavo įrašai buvo pašalintos.",
|
||||
"notification.moderation_warning.action_disable": "Tavo paskyra buvo išjungta.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Kai kurie tavo įrašai buvo pažymėtos kaip jautrios.",
|
||||
|
@ -479,7 +496,7 @@
|
|||
"notifications.column_settings.follow_request": "Nauji sekimo prašymai:",
|
||||
"notifications.column_settings.mention": "Paminėjimai:",
|
||||
"notifications.column_settings.poll": "Balsavimo rezultatai:",
|
||||
"notifications.column_settings.push": "Stumdomieji pranešimai",
|
||||
"notifications.column_settings.push": "Tiesioginiai pranešimai",
|
||||
"notifications.column_settings.reblog": "Pakėlimai:",
|
||||
"notifications.column_settings.show": "Rodyti stulpelyje",
|
||||
"notifications.column_settings.sound": "Paleisti garsą",
|
||||
|
@ -518,7 +535,7 @@
|
|||
"onboarding.follows.lead": "Tavo pagrindinis srautas – pagrindinis būdas patirti Mastodon. Kuo daugiau žmonių seksi, tuo jis bus aktyvesnis ir įdomesnis. Norint pradėti, pateikiame keletą pasiūlymų:",
|
||||
"onboarding.follows.title": "Suasmenink savo pagrindinį srautą",
|
||||
"onboarding.profile.discoverable": "Padaryti mano profilį atrandamą",
|
||||
"onboarding.profile.discoverable_hint": "Kai pasirenki Mastodon atrandamumą, tavo įrašai gali būti rodomi paieškos rezultatuose ir tendencijose, o profilis gali būti siūlomas panašių pomėgių turintiems žmonėms.",
|
||||
"onboarding.profile.discoverable_hint": "Kai sutinki su Mastodon atrandamumu, tavo įrašai gali būti rodomi paieškos rezultatuose ir tendencijose, o profilis gali būti siūlomas panašių pomėgių turintiems žmonėms.",
|
||||
"onboarding.profile.display_name": "Rodomas vardas",
|
||||
"onboarding.profile.display_name_hint": "Tavo pilnas vardas arba linksmas vardas…",
|
||||
"onboarding.profile.lead": "Gali visada tai užbaigti vėliau nustatymuose, kur yra dar daugiau pritaikymo parinkčių.",
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"about.domain_blocks.silenced.title": "Ierobežotie",
|
||||
"about.domain_blocks.suspended.explanation": "Nekādi dati no šī servera netiks apstrādāti, uzglabāti vai apmainīti, padarot neiespējamu mijiedarbību vai saziņu ar lietotājiem no šī servera.",
|
||||
"about.domain_blocks.suspended.title": "Apturētie",
|
||||
"about.not_available": "Šī informācija šajā serverī nav bijusi pieejama.",
|
||||
"about.not_available": "Šī informācija nav padarīta pieejama šajā serverī.",
|
||||
"about.powered_by": "Decentralizētu sociālo tīklu nodrošina {mastodon}",
|
||||
"about.rules": "Servera noteikumi",
|
||||
"account.account_note_header": "Piezīme",
|
||||
|
@ -89,6 +89,9 @@
|
|||
"announcement.announcement": "Paziņojums",
|
||||
"attachments_list.unprocessed": "(neapstrādāti)",
|
||||
"audio.hide": "Slēpt audio",
|
||||
"block_modal.remote_users_caveat": "Mēs vaicāsim serverim {domain} ņemt vērā Tavu lēmumu. Tomēr atbilstība nav nodrošināta, jo atsevišķi serveri var apstrādāt bloķēšanu citādi. Publiski ieraksti joprojām var būt redzami lietotājiem, kuri nav pieteikušies.",
|
||||
"block_modal.show_less": "Parādīt vairāk",
|
||||
"block_modal.show_more": "Parādīt mazāk",
|
||||
"boost_modal.combo": "Nospied {combo}, lai nākamreiz šo izlaistu",
|
||||
"bundle_column_error.copy_stacktrace": "Kopēt kļūdu ziņojumu",
|
||||
"bundle_column_error.error.body": "Pieprasīto lapu nevarēja atveidot. Tas varētu būt saistīts ar kļūdu mūsu kodā, vai tā ir pārlūkprogrammas saderības problēma.",
|
||||
|
@ -190,7 +193,7 @@
|
|||
"directory.federated": "No pazīstamas federācijas",
|
||||
"directory.local": "Tikai no {domain}",
|
||||
"directory.new_arrivals": "Jaunpienācēji",
|
||||
"directory.recently_active": "Nesen aktīvie",
|
||||
"directory.recently_active": "Nesen aktīvi",
|
||||
"disabled_account_banner.account_settings": "Konta iestatījumi",
|
||||
"disabled_account_banner.text": "Tavs konts {disabledAccount} pašlaik ir atspējots.",
|
||||
"dismissable_banner.community_timeline": "Šie ir jaunākie publiskie ieraksti no cilvēkiem, kuru konti ir mitināti {domain}.",
|
||||
|
@ -199,6 +202,9 @@
|
|||
"dismissable_banner.explore_statuses": "Šie ir ieraksti, kas šodien gūst arvien lielāku ievērību visā sociālajā tīklā. Augstāk tiek kārtoti jaunāki ieraksti, kuri tiek vairāk pastiprināti un ievietoti izlasēs.",
|
||||
"dismissable_banner.explore_tags": "Šie tēmturi šobrīd kļūst arvien populārāki cilvēku vidū šajā un citos decentralizētā tīkla serveros.",
|
||||
"dismissable_banner.public_timeline": "Šie ir jaunākie publiskie ieraksti no lietotājiem sociālajā tīmeklī, kuriem {domain} seko cilvēki.",
|
||||
"domain_block_modal.they_cant_follow": "Neviens šajā serverī nevar Tev sekot.",
|
||||
"domain_pill.server": "Serveris",
|
||||
"domain_pill.username": "Lietotājvārds",
|
||||
"embed.instructions": "Iestrādā šo ziņu savā mājaslapā, kopējot zemāk redzamo kodu.",
|
||||
"embed.preview": "Tas izskatīsies šādi:",
|
||||
"emoji_button.activity": "Aktivitāte",
|
||||
|
@ -275,6 +281,7 @@
|
|||
"follow_suggestions.curated_suggestion": "Darbinieku izvēle",
|
||||
"follow_suggestions.dismiss": "Vairs nerādīt",
|
||||
"follow_suggestions.personalized_suggestion": "Pielāgots ieteikums",
|
||||
"follow_suggestions.similar_to_recently_followed_longer": "Līdzīgi profieliem, kuriem nesen sāki sekot",
|
||||
"follow_suggestions.view_all": "Skatīt visu",
|
||||
"follow_suggestions.who_to_follow": "Kam sekot",
|
||||
"followed_tags": "Sekojamie tēmturi",
|
||||
|
@ -388,6 +395,10 @@
|
|||
"loading_indicator.label": "Ielādē…",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Slēpt attēlu} other {Slēpt attēlus}}",
|
||||
"moved_to_account_banner.text": "Tavs konts {disabledAccount} pašlaik ir atspējots, jo Tu pārcēlies uz kontu {movedToAccount}.",
|
||||
"mute_modal.hide_from_notifications": "Paslēpt paziņojumos",
|
||||
"mute_modal.hide_options": "Paslēpt iespējas",
|
||||
"mute_modal.show_options": "Parādīt iespējas",
|
||||
"mute_modal.title": "Apklusināt lietotāju?",
|
||||
"navigation_bar.about": "Par",
|
||||
"navigation_bar.advanced_interface": "Atvērt paplašinātā tīmekļa saskarnē",
|
||||
"navigation_bar.blocks": "Bloķētie lietotāji",
|
||||
|
@ -420,11 +431,23 @@
|
|||
"notification.follow": "{name} uzsāka Tev sekot",
|
||||
"notification.follow_request": "{name} nosūtīja Tev sekošanas pieprasījumu",
|
||||
"notification.mention": "{name} pieminēja Tevi",
|
||||
"notification.moderation-warning.learn_more": "Uzzināt vairāk",
|
||||
"notification.moderation_warning.action_delete_statuses": "Daži no Taviem ierakstiem tika noņemti.",
|
||||
"notification.moderation_warning.action_disable": "Tavs konts tika atspējots.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Daži no Taviem ierakstiem tika atzīmēti kā jutīgi.",
|
||||
"notification.moderation_warning.action_sensitive": "Tavi ieraksti turpmāk tiks atzīmēti kā jutīgi.",
|
||||
"notification.moderation_warning.action_silence": "Tavs konts tika ierobežots.",
|
||||
"notification.moderation_warning.action_suspend": "Tava konta darbība tika apturēta.",
|
||||
"notification.own_poll": "Tava aptauja ir noslēgusies",
|
||||
"notification.poll": "Aptauja, kurā tu piedalījies, ir noslēgusies",
|
||||
"notification.reblog": "{name} pastiprināja Tavu ierakstu",
|
||||
"notification.relationships_severance_event": "Zaudēti savienojumi ar {name}",
|
||||
"notification.relationships_severance_event.learn_more": "Uzzināt vairāk",
|
||||
"notification.status": "{name} tikko publicēja",
|
||||
"notification.update": "{name} rediģēja ierakstu",
|
||||
"notification_requests.accept": "Pieņemt",
|
||||
"notification_requests.dismiss": "Noraidīt",
|
||||
"notification_requests.notifications_from": "Paziņojumi no {name}",
|
||||
"notifications.clear": "Notīrīt paziņojumus",
|
||||
"notifications.clear_confirmation": "Vai tiešām vēlies neatgriezeniski notīrīt visus savus paziņojumus?",
|
||||
"notifications.column_settings.admin.report": "Jauni ziņojumi:",
|
||||
|
@ -456,6 +479,9 @@
|
|||
"notifications.permission_denied": "Darbvirsmas paziņojumi nav pieejami, jo iepriekš tika noraidīts pārlūka atļauju pieprasījums",
|
||||
"notifications.permission_denied_alert": "Darbvirsmas paziņojumus nevar iespējot, jo pārlūkprogrammai atļauja tika iepriekš atteikta",
|
||||
"notifications.permission_required": "Darbvirsmas paziņojumi nav pieejami, jo nav piešķirta nepieciešamā atļauja.",
|
||||
"notifications.policy.filter_new_accounts_title": "Jauni konti",
|
||||
"notifications.policy.filter_not_followers_title": "Cilvēki, kuri Tev neseko",
|
||||
"notifications.policy.filter_not_following_title": "Cilvēki, kuriem Tu neseko",
|
||||
"notifications_permission_banner.enable": "Iespējot darbvirsmas paziņojumus",
|
||||
"notifications_permission_banner.how_to_control": "Lai saņemtu paziņojumus, kad Mastodon nav atvērts, iespējo darbvirsmas paziņojumus. Vari precīzi kontrolēt, kāda veida mijiedarbības rada darbvirsmas paziņojumus, izmantojot augstāk redzamo pogu {icon}, kad tie būs iespējoti.",
|
||||
"notifications_permission_banner.title": "Nekad nepalaid neko garām",
|
||||
|
@ -485,7 +511,7 @@
|
|||
"onboarding.start.title": "Tev tas izdevās!",
|
||||
"onboarding.steps.follow_people.body": "Tu pats veido savu plūsmu. Piepildīsim to ar interesantiem cilvēkiem.",
|
||||
"onboarding.steps.follow_people.title": "Pielāgo savu mājas barotni",
|
||||
"onboarding.steps.publish_status.body": "Sveicini pasauli ar tekstu, fotoattēliem, video, vai aptaujām {emoji}",
|
||||
"onboarding.steps.publish_status.body": "Pasveicini pasauli ar tekstu, attēliem, video vai aptaujām {emoji}",
|
||||
"onboarding.steps.publish_status.title": "Izveido savu pirmo ziņu",
|
||||
"onboarding.steps.setup_profile.body": "Palielini mijiedarbību ar aptverošu profilu!",
|
||||
"onboarding.steps.setup_profile.title": "Pielāgo savu profilu",
|
||||
|
@ -603,7 +629,7 @@
|
|||
"search_results.statuses": "Ieraksti",
|
||||
"search_results.title": "Meklēt {q}",
|
||||
"server_banner.about_active_users": "Cilvēki, kas izmantojuši šo serveri pēdējo 30 dienu laikā (aktīvie lietotāji mēnesī)",
|
||||
"server_banner.active_users": "aktīvie lietotāji",
|
||||
"server_banner.active_users": "aktīvi lietotāji",
|
||||
"server_banner.administered_by": "Administrē:",
|
||||
"server_banner.introduction": "{domain} ir daļa no decentralizētā sociālā tīkla, ko nodrošina {mastodon}.",
|
||||
"server_banner.learn_more": "Uzzināt vairāk",
|
||||
|
@ -625,6 +651,7 @@
|
|||
"status.direct": "Pieminēt @{name} privāti",
|
||||
"status.direct_indicator": "Pieminēts privāti",
|
||||
"status.edit": "Labot",
|
||||
"status.edited": "Pēdējoreiz labots {date}",
|
||||
"status.edited_x_times": "Labots {count, plural, one {{count} reizi} other {{count} reizes}}",
|
||||
"status.embed": "Iegult",
|
||||
"status.favourite": "Izlasē",
|
||||
|
|
|
@ -308,6 +308,8 @@
|
|||
"follow_requests.unlocked_explanation": "Apesar de seu perfil não ser trancado, {domain} exige que você revise a solicitação para te seguir destes perfis manualmente.",
|
||||
"follow_suggestions.curated_suggestion": "Escolha da equipe",
|
||||
"follow_suggestions.dismiss": "Não mostrar novamente",
|
||||
"follow_suggestions.featured_longer": "Escolhido à mão pela equipe de {domain}",
|
||||
"follow_suggestions.friends_of_friends_longer": "Popular entre as pessoas que você segue",
|
||||
"follow_suggestions.hints.featured": "Este perfil foi escolhido a dedo pela equipe {domain}.",
|
||||
"follow_suggestions.hints.friends_of_friends": "Este perfil é popular entre as pessoas que você segue.",
|
||||
"follow_suggestions.hints.most_followed": "Este perfil é um dos mais seguidos em {domain}.",
|
||||
|
@ -315,6 +317,8 @@
|
|||
"follow_suggestions.hints.similar_to_recently_followed": "Este perfil é semelhante aos perfis que você seguiu recentemente.",
|
||||
"follow_suggestions.personalized_suggestion": "Sugestão personalizada",
|
||||
"follow_suggestions.popular_suggestion": "Sugestão popular",
|
||||
"follow_suggestions.popular_suggestion_longer": "Popular em {domain}",
|
||||
"follow_suggestions.similar_to_recently_followed_longer": "Similar a perfis que você seguiu recentemente",
|
||||
"follow_suggestions.view_all": "Visualizar tudo",
|
||||
"follow_suggestions.who_to_follow": "Quem seguir",
|
||||
"followed_tags": "Hashtags seguidas",
|
||||
|
@ -469,6 +473,14 @@
|
|||
"notification.follow": "{name} te seguiu",
|
||||
"notification.follow_request": "{name} quer te seguir",
|
||||
"notification.mention": "{name} te mencionou",
|
||||
"notification.moderation-warning.learn_more": "Aprender mais",
|
||||
"notification.moderation_warning.action_delete_statuses": "Algumas das suas publicações foram removidas.",
|
||||
"notification.moderation_warning.action_disable": "Sua conta foi desativada.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Algumas de suas publicações foram marcadas por ter conteúdo sensível.",
|
||||
"notification.moderation_warning.action_none": "Sua conta recebeu um aviso de moderação.",
|
||||
"notification.moderation_warning.action_sensitive": "Suas publicações serão marcadas como sensíveis a partir de agora.",
|
||||
"notification.moderation_warning.action_silence": "Sua conta foi limitada.",
|
||||
"notification.moderation_warning.action_suspend": "Sua conta foi suspensa.",
|
||||
"notification.own_poll": "Sua enquete terminou",
|
||||
"notification.poll": "Uma enquete que você votou terminou",
|
||||
"notification.reblog": "{name} deu boost no teu toot",
|
||||
|
|
|
@ -468,6 +468,7 @@
|
|||
"notification.follow": "{name} подписался (-лась) на вас",
|
||||
"notification.follow_request": "{name} отправил запрос на подписку",
|
||||
"notification.mention": "{name} упомянул(а) вас",
|
||||
"notification.moderation_warning.action_delete_statuses": "Некоторые из ваших публикаций были удалены.",
|
||||
"notification.own_poll": "Ваш опрос закончился",
|
||||
"notification.poll": "Опрос, в котором вы приняли участие, завершился",
|
||||
"notification.reblog": "{name} продвинул(а) ваш пост",
|
||||
|
|
|
@ -295,6 +295,7 @@
|
|||
"follow_suggestions.personalized_suggestion": "Prispôsobený návrh",
|
||||
"follow_suggestions.popular_suggestion": "Obľúbený návrh",
|
||||
"follow_suggestions.popular_suggestion_longer": "Populárne na {domain}",
|
||||
"follow_suggestions.similar_to_recently_followed_longer": "Podobné profilom, ktoré si nedávno nasledoval/a",
|
||||
"follow_suggestions.view_all": "Zobraziť všetky",
|
||||
"follow_suggestions.who_to_follow": "Koho sledovať",
|
||||
"followed_tags": "Sledované hashtagy",
|
||||
|
@ -445,9 +446,14 @@
|
|||
"notification.follow_request": "{name} vás žiada sledovať",
|
||||
"notification.mention": "{name} vás spomína",
|
||||
"notification.moderation-warning.learn_more": "Zisti viac",
|
||||
"notification.moderation_warning.action_disable": "Tvoj účet bol vypnutý.",
|
||||
"notification.moderation_warning.action_silence": "Tvoj účet bol obmedzený.",
|
||||
"notification.moderation_warning.action_suspend": "Tvoj účet bol pozastavený.",
|
||||
"notification.own_poll": "Vaša anketa sa skončila",
|
||||
"notification.poll": "Anketa, v ktorej ste hlasovali, sa skončila",
|
||||
"notification.reblog": "{name} zdieľa váš príspevok",
|
||||
"notification.relationships_severance_event": "Stratené prepojenia s {name}",
|
||||
"notification.relationships_severance_event.account_suspension": "Správca z {from} pozastavil/a {target}, čo znamená, že od nich viac nemôžeš dostávať aktualizácie, alebo s nimi interaktovať.",
|
||||
"notification.relationships_severance_event.learn_more": "Zisti viac",
|
||||
"notification.status": "{name} uverejňuje niečo nové",
|
||||
"notification.update": "{name} upravuje príspevok",
|
||||
|
@ -490,6 +496,7 @@
|
|||
"notifications.policy.filter_new_accounts_title": "Nové účty",
|
||||
"notifications.policy.filter_not_followers_title": "Ľudia, ktorí ťa nenasledujú",
|
||||
"notifications.policy.filter_not_following_title": "Ľudia, ktorých nenasleduješ",
|
||||
"notifications.policy.filter_private_mentions_title": "Nevyžiadané priame spomenutia",
|
||||
"notifications.policy.title": "Filtrovať oznámenia od…",
|
||||
"notifications_permission_banner.enable": "Povoliť upozornenia na ploche",
|
||||
"notifications_permission_banner.how_to_control": "Ak chcete dostávať upozornenia, keď Mastodon nie je otvorený, povoľte upozornenia na ploche. Po ich zapnutí môžete presne kontrolovať, ktoré typy interakcií generujú upozornenia na ploche, a to prostredníctvom tlačidla {icon} vyššie.",
|
||||
|
|
|
@ -308,6 +308,8 @@
|
|||
"follow_requests.unlocked_explanation": "Čeprav vaš račun ni zaklenjen, zaposleni pri {domain} menijo, da bi morda želeli pregledati zahteve za sledenje teh računov ročno.",
|
||||
"follow_suggestions.curated_suggestion": "Izbor osebja",
|
||||
"follow_suggestions.dismiss": "Ne pokaži več",
|
||||
"follow_suggestions.featured_longer": "Osebno izbrala ekipa {domain}",
|
||||
"follow_suggestions.friends_of_friends_longer": "Priljubljeno med osebami, ki jim sledite",
|
||||
"follow_suggestions.hints.featured": "Ta profil so izbrali skrbniki strežnika {domain}.",
|
||||
"follow_suggestions.hints.friends_of_friends": "Ta profil je priljubljen med osebami, ki jim sledite.",
|
||||
"follow_suggestions.hints.most_followed": "Ta profil na strežniku {domain} je en izmed najbolj sledenih.",
|
||||
|
@ -315,6 +317,8 @@
|
|||
"follow_suggestions.hints.similar_to_recently_followed": "Ta profil je podoben profilom, ki ste jim nedavno začeli slediti.",
|
||||
"follow_suggestions.personalized_suggestion": "Osebno prilagojen predlog",
|
||||
"follow_suggestions.popular_suggestion": "Priljubljen predlog",
|
||||
"follow_suggestions.popular_suggestion_longer": "Priljubljeno na {domain}",
|
||||
"follow_suggestions.similar_to_recently_followed_longer": "Podobno profilom, ki ste jim pred kratkim sledili",
|
||||
"follow_suggestions.view_all": "Pokaži vse",
|
||||
"follow_suggestions.who_to_follow": "Komu slediti",
|
||||
"followed_tags": "Sledeni ključniki",
|
||||
|
@ -469,6 +473,15 @@
|
|||
"notification.follow": "{name} vam sledi",
|
||||
"notification.follow_request": "{name} vam želi slediti",
|
||||
"notification.mention": "{name} vas je omenil/a",
|
||||
"notification.moderation-warning.learn_more": "Več o tem",
|
||||
"notification.moderation_warning": "Prejeli ste opozorilo moderatorjev",
|
||||
"notification.moderation_warning.action_delete_statuses": "Nekatere vaše objave so odstranjene.",
|
||||
"notification.moderation_warning.action_disable": "Vaš račun je bil onemogočen.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Nekatere vaše objave so bile označene kot občutljive.",
|
||||
"notification.moderation_warning.action_none": "Vaš račun je prejel opozorilo moderatorjev.",
|
||||
"notification.moderation_warning.action_sensitive": "Vaše objave bodo odslej označene kot občutljive.",
|
||||
"notification.moderation_warning.action_silence": "Vaš račun je bil omejen.",
|
||||
"notification.moderation_warning.action_suspend": "Vaš račun je bil suspendiran.",
|
||||
"notification.own_poll": "Vaša anketa je zaključena",
|
||||
"notification.poll": "Anketa, v kateri ste sodelovali, je zaključena",
|
||||
"notification.reblog": "{name} je izpostavila/a vašo objavo",
|
||||
|
|
|
@ -297,6 +297,7 @@
|
|||
"filter_modal.select_filter.subtitle": "Përdorni një kategori ekzistuese, ose krijoni një të re",
|
||||
"filter_modal.select_filter.title": "Filtroje këtë postim",
|
||||
"filter_modal.title.status": "Filtroni një postim",
|
||||
"filtered_notifications_banner.mentions": "{count, plural, one {përmendje} other {përmendje}}",
|
||||
"filtered_notifications_banner.pending_requests": "Njoftime prej {count, plural, =0 {askujt} one {një personi} other {# vetësh}} që mund të njihni",
|
||||
"filtered_notifications_banner.title": "Njoftime të filtruar",
|
||||
"firehose.all": "Krejt",
|
||||
|
@ -307,6 +308,8 @@
|
|||
"follow_requests.unlocked_explanation": "Edhe pse llogaria juaj s’është e kyçur, ekipi i {domain} mendoi se mund të donit të shqyrtonit dorazi kërkesa ndjekjeje prej këtyre llogarive.",
|
||||
"follow_suggestions.curated_suggestion": "Zgjedhur nga ekipi",
|
||||
"follow_suggestions.dismiss": "Mos shfaq më",
|
||||
"follow_suggestions.featured_longer": "Zgjedhur enkas nga ekipi {domain}",
|
||||
"follow_suggestions.friends_of_friends_longer": "Popullore mes personash që ndiqni",
|
||||
"follow_suggestions.hints.featured": "Ky profil është zgjedhur nga ekipi {domain}.",
|
||||
"follow_suggestions.hints.friends_of_friends": "Ky profil është popullor mes personave që ndiqni.",
|
||||
"follow_suggestions.hints.most_followed": "Ky profil është një nga më të ndjekur në {domain}.",
|
||||
|
@ -314,6 +317,8 @@
|
|||
"follow_suggestions.hints.similar_to_recently_followed": "Ky profil është i ngjashëm me profile që keni ndjekur tani afër.",
|
||||
"follow_suggestions.personalized_suggestion": "Sugjerim i personalizuar",
|
||||
"follow_suggestions.popular_suggestion": "Sugjerim popullor",
|
||||
"follow_suggestions.popular_suggestion_longer": "Popullore në {domain}",
|
||||
"follow_suggestions.similar_to_recently_followed_longer": "I ngjashëm me profile që keni zënë të ndiqni së fundi",
|
||||
"follow_suggestions.view_all": "Shihni krejt",
|
||||
"follow_suggestions.who_to_follow": "Cilët të ndiqen",
|
||||
"followed_tags": "Hashtag-ë të ndjekur",
|
||||
|
@ -468,6 +473,15 @@
|
|||
"notification.follow": "{name} zuri t’ju ndjekë",
|
||||
"notification.follow_request": "{name} ka kërkuar t’ju ndjekë",
|
||||
"notification.mention": "{name} ju ka përmendur",
|
||||
"notification.moderation-warning.learn_more": "Mësoni më tepër",
|
||||
"notification.moderation_warning": "Ju është dhënë një sinjalizim moderimi",
|
||||
"notification.moderation_warning.action_delete_statuses": "Disa nga postimet tuaja janë hequr.",
|
||||
"notification.moderation_warning.action_disable": "Llogaria juaj është çaktivizuar.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Disa prej postimeve tuaja u është vënë shenjë si me spec.",
|
||||
"notification.moderation_warning.action_none": "Llogaria juaj ka marrë një sinjalizim moderimi.",
|
||||
"notification.moderation_warning.action_sensitive": "Postimeve tuaja do t’u vihet shenjë si me spec, nga tani e tutje.",
|
||||
"notification.moderation_warning.action_silence": "Llogaria juaj është kufizuar.",
|
||||
"notification.moderation_warning.action_suspend": "Llogaria juaj është pezulluar.",
|
||||
"notification.own_poll": "Pyetësori juaj ka përfunduar",
|
||||
"notification.poll": "Ka përfunduar një pyetësor ku keni votuar",
|
||||
"notification.reblog": "{name} përforcoi mesazhin tuaj",
|
||||
|
|
|
@ -308,6 +308,8 @@
|
|||
"follow_requests.unlocked_explanation": "Iako vaš nalog nije zaključan, osoblje {domain} smatra da biste možda želeli da ručno pregledate zahteve za praćenje sa ovih naloga.",
|
||||
"follow_suggestions.curated_suggestion": "Izbor osoblja",
|
||||
"follow_suggestions.dismiss": "Ne prikazuj ponovo",
|
||||
"follow_suggestions.featured_longer": "Ručno odabrao tim {domain}",
|
||||
"follow_suggestions.friends_of_friends_longer": "Popularno među ljudima koje pratite",
|
||||
"follow_suggestions.hints.featured": "Ovaj profil je ručno izabrao tim {domain}.",
|
||||
"follow_suggestions.hints.friends_of_friends": "Ovaj profil je popularan među ljudima koje pratite.",
|
||||
"follow_suggestions.hints.most_followed": "Ovaj profil je jedan od najpraćenijih na {domain}.",
|
||||
|
@ -315,6 +317,8 @@
|
|||
"follow_suggestions.hints.similar_to_recently_followed": "Ovaj profil je sličan profilima koje ste nedavno zapratili.",
|
||||
"follow_suggestions.personalized_suggestion": "Personalizovani predlog",
|
||||
"follow_suggestions.popular_suggestion": "Popularni predlog",
|
||||
"follow_suggestions.popular_suggestion_longer": "Popularno na {domain}",
|
||||
"follow_suggestions.similar_to_recently_followed_longer": "Slično profilima koje ste nedavno zapratili",
|
||||
"follow_suggestions.view_all": "Prikaži sve",
|
||||
"follow_suggestions.who_to_follow": "Koga pratiti",
|
||||
"followed_tags": "Praćene heš oznake",
|
||||
|
@ -469,6 +473,15 @@
|
|||
"notification.follow": "{name} vas je zapratio",
|
||||
"notification.follow_request": "{name} je zatražio da vas prati",
|
||||
"notification.mention": "{name} vas je pomenuo",
|
||||
"notification.moderation-warning.learn_more": "Saznajte više",
|
||||
"notification.moderation_warning": "Dobili ste moderatorsko upozorenje",
|
||||
"notification.moderation_warning.action_delete_statuses": "Neke od vaših objava su uklonjene.",
|
||||
"notification.moderation_warning.action_disable": "Vaš nalog je onemogućen.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Neke od vaših objava su obeležene kao osetljive.",
|
||||
"notification.moderation_warning.action_none": "Vaš nalog je dobio moderatorsko upozorenje.",
|
||||
"notification.moderation_warning.action_sensitive": "Vaše objave će ubuduće biti označene kao osetljive.",
|
||||
"notification.moderation_warning.action_silence": "Vaš nalog je ograničen.",
|
||||
"notification.moderation_warning.action_suspend": "Vaš nalog je suspendovan.",
|
||||
"notification.own_poll": "Vaša anketa je završena",
|
||||
"notification.poll": "Završena je anketa u kojoj ste glasali",
|
||||
"notification.reblog": "{name} je podržao vašu objavu",
|
||||
|
|
|
@ -308,6 +308,8 @@
|
|||
"follow_requests.unlocked_explanation": "Иако ваш налог није закључан, особље {domain} сматра да бисте можда желели да ручно прегледате захтеве за праћење са ових налога.",
|
||||
"follow_suggestions.curated_suggestion": "Избор особља",
|
||||
"follow_suggestions.dismiss": "Не приказуј поново",
|
||||
"follow_suggestions.featured_longer": "Ручно одабрао тим {domain}",
|
||||
"follow_suggestions.friends_of_friends_longer": "Популарно међу људима које пратите",
|
||||
"follow_suggestions.hints.featured": "Овај профил је ручно изабрао тим {domain}.",
|
||||
"follow_suggestions.hints.friends_of_friends": "Овај профил је популаран међу људима које пратите.",
|
||||
"follow_suggestions.hints.most_followed": "Овај профил је један од најпраћенијих на {domain}.",
|
||||
|
@ -315,6 +317,8 @@
|
|||
"follow_suggestions.hints.similar_to_recently_followed": "Овај профил је сличан профилима које сте недавно запратили.",
|
||||
"follow_suggestions.personalized_suggestion": "Персонализовани предлог",
|
||||
"follow_suggestions.popular_suggestion": "Популарни предлог",
|
||||
"follow_suggestions.popular_suggestion_longer": "Популарно на {domain}",
|
||||
"follow_suggestions.similar_to_recently_followed_longer": "Слично профилима које сте недавно запратили",
|
||||
"follow_suggestions.view_all": "Прикажи све",
|
||||
"follow_suggestions.who_to_follow": "Кога пратити",
|
||||
"followed_tags": "Праћене хеш ознаке",
|
||||
|
@ -469,6 +473,15 @@
|
|||
"notification.follow": "{name} вас је запратио",
|
||||
"notification.follow_request": "{name} је затражио да вас прати",
|
||||
"notification.mention": "{name} вас је поменуо",
|
||||
"notification.moderation-warning.learn_more": "Сазнајте више",
|
||||
"notification.moderation_warning": "Добили сте модераторско упозорење",
|
||||
"notification.moderation_warning.action_delete_statuses": "Неке од ваших објава су уклоњене.",
|
||||
"notification.moderation_warning.action_disable": "Ваш налог је онемогућен.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Неке од ваших објава су обележене као осетљиве.",
|
||||
"notification.moderation_warning.action_none": "Ваш налог је добио модераторско упозорење.",
|
||||
"notification.moderation_warning.action_sensitive": "Ваше објаве ће убудуће бити означене као осетљиве.",
|
||||
"notification.moderation_warning.action_silence": "Ваш налог је ограничен.",
|
||||
"notification.moderation_warning.action_suspend": "Ваш налог је суспендован.",
|
||||
"notification.own_poll": "Ваша анкета је завршена",
|
||||
"notification.poll": "Завршена је анкета у којој сте гласали",
|
||||
"notification.reblog": "{name} је подржао вашу објаву",
|
||||
|
|
|
@ -474,7 +474,7 @@
|
|||
"notification.follow_request": "{name} har begärt att följa dig",
|
||||
"notification.mention": "{name} nämnde dig",
|
||||
"notification.moderation-warning.learn_more": "Läs mer",
|
||||
"notification.moderation_warning": "Du har mottagit en modereringsvarning",
|
||||
"notification.moderation_warning": "Du har fått en moderationsvarning",
|
||||
"notification.moderation_warning.action_delete_statuses": "Några av dina inlägg har tagits bort.",
|
||||
"notification.moderation_warning.action_disable": "Ditt konto har inaktiverats.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Några av dina inlägg har markerats som känsliga.",
|
||||
|
|
|
@ -158,7 +158,7 @@
|
|||
"compose_form.poll.option_placeholder": "ตัวเลือก {number}",
|
||||
"compose_form.poll.single": "เลือกอย่างใดอย่างหนึ่ง",
|
||||
"compose_form.poll.switch_to_multiple": "เปลี่ยนการสำรวจความคิดเห็นเป็นอนุญาตหลายตัวเลือก",
|
||||
"compose_form.poll.switch_to_single": "เปลี่ยนการสำรวจความคิดเห็นเป็นอนุญาตตัวเลือกเดี่ยว",
|
||||
"compose_form.poll.switch_to_single": "เปลี่ยนการสำรวจความคิดเห็นเป็นอนุญาตตัวเลือกเดียว",
|
||||
"compose_form.poll.type": "ลักษณะ",
|
||||
"compose_form.publish": "โพสต์",
|
||||
"compose_form.publish_form": "โพสต์ใหม่",
|
||||
|
|
|
@ -474,7 +474,7 @@
|
|||
"notification.follow_request": "{name} size takip isteği gönderdi",
|
||||
"notification.mention": "{name} senden bahsetti",
|
||||
"notification.moderation-warning.learn_more": "Daha fazlası",
|
||||
"notification.moderation_warning": "Bir denetim uyarısı aldınız",
|
||||
"notification.moderation_warning": "Hesabınız bir denetim uyarısı aldı",
|
||||
"notification.moderation_warning.action_delete_statuses": "Bazı gönderileriniz kaldırıldı.",
|
||||
"notification.moderation_warning.action_disable": "Hesabınız devre dışı bırakıldı.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Bazı gönderileriniz hassas olarak işaretlendi.",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue