Compare commits
73 commits
d5769ec3e9
...
ed4a0bba04
Author | SHA1 | Date | |
---|---|---|---|
ed4a0bba04 | |||
|
fa0ba67753 | ||
|
c4af668e5c | ||
|
0c0d077276 | ||
|
0bc526a967 | ||
|
8c08e5cdb2 | ||
|
ce0d134147 | ||
|
86fbde7b46 | ||
|
b3075a9993 | ||
|
f91acba70a | ||
|
9d3830344f | ||
|
adcd693b71 | ||
|
1467f1e1e1 | ||
|
ff8937aa2c | ||
|
44f6d285af | ||
|
239244e2ed | ||
|
5119fbc9b7 | ||
|
b6baab447d | ||
|
7adcc0aae3 | ||
|
0b0ca6f3b8 | ||
|
87ad398ddc | ||
|
f4a12adfb7 | ||
|
160c8f4923 | ||
|
e519f113e8 | ||
|
d791bca11b | ||
|
09a3493fca | ||
|
5fbdb2055b | ||
|
1a30a517d6 | ||
|
685eaa04d4 | ||
|
beb74fd71c | ||
|
beaef4b672 | ||
|
6d35a77c92 | ||
|
2f8656334d | ||
|
9cc1817bb4 | ||
|
805dba7f8d | ||
|
45287049ab | ||
|
0e0a94f483 | ||
|
881e8c113c | ||
|
6936e5aa69 | ||
|
42ab855b23 | ||
|
4cdf62e576 | ||
|
0b38946c87 | ||
|
17ea22671d | ||
|
3205a654ca | ||
|
274a48a9f4 | ||
|
2866106ec1 | ||
|
c8f59d2ca4 | ||
|
ca7053f19c | ||
|
59d2ea0d82 | ||
|
6b6586f5d0 | ||
|
a69506a434 | ||
|
7c9c6c7f80 | ||
|
1a565e4bea | ||
|
d158f7e622 | ||
|
087415d0fe | ||
|
0471a78055 | ||
|
c50274a0ac | ||
|
9a8293f58d | ||
|
38f7f8b909 | ||
|
9c5be13980 | ||
|
7019af431d | ||
|
559bbf0aa6 | ||
|
ea5397c373 | ||
|
64993d3f77 | ||
|
5a838ceaa9 | ||
|
1290fede65 | ||
|
41c2af2270 | ||
|
b19ae521b7 | ||
|
7a1f087659 | ||
|
a11a2fb052 | ||
|
a34d27c18f | ||
|
9d413cbaf8 | ||
|
599bc69503 |
340 changed files with 4107 additions and 5385 deletions
|
@ -165,7 +165,7 @@ module.exports = defineConfig({
|
|||
// },
|
||||
// ],
|
||||
'jsx-a11y/no-noninteractive-tabindex': 'off',
|
||||
'jsx-a11y/no-onchange': 'warn',
|
||||
'jsx-a11y/no-onchange': 'off',
|
||||
// recommended is full 'error'
|
||||
'jsx-a11y/no-static-element-interactions': [
|
||||
'warn',
|
||||
|
|
19
.github/workflows/test-migrations-one-step.yml
vendored
19
.github/workflows/test-migrations-one-step.yml
vendored
|
@ -78,23 +78,8 @@ jobs:
|
|||
- name: Create database
|
||||
run: './bin/rails db:create'
|
||||
|
||||
- name: Run migrations up to v2.0.0
|
||||
run: './bin/rails db:migrate VERSION=20171010025614'
|
||||
|
||||
- name: Populate database with test data
|
||||
run: './bin/rails tests:migrations:populate_v2'
|
||||
|
||||
- name: Run migrations up to v2.4.0
|
||||
run: './bin/rails db:migrate VERSION=20180514140000'
|
||||
|
||||
- name: Populate database with test data
|
||||
run: './bin/rails tests:migrations:populate_v2_4'
|
||||
|
||||
- name: Run migrations up to v2.4.3
|
||||
run: './bin/rails db:migrate VERSION=20180707154237'
|
||||
|
||||
- name: Populate database with test data
|
||||
run: './bin/rails tests:migrations:populate_v2_4_3'
|
||||
- name: Run historical migrations with data population
|
||||
run: './bin/rails tests:migrations:prepare_database'
|
||||
|
||||
- name: Run all remaining migrations
|
||||
run: './bin/rails db:migrate'
|
||||
|
|
22
.github/workflows/test-migrations-two-step.yml
vendored
22
.github/workflows/test-migrations-two-step.yml
vendored
|
@ -45,6 +45,7 @@ jobs:
|
|||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
options: >-
|
||||
|
@ -77,28 +78,11 @@ jobs:
|
|||
- name: Create database
|
||||
run: './bin/rails db:create'
|
||||
|
||||
- name: Run migrations up to v2.0.0
|
||||
run: './bin/rails db:migrate VERSION=20171010025614'
|
||||
|
||||
- name: Populate database with test data
|
||||
run: './bin/rails tests:migrations:populate_v2'
|
||||
|
||||
- name: Run pre-deployment migrations up to v2.4.0
|
||||
run: './bin/rails db:migrate VERSION=20180514140000'
|
||||
- name: Run historical migrations with data population
|
||||
run: './bin/rails tests:migrations:prepare_database'
|
||||
env:
|
||||
SKIP_POST_DEPLOYMENT_MIGRATIONS: true
|
||||
|
||||
- name: Populate database with test data
|
||||
run: './bin/rails tests:migrations:populate_v2_4'
|
||||
|
||||
- name: Run migrations up to v2.4.3
|
||||
run: './bin/rails db:migrate VERSION=20180707154237'
|
||||
env:
|
||||
SKIP_POST_DEPLOYMENT_MIGRATIONS: true
|
||||
|
||||
- name: Populate database with test data
|
||||
run: './bin/rails tests:migrations:populate_v2_4_3'
|
||||
|
||||
- name: Run all remaining pre-deployment migrations
|
||||
run: './bin/rails db:migrate'
|
||||
env:
|
||||
|
|
16
.github/workflows/test-ruby.yml
vendored
16
.github/workflows/test-ruby.yml
vendored
|
@ -52,7 +52,7 @@ jobs:
|
|||
run: |
|
||||
tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs*
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: matrix.mode == 'test'
|
||||
with:
|
||||
path: |-
|
||||
|
@ -117,7 +117,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: './'
|
||||
name: ${{ github.sha }}
|
||||
|
@ -193,7 +193,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: './public'
|
||||
name: ${{ github.sha }}
|
||||
|
@ -213,14 +213,14 @@ jobs:
|
|||
- run: bundle exec rake spec:system
|
||||
|
||||
- name: Archive logs
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: e2e-logs-${{ matrix.ruby-version }}
|
||||
path: log/
|
||||
|
||||
- name: Archive test screenshots
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: e2e-screenshots
|
||||
|
@ -297,7 +297,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: './public'
|
||||
name: ${{ github.sha }}
|
||||
|
@ -317,14 +317,14 @@ jobs:
|
|||
- run: bin/rspec --tag search
|
||||
|
||||
- name: Archive logs
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: test-search-logs-${{ matrix.ruby-version }}
|
||||
path: log/
|
||||
|
||||
- name: Archive test screenshots
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: test-search-screenshots
|
||||
|
|
12
.rubocop.yml
12
.rubocop.yml
|
@ -96,12 +96,11 @@ Rails/FilePath:
|
|||
Rails/HttpStatus:
|
||||
EnforcedStyle: numeric
|
||||
|
||||
# Reason: Allowed in `tootctl` CLI code and in boot ENV checker
|
||||
# Reason: Allowed in boot ENV checker
|
||||
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsexit
|
||||
Rails/Exit:
|
||||
Exclude:
|
||||
- 'config/boot.rb'
|
||||
- 'lib/mastodon/cli/*.rb'
|
||||
|
||||
# Reason: Conflicts with `Lint/UselessMethodDefinition` for inherited controller actions
|
||||
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railslexicallyscopedactionfilter
|
||||
|
@ -175,6 +174,15 @@ Style/ClassAndModuleChildren:
|
|||
Style/Documentation:
|
||||
Enabled: false
|
||||
|
||||
# Reason: Route redirects are not token-formatted and must be skipped
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#styleformatstringtoken
|
||||
Style/FormatStringToken:
|
||||
inherit_mode:
|
||||
merge:
|
||||
- AllowedMethods # The rubocop-rails config adds `redirect`
|
||||
AllowedMethods:
|
||||
- redirect_with_vary
|
||||
|
||||
# Reason: Enforce modern Ruby style
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#stylehashsyntax
|
||||
Style/HashSyntax:
|
||||
|
|
|
@ -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.59.0.
|
||||
# using RuboCop version 1.60.2.
|
||||
# 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
|
||||
|
@ -70,28 +70,6 @@ Rails/UniqueValidationWithoutIndex:
|
|||
- 'app/models/identity.rb'
|
||||
- 'app/models/webauthn_credential.rb'
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: exists, where
|
||||
Rails/WhereExists:
|
||||
Exclude:
|
||||
- 'app/controllers/activitypub/inboxes_controller.rb'
|
||||
- 'app/controllers/admin/email_domain_blocks_controller.rb'
|
||||
- 'app/lib/activitypub/activity/create.rb'
|
||||
- 'app/lib/delivery_failure_tracker.rb'
|
||||
- 'app/lib/feed_manager.rb'
|
||||
- 'app/lib/suspicious_sign_in_detector.rb'
|
||||
- 'app/models/poll.rb'
|
||||
- 'app/models/session_activation.rb'
|
||||
- 'app/models/status.rb'
|
||||
- 'app/policies/status_policy.rb'
|
||||
- 'app/serializers/rest/announcement_serializer.rb'
|
||||
- 'app/workers/move_worker.rb'
|
||||
- 'spec/models/account_spec.rb'
|
||||
- 'spec/services/activitypub/process_collection_service_spec.rb'
|
||||
- 'spec/services/purge_domain_service_spec.rb'
|
||||
- 'spec/services/unallow_domain_service_spec.rb'
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||
# AllowedMethods: ==, equal?, eql?
|
||||
|
@ -143,10 +121,6 @@ Style/GlobalStdStream:
|
|||
# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
|
||||
Style/GuardClause:
|
||||
Exclude:
|
||||
- 'app/controllers/admin/confirmations_controller.rb'
|
||||
- 'app/controllers/auth/confirmations_controller.rb'
|
||||
- 'app/controllers/auth/passwords_controller.rb'
|
||||
- 'app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb'
|
||||
- 'app/lib/activitypub/activity/block.rb'
|
||||
- 'app/lib/request.rb'
|
||||
- 'app/lib/request_pool.rb'
|
||||
|
@ -301,13 +275,6 @@ Style/StringLiterals:
|
|||
- 'config/initializers/webauthn.rb'
|
||||
- 'config/routes.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: EnforcedStyle, AllowSafeAssignment.
|
||||
# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex
|
||||
Style/TernaryParentheses:
|
||||
Exclude:
|
||||
- 'config/environments/development.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: EnforcedStyleForMultiline.
|
||||
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -123,7 +123,7 @@ group :test do
|
|||
gem 'database_cleaner-active_record'
|
||||
|
||||
# Used to mock environment variables
|
||||
gem 'climate_control', '~> 0.2'
|
||||
gem 'climate_control'
|
||||
|
||||
# Generating fake data for specs
|
||||
gem 'faker', '~> 3.2'
|
||||
|
|
29
Gemfile.lock
29
Gemfile.lock
|
@ -180,12 +180,12 @@ GEM
|
|||
activesupport
|
||||
cbor (0.5.9.6)
|
||||
charlock_holmes (0.7.7)
|
||||
chewy (7.4.0)
|
||||
chewy (7.5.1)
|
||||
activesupport (>= 5.2)
|
||||
elasticsearch (>= 7.12.0, < 7.14.0)
|
||||
elasticsearch-dsl
|
||||
chunky_png (1.4.0)
|
||||
climate_control (0.2.0)
|
||||
climate_control (1.2.0)
|
||||
cocoon (1.2.15)
|
||||
color_diff (0.1)
|
||||
concurrent-ruby (1.2.3)
|
||||
|
@ -319,7 +319,7 @@ GEM
|
|||
activesupport (>= 5.1)
|
||||
haml (>= 4.0.6)
|
||||
railties (>= 5.1)
|
||||
haml_lint (0.53.0)
|
||||
haml_lint (0.55.0)
|
||||
haml (>= 5.0)
|
||||
parallel (~> 1.10)
|
||||
rainbow
|
||||
|
@ -360,7 +360,7 @@ GEM
|
|||
rainbow (>= 2.2.2, < 4.0)
|
||||
terminal-table (>= 1.5.1)
|
||||
idn-ruby (0.1.5)
|
||||
io-console (0.7.1)
|
||||
io-console (0.7.2)
|
||||
irb (1.11.1)
|
||||
rdoc
|
||||
reline (>= 0.4.2)
|
||||
|
@ -445,7 +445,7 @@ GEM
|
|||
mime-types-data (3.2023.1205)
|
||||
mini_mime (1.1.5)
|
||||
mini_portile2 (2.8.5)
|
||||
minitest (5.20.0)
|
||||
minitest (5.21.2)
|
||||
msgpack (1.7.2)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.3.0)
|
||||
|
@ -504,7 +504,7 @@ GEM
|
|||
orm_adapter (0.5.0)
|
||||
ox (2.14.17)
|
||||
parallel (1.24.0)
|
||||
parser (3.2.2.4)
|
||||
parser (3.3.0.5)
|
||||
ast (~> 2.4.1)
|
||||
racc
|
||||
parslet (2.0.0)
|
||||
|
@ -610,7 +610,7 @@ GEM
|
|||
redis (>= 4)
|
||||
redlock (1.3.2)
|
||||
redis (>= 3.0.0, < 6.0)
|
||||
regexp_parser (2.8.3)
|
||||
regexp_parser (2.9.0)
|
||||
reline (0.4.2)
|
||||
io-console (~> 0.5)
|
||||
request_store (1.5.1)
|
||||
|
@ -636,7 +636,7 @@ GEM
|
|||
rspec-mocks (3.12.6)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.12.0)
|
||||
rspec-rails (6.1.0)
|
||||
rspec-rails (6.1.1)
|
||||
actionpack (>= 6.1)
|
||||
activesupport (>= 6.1)
|
||||
railties (>= 6.1)
|
||||
|
@ -650,11 +650,11 @@ GEM
|
|||
rspec-mocks (~> 3.0)
|
||||
sidekiq (>= 5, < 8)
|
||||
rspec-support (3.12.1)
|
||||
rubocop (1.59.0)
|
||||
rubocop (1.60.2)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (>= 3.17.0)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.2.2.4)
|
||||
parser (>= 3.3.0.2)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 1.8, < 3.0)
|
||||
rexml (>= 3.2.5, < 4.0)
|
||||
|
@ -696,7 +696,8 @@ GEM
|
|||
scenic (1.7.0)
|
||||
activerecord (>= 4.0.0)
|
||||
railties (>= 4.0.0)
|
||||
selenium-webdriver (4.16.0)
|
||||
selenium-webdriver (4.17.0)
|
||||
base64 (~> 0.2)
|
||||
rexml (~> 3.2, >= 3.2.5)
|
||||
rubyzip (>= 1.2.2, < 3.0)
|
||||
websocket (~> 1.0)
|
||||
|
@ -745,8 +746,8 @@ GEM
|
|||
temple (0.10.3)
|
||||
terminal-table (3.0.2)
|
||||
unicode-display_width (>= 1.1.1, < 3)
|
||||
terrapin (0.6.0)
|
||||
climate_control (>= 0.0.3, < 1.0)
|
||||
terrapin (1.0.1)
|
||||
climate_control
|
||||
test-prof (1.3.1)
|
||||
thor (1.3.0)
|
||||
tilt (2.3.0)
|
||||
|
@ -835,7 +836,7 @@ DEPENDENCIES
|
|||
capybara (~> 3.39)
|
||||
charlock_holmes (~> 0.7.7)
|
||||
chewy (~> 7.3)
|
||||
climate_control (~> 0.2)
|
||||
climate_control
|
||||
cocoon (~> 1.2)
|
||||
color_diff (~> 0.1)
|
||||
concurrent-ruby
|
||||
|
|
|
@ -24,7 +24,7 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
|
|||
|
||||
def unknown_affected_account?
|
||||
json = Oj.load(body, mode: :strict)
|
||||
json.is_a?(Hash) && %w(Delete Update).include?(json['type']) && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.where(uri: json['actor']).exists?
|
||||
json.is_a?(Hash) && %w(Delete Update).include?(json['type']) && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.exists?(uri: json['actor'])
|
||||
rescue Oj::ParseError
|
||||
false
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module Admin
|
||||
class ConfirmationsController < BaseController
|
||||
before_action :set_user
|
||||
before_action :check_confirmation, only: [:resend]
|
||||
before_action :redirect_confirmed_user, only: [:resend], if: :user_confirmed?
|
||||
|
||||
def create
|
||||
authorize @user, :confirm?
|
||||
|
@ -25,11 +25,13 @@ module Admin
|
|||
|
||||
private
|
||||
|
||||
def check_confirmation
|
||||
if @user.confirmed?
|
||||
flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed')
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
def redirect_confirmed_user
|
||||
flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed')
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
def user_confirmed?
|
||||
@user.confirmed?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,7 +38,7 @@ module Admin
|
|||
log_action :create, @email_domain_block
|
||||
|
||||
(@email_domain_block.other_domains || []).uniq.each do |domain|
|
||||
next if EmailDomainBlock.where(domain: domain).exists?
|
||||
next if EmailDomainBlock.exists?(domain: domain)
|
||||
|
||||
other_email_domain_block = EmailDomainBlock.create!(domain: domain, allow_with_approval: @email_domain_block.allow_with_approval, parent: @email_domain_block)
|
||||
log_action :create, other_email_domain_block
|
||||
|
|
|
@ -49,7 +49,7 @@ module Admin
|
|||
next
|
||||
end
|
||||
|
||||
@warning_domains = Instance.where(domain: @domain_blocks.map(&:domain)).where('EXISTS (SELECT 1 FROM follows JOIN accounts ON follows.account_id = accounts.id OR follows.target_account_id = accounts.id WHERE accounts.domain = instances.domain)').pluck(:domain)
|
||||
@warning_domains = instances_from_imported_blocks.pluck(:domain)
|
||||
rescue ActionController::ParameterMissing
|
||||
flash.now[:alert] = I18n.t('admin.export_domain_blocks.no_file')
|
||||
set_dummy_import!
|
||||
|
@ -58,6 +58,10 @@ module Admin
|
|||
|
||||
private
|
||||
|
||||
def instances_from_imported_blocks
|
||||
Instance.with_domain_follows(@domain_blocks.map(&:domain))
|
||||
end
|
||||
|
||||
def export_filename
|
||||
'domain_blocks.csv'
|
||||
end
|
||||
|
|
|
@ -7,7 +7,7 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
|
|||
|
||||
before_action :set_body_classes
|
||||
before_action :set_confirmation_user!, only: [:show, :confirm_captcha]
|
||||
before_action :require_unconfirmed!
|
||||
before_action :redirect_confirmed_user, if: :signed_in_confirmed_user?
|
||||
|
||||
before_action :extend_csp_for_captcha!, only: [:show, :confirm_captcha]
|
||||
before_action :require_captcha_if_needed!, only: [:show]
|
||||
|
@ -65,10 +65,12 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
|
|||
@confirmation_user.nil? || @confirmation_user.confirmed?
|
||||
end
|
||||
|
||||
def require_unconfirmed!
|
||||
if user_signed_in? && current_user.confirmed? && current_user.unconfirmed_email.blank?
|
||||
redirect_to(current_user.approved? ? root_path : edit_user_registration_path)
|
||||
end
|
||||
def redirect_confirmed_user
|
||||
redirect_to(current_user.approved? ? root_path : edit_user_registration_path)
|
||||
end
|
||||
|
||||
def signed_in_confirmed_user?
|
||||
user_signed_in? && current_user.confirmed? && current_user.unconfirmed_email.blank?
|
||||
end
|
||||
|
||||
def set_body_classes
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class Auth::PasswordsController < Devise::PasswordsController
|
||||
skip_before_action :check_self_destruct!
|
||||
before_action :check_validity_of_reset_password_token, only: :edit
|
||||
before_action :redirect_invalid_reset_token, only: :edit, unless: :reset_password_token_is_valid?
|
||||
before_action :set_body_classes
|
||||
|
||||
layout 'auth'
|
||||
|
@ -19,11 +19,9 @@ class Auth::PasswordsController < Devise::PasswordsController
|
|||
|
||||
private
|
||||
|
||||
def check_validity_of_reset_password_token
|
||||
unless reset_password_token_is_valid?
|
||||
flash[:error] = I18n.t('auth.invalid_reset_password_token')
|
||||
redirect_to new_password_path(resource_name)
|
||||
end
|
||||
def redirect_invalid_reset_token
|
||||
flash[:error] = I18n.t('auth.invalid_reset_password_token')
|
||||
redirect_to new_password_path(resource_name)
|
||||
end
|
||||
|
||||
def set_body_classes
|
||||
|
|
|
@ -21,10 +21,19 @@ module WebAppControllerConcern
|
|||
def redirect_unauthenticated_to_permalinks!
|
||||
return if user_signed_in? && current_account.moved_to_account_id.nil?
|
||||
|
||||
redirect_path = PermalinkRedirector.new(request.path).redirect_path
|
||||
return if redirect_path.blank?
|
||||
permalink_redirector = PermalinkRedirector.new(request.path)
|
||||
return if permalink_redirector.redirect_path.blank?
|
||||
|
||||
expires_in(15.seconds, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day) unless user_signed_in?
|
||||
redirect_to(redirect_path)
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
redirect_to(permalink_redirector.redirect_confirmation_path, allow_other_host: false)
|
||||
end
|
||||
|
||||
format.json do
|
||||
redirect_to(permalink_redirector.redirect_uri, allow_other_host: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
10
app/controllers/redirect/accounts_controller.rb
Normal file
10
app/controllers/redirect/accounts_controller.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Redirect::AccountsController < Redirect::BaseController
|
||||
private
|
||||
|
||||
def set_resource
|
||||
@resource = Account.find(params[:id])
|
||||
not_found if @resource.local?
|
||||
end
|
||||
end
|
24
app/controllers/redirect/base_controller.rb
Normal file
24
app/controllers/redirect/base_controller.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Redirect::BaseController < ApplicationController
|
||||
vary_by 'Accept-Language'
|
||||
|
||||
before_action :set_resource
|
||||
before_action :set_app_body_class
|
||||
|
||||
def show
|
||||
@redirect_path = ActivityPub::TagManager.instance.url_for(@resource)
|
||||
|
||||
render 'redirects/show', layout: 'application'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_app_body_class
|
||||
@body_classes = 'app-body'
|
||||
end
|
||||
|
||||
def set_resource
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
10
app/controllers/redirect/statuses_controller.rb
Normal file
10
app/controllers/redirect/statuses_controller.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Redirect::StatusesController < Redirect::BaseController
|
||||
private
|
||||
|
||||
def set_resource
|
||||
@resource = Status.find(params[:id])
|
||||
not_found if @resource.local? || !@resource.distributable?
|
||||
end
|
||||
end
|
|
@ -6,8 +6,8 @@ module Settings
|
|||
skip_before_action :check_self_destruct!
|
||||
skip_before_action :require_functional!
|
||||
|
||||
before_action :require_otp_enabled
|
||||
before_action :require_webauthn_enabled, only: [:index, :destroy]
|
||||
before_action :redirect_invalid_otp, unless: -> { current_user.otp_enabled? }
|
||||
before_action :redirect_invalid_webauthn, only: [:index, :destroy], unless: -> { current_user.webauthn_enabled? }
|
||||
|
||||
def index; end
|
||||
def new; end
|
||||
|
@ -85,18 +85,14 @@ module Settings
|
|||
|
||||
private
|
||||
|
||||
def require_otp_enabled
|
||||
unless current_user.otp_enabled?
|
||||
flash[:error] = t('webauthn_credentials.otp_required')
|
||||
redirect_to settings_two_factor_authentication_methods_path
|
||||
end
|
||||
def redirect_invalid_otp
|
||||
flash[:error] = t('webauthn_credentials.otp_required')
|
||||
redirect_to settings_two_factor_authentication_methods_path
|
||||
end
|
||||
|
||||
def require_webauthn_enabled
|
||||
unless current_user.webauthn_enabled?
|
||||
flash[:error] = t('webauthn_credentials.not_enabled')
|
||||
redirect_to settings_two_factor_authentication_methods_path
|
||||
end
|
||||
def redirect_invalid_webauthn
|
||||
flash[:error] = t('webauthn_credentials.not_enabled')
|
||||
redirect_to settings_two_factor_authentication_methods_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
25
app/javascript/images/warning-stripes.svg
Executable file
25
app/javascript/images/warning-stripes.svg
Executable file
|
@ -0,0 +1,25 @@
|
|||
<svg width="5" height="80" viewBox="0 0 5 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_253_1286)">
|
||||
<rect width="5" height="80" fill="url(#paint0_linear_253_1286)"/>
|
||||
<line x1="-0.860365" y1="6.80136" x2="10.6078" y2="-1.22871" stroke="black" stroke-width="3"/>
|
||||
<line x1="-0.860365" y1="14.8314" x2="10.6078" y2="6.80132" stroke="black" stroke-width="3"/>
|
||||
<line x1="-0.860365" y1="22.8615" x2="10.6078" y2="14.8314" stroke="black" stroke-width="3"/>
|
||||
<line x1="-0.860365" y1="30.8916" x2="10.6078" y2="22.8615" stroke="black" stroke-width="3"/>
|
||||
<line x1="-0.860365" y1="38.9216" x2="10.6078" y2="30.8915" stroke="black" stroke-width="3"/>
|
||||
<line x1="-0.860365" y1="46.9517" x2="10.6078" y2="38.9216" stroke="black" stroke-width="3"/>
|
||||
<line x1="-0.860365" y1="54.9818" x2="10.6078" y2="46.9517" stroke="black" stroke-width="3"/>
|
||||
<line x1="-0.860365" y1="63.0118" x2="10.6078" y2="54.9817" stroke="black" stroke-width="3"/>
|
||||
<line x1="-0.860365" y1="71.0419" x2="10.6078" y2="63.0118" stroke="black" stroke-width="3"/>
|
||||
<line x1="-0.860365" y1="79.072" x2="10.6078" y2="71.0419" stroke="black" stroke-width="3"/>
|
||||
<line x1="-0.860365" y1="87.102" x2="10.6078" y2="79.072" stroke="black" stroke-width="3"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_253_1286" x1="2.5" y1="0" x2="2.5" y2="80" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FEC84B"/>
|
||||
<stop offset="1" stop-color="#F79009"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_253_1286">
|
||||
<rect width="5" height="80" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -9,7 +9,11 @@ exports[`<AutosuggestEmoji /> renders emoji with custom url 1`] = `
|
|||
className="emojione"
|
||||
src="http://example.com/emoji.png"
|
||||
/>
|
||||
:foobar:
|
||||
<div
|
||||
className="autosuggest-emoji__name"
|
||||
>
|
||||
:foobar:
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -22,6 +26,10 @@ exports[`<AutosuggestEmoji /> renders native emoji 1`] = `
|
|||
className="emojione"
|
||||
src="/emoji/1f499.svg"
|
||||
/>
|
||||
:foobar:
|
||||
<div
|
||||
className="autosuggest-emoji__name"
|
||||
>
|
||||
:foobar:
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -37,10 +37,10 @@ class Account extends ImmutablePureComponent {
|
|||
static propTypes = {
|
||||
size: PropTypes.number,
|
||||
account: ImmutablePropTypes.record,
|
||||
onFollow: PropTypes.func.isRequired,
|
||||
onBlock: PropTypes.func.isRequired,
|
||||
onMute: PropTypes.func.isRequired,
|
||||
onMuteNotifications: PropTypes.func.isRequired,
|
||||
onFollow: PropTypes.func,
|
||||
onBlock: PropTypes.func,
|
||||
onMute: PropTypes.func,
|
||||
onMuteNotifications: PropTypes.func,
|
||||
intl: PropTypes.object.isRequired,
|
||||
hidden: PropTypes.bool,
|
||||
minimal: PropTypes.bool,
|
||||
|
|
|
@ -35,7 +35,7 @@ export default class AutosuggestEmoji extends PureComponent {
|
|||
alt={emoji.native || emoji.colons}
|
||||
/>
|
||||
|
||||
{emoji.colons}
|
||||
<div className='autosuggest-emoji__name'>{emoji.colons}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { ShortNumber } from 'mastodon/components/short_number';
|
||||
|
||||
interface Props {
|
||||
|
@ -16,27 +14,18 @@ interface Props {
|
|||
};
|
||||
}
|
||||
|
||||
export const AutosuggestHashtag: React.FC<Props> = ({ tag }) => {
|
||||
const weeklyUses = tag.history && (
|
||||
<ShortNumber
|
||||
value={tag.history.reduce((total, day) => total + day.uses * 1, 0)}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className='autosuggest-hashtag'>
|
||||
<div className='autosuggest-hashtag__name'>
|
||||
#<strong>{tag.name}</strong>
|
||||
</div>
|
||||
{tag.history !== undefined && (
|
||||
<div className='autosuggest-hashtag__uses'>
|
||||
<FormattedMessage
|
||||
id='autosuggest_hashtag.per_week'
|
||||
defaultMessage='{count} per week'
|
||||
values={{ count: weeklyUses }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
export const AutosuggestHashtag: React.FC<Props> = ({ tag }) => (
|
||||
<div className='autosuggest-hashtag'>
|
||||
<div className='autosuggest-hashtag__name'>
|
||||
#<strong>{tag.name}</strong>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
{tag.history !== undefined && (
|
||||
<div className='autosuggest-hashtag__uses'>
|
||||
<ShortNumber
|
||||
value={tag.history.reduce((total, day) => total + day.uses * 1, 0)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -5,6 +5,8 @@ import classNames from 'classnames';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
|
||||
import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container';
|
||||
|
||||
import AutosuggestEmoji from './autosuggest_emoji';
|
||||
|
@ -195,34 +197,37 @@ export default class AutosuggestInput extends ImmutablePureComponent {
|
|||
|
||||
return (
|
||||
<div className='autosuggest-input'>
|
||||
<label>
|
||||
<span style={{ display: 'none' }}>{placeholder}</span>
|
||||
<input
|
||||
type='text'
|
||||
ref={this.setInput}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
autoFocus={autoFocus}
|
||||
value={value}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onKeyUp={onKeyUp}
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
dir='auto'
|
||||
aria-autocomplete='list'
|
||||
aria-label={placeholder}
|
||||
id={id}
|
||||
className={className}
|
||||
maxLength={maxLength}
|
||||
lang={lang}
|
||||
spellCheck={spellCheck}
|
||||
/>
|
||||
|
||||
<input
|
||||
type='text'
|
||||
ref={this.setInput}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
autoFocus={autoFocus}
|
||||
value={value}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onKeyUp={onKeyUp}
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
dir='auto'
|
||||
aria-autocomplete='list'
|
||||
id={id}
|
||||
className={className}
|
||||
maxLength={maxLength}
|
||||
lang={lang}
|
||||
spellCheck={spellCheck}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
|
||||
{suggestions.map(this.renderSuggestion)}
|
||||
</div>
|
||||
<Overlay show={!(suggestionsHidden || suggestions.isEmpty())} offset={[0, 0]} placement='bottom' target={this.input} popperConfig={{ strategy: 'fixed' }}>
|
||||
{({ props }) => (
|
||||
<div {...props}>
|
||||
<div className='autosuggest-textarea__suggestions' style={{ width: this.input?.clientWidth }}>
|
||||
{suggestions.map(this.renderSuggestion)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Overlay>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import classNames from 'classnames';
|
|||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
import Textarea from 'react-textarea-autosize';
|
||||
|
||||
import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container';
|
||||
|
@ -52,7 +53,6 @@ const AutosuggestTextarea = forwardRef(({
|
|||
onFocus,
|
||||
autoFocus = true,
|
||||
lang,
|
||||
children,
|
||||
}, textareaRef) => {
|
||||
|
||||
const [suggestionsHidden, setSuggestionsHidden] = useState(true);
|
||||
|
@ -183,40 +183,38 @@ const AutosuggestTextarea = forwardRef(({
|
|||
);
|
||||
};
|
||||
|
||||
return [
|
||||
<div className='compose-form__autosuggest-wrapper' key='autosuggest-wrapper'>
|
||||
<div className='autosuggest-textarea'>
|
||||
<label>
|
||||
<span style={{ display: 'none' }}>{placeholder}</span>
|
||||
return (
|
||||
<div className='autosuggest-textarea'>
|
||||
<Textarea
|
||||
ref={textareaRef}
|
||||
className='autosuggest-textarea__textarea'
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
autoFocus={autoFocus}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={onKeyUp}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
onPaste={handlePaste}
|
||||
dir='auto'
|
||||
aria-autocomplete='list'
|
||||
aria-label={placeholder}
|
||||
lang={lang}
|
||||
/>
|
||||
|
||||
<Textarea
|
||||
ref={textareaRef}
|
||||
className='autosuggest-textarea__textarea'
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
autoFocus={autoFocus}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={onKeyUp}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
onPaste={handlePaste}
|
||||
dir='auto'
|
||||
aria-autocomplete='list'
|
||||
lang={lang}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
{children}
|
||||
</div>,
|
||||
|
||||
<div className='autosuggest-textarea__suggestions-wrapper' key='suggestions-wrapper'>
|
||||
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
|
||||
{suggestions.map(renderSuggestion)}
|
||||
</div>
|
||||
</div>,
|
||||
];
|
||||
<Overlay show={!(suggestionsHidden || suggestions.isEmpty())} offset={[0, 0]} placement='bottom' target={textareaRef} popperConfig={{ strategy: 'fixed' }}>
|
||||
{({ props }) => (
|
||||
<div {...props}>
|
||||
<div className='autosuggest-textarea__suggestions' style={{ width: textareaRef.current?.clientWidth }}>
|
||||
{suggestions.map(renderSuggestion)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Overlay>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
AutosuggestTextarea.propTypes = {
|
||||
|
@ -232,7 +230,6 @@ AutosuggestTextarea.propTypes = {
|
|||
onKeyDown: PropTypes.func,
|
||||
onPaste: PropTypes.func.isRequired,
|
||||
onFocus:PropTypes.func,
|
||||
children: PropTypes.node,
|
||||
autoFocus: PropTypes.bool,
|
||||
lang: PropTypes.string,
|
||||
};
|
||||
|
|
|
@ -165,7 +165,7 @@ class Dropdown extends PureComponent {
|
|||
children: PropTypes.node,
|
||||
icon: PropTypes.string,
|
||||
iconComponent: PropTypes.func,
|
||||
items: PropTypes.oneOfType([PropTypes.array, ImmutablePropTypes.list]).isRequired,
|
||||
items: PropTypes.oneOfType([PropTypes.array, ImmutablePropTypes.list]),
|
||||
loading: PropTypes.bool,
|
||||
size: PropTypes.number,
|
||||
title: PropTypes.string,
|
||||
|
|
|
@ -70,9 +70,9 @@ export const defaultMediaVisibility = (status) => {
|
|||
|
||||
const messages = defineMessages({
|
||||
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
|
||||
unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
|
||||
private_short: { id: 'privacy.private.short', defaultMessage: 'Followers only' },
|
||||
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Mentioned people only' },
|
||||
unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Quiet public' },
|
||||
private_short: { id: 'privacy.private.short', defaultMessage: 'Followers' },
|
||||
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Specific people' },
|
||||
edited: { id: 'status.edited', defaultMessage: 'Edited {date}' },
|
||||
});
|
||||
|
||||
|
|
|
@ -18,8 +18,10 @@ import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
|
|||
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
||||
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
|
||||
import VisibilityIcon from '@/material-icons/400-24px/visibility.svg?react';
|
||||
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 { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
|
@ -366,7 +368,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
|
||||
if (status.get('reblogged')) {
|
||||
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
|
||||
reblogIconComponent = publicStatus ? RepeatIcon : RepeatPrivateIcon;
|
||||
reblogIconComponent = publicStatus ? RepeatActiveIcon : RepeatPrivateActiveIcon;
|
||||
} else if (publicStatus) {
|
||||
reblogTitle = intl.formatMessage(messages.reblog);
|
||||
reblogIconComponent = RepeatIcon;
|
||||
|
|
|
@ -2,8 +2,8 @@ import { defineMessages, useIntl } from 'react-intl';
|
|||
|
||||
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
|
||||
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
||||
import LockOpenIcon from '@/material-icons/400-24px/lock_open.svg?react';
|
||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||
import QuietTimeIcon from '@/material-icons/400-24px/quiet_time.svg?react';
|
||||
|
||||
import { Icon } from './icon';
|
||||
|
||||
|
@ -11,14 +11,17 @@ type Visibility = 'public' | 'unlisted' | 'private' | 'direct';
|
|||
|
||||
const messages = defineMessages({
|
||||
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
|
||||
unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
|
||||
unlisted_short: {
|
||||
id: 'privacy.unlisted.short',
|
||||
defaultMessage: 'Quiet public',
|
||||
},
|
||||
private_short: {
|
||||
id: 'privacy.private.short',
|
||||
defaultMessage: 'Followers only',
|
||||
defaultMessage: 'Followers',
|
||||
},
|
||||
direct_short: {
|
||||
id: 'privacy.direct.short',
|
||||
defaultMessage: 'Mentioned people only',
|
||||
defaultMessage: 'Specific people',
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -35,7 +38,7 @@ export const VisibilityIcon: React.FC<{ visibility: Visibility }> = ({
|
|||
},
|
||||
unlisted: {
|
||||
icon: 'unlock',
|
||||
iconComponent: LockOpenIcon,
|
||||
iconComponent: QuietTimeIcon,
|
||||
text: intl.formatMessage(messages.unlisted_short),
|
||||
},
|
||||
private: {
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
import { PureComponent } from 'react';
|
||||
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import { fetchCustomEmojis } from '../actions/custom_emojis';
|
||||
import { hydrateStore } from '../actions/store';
|
||||
import Compose from '../features/standalone/compose';
|
||||
import initialState from '../initial_state';
|
||||
import { IntlProvider } from '../locales';
|
||||
import { store } from '../store';
|
||||
|
||||
import { fetchCustomEmojis } from 'mastodon/actions/custom_emojis';
|
||||
import { hydrateStore } from 'mastodon/actions/store';
|
||||
import { Router } from 'mastodon/components/router';
|
||||
import Compose from 'mastodon/features/standalone/compose';
|
||||
import initialState from 'mastodon/initial_state';
|
||||
import { IntlProvider } from 'mastodon/locales';
|
||||
import { store } from 'mastodon/store';
|
||||
|
||||
if (initialState) {
|
||||
store.dispatch(hydrateStore(initialState));
|
||||
|
@ -16,16 +14,14 @@ if (initialState) {
|
|||
|
||||
store.dispatch(fetchCustomEmojis());
|
||||
|
||||
export default class ComposeContainer extends PureComponent {
|
||||
const ComposeContainer = () => (
|
||||
<IntlProvider>
|
||||
<Provider store={store}>
|
||||
<Router>
|
||||
<Compose />
|
||||
</Router>
|
||||
</Provider>
|
||||
</IntlProvider>
|
||||
);
|
||||
|
||||
render () {
|
||||
return (
|
||||
<IntlProvider>
|
||||
<Provider store={store}>
|
||||
<Compose />
|
||||
</Provider>
|
||||
</IntlProvider>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
export default ComposeContainer;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import MenuIcon from '@/material-icons/400-24px/menu.svg?react';
|
||||
|
||||
import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
|
||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
|
||||
import { logOut } from 'mastodon/utils/log_out';
|
||||
|
||||
const messages = defineMessages({
|
||||
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
|
||||
|
@ -23,51 +23,52 @@ const messages = defineMessages({
|
|||
filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' },
|
||||
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
|
||||
bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
|
||||
logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' },
|
||||
logoutConfirm: { id: 'confirmations.logout.confirm', defaultMessage: 'Log out' },
|
||||
});
|
||||
|
||||
class ActionBar extends PureComponent {
|
||||
export const ActionBar = () => {
|
||||
const dispatch = useDispatch();
|
||||
const intl = useIntl();
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.record.isRequired,
|
||||
onLogout: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
const handleLogoutClick = useCallback(() => {
|
||||
dispatch(openModal({
|
||||
modalType: 'CONFIRM',
|
||||
modalProps: {
|
||||
message: intl.formatMessage(messages.logoutMessage),
|
||||
confirm: intl.formatMessage(messages.logoutConfirm),
|
||||
closeWhenConfirm: false,
|
||||
onConfirm: () => logOut(),
|
||||
},
|
||||
}));
|
||||
}, [dispatch, intl]);
|
||||
|
||||
handleLogout = () => {
|
||||
this.props.onLogout();
|
||||
};
|
||||
let menu = [];
|
||||
|
||||
render () {
|
||||
const { intl } = this.props;
|
||||
menu.push({ text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' });
|
||||
menu.push({ text: intl.formatMessage(messages.preferences), href: '/settings/preferences' });
|
||||
menu.push({ text: intl.formatMessage(messages.pins), to: '/pinned' });
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' });
|
||||
menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' });
|
||||
menu.push({ text: intl.formatMessage(messages.bookmarks), to: '/bookmarks' });
|
||||
menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
|
||||
menu.push({ text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' });
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
|
||||
menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
|
||||
menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' });
|
||||
menu.push({ text: intl.formatMessage(messages.filters), href: '/filters' });
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.logout), action: handleLogoutClick });
|
||||
|
||||
let menu = [];
|
||||
|
||||
menu.push({ text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' });
|
||||
menu.push({ text: intl.formatMessage(messages.preferences), href: '/settings/preferences' });
|
||||
menu.push({ text: intl.formatMessage(messages.pins), to: '/pinned' });
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' });
|
||||
menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' });
|
||||
menu.push({ text: intl.formatMessage(messages.bookmarks), to: '/bookmarks' });
|
||||
menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
|
||||
menu.push({ text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' });
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
|
||||
menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
|
||||
menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' });
|
||||
menu.push({ text: intl.formatMessage(messages.filters), href: '/filters' });
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.logout), action: this.handleLogout });
|
||||
|
||||
return (
|
||||
<div className='compose__action-bar'>
|
||||
<div className='compose__action-bar-dropdown'>
|
||||
<DropdownMenuContainer items={menu} icon='bars' iconComponent={MenuIcon} size={24} direction='right' />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default injectIntl(ActionBar);
|
||||
return (
|
||||
<DropdownMenuContainer
|
||||
items={menu}
|
||||
icon='bars'
|
||||
iconComponent={MoreHorizIcon}
|
||||
size={24}
|
||||
direction='right'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@ export default class AutosuggestAccount extends ImmutablePureComponent {
|
|||
|
||||
return (
|
||||
<div className='autosuggest-account' title={account.get('acct')}>
|
||||
<div className='autosuggest-account-icon'><Avatar account={account} size={18} /></div>
|
||||
<Avatar account={account} size={24} />
|
||||
<DisplayName account={account} />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,26 +1,18 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { length } from 'stringz';
|
||||
|
||||
export default class CharacterCounter extends PureComponent {
|
||||
export const CharacterCounter = ({ text, max }) => {
|
||||
const diff = max - length(text);
|
||||
|
||||
static propTypes = {
|
||||
text: PropTypes.string.isRequired,
|
||||
max: PropTypes.number.isRequired,
|
||||
};
|
||||
|
||||
checkRemainingText (diff) {
|
||||
if (diff < 0) {
|
||||
return <span className='character-counter character-counter--over'>{diff}</span>;
|
||||
}
|
||||
|
||||
return <span className='character-counter'>{diff}</span>;
|
||||
if (diff < 0) {
|
||||
return <span className='character-counter character-counter--over'>{diff}</span>;
|
||||
}
|
||||
|
||||
render () {
|
||||
const diff = this.props.max - length(this.props.text);
|
||||
return this.checkRemainingText(diff);
|
||||
}
|
||||
return <span className='character-counter'>{diff}</span>;
|
||||
};
|
||||
|
||||
}
|
||||
CharacterCounter.propTypes = {
|
||||
text: PropTypes.string.isRequired,
|
||||
max: PropTypes.number.isRequired,
|
||||
};
|
||||
|
|
|
@ -10,8 +10,6 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||
|
||||
import { length } from 'stringz';
|
||||
|
||||
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { WithOptionalRouterPropTypes, withOptionalRouter } from 'mastodon/utils/react_router';
|
||||
|
||||
import AutosuggestInput from '../../../components/autosuggest_input';
|
||||
|
@ -20,25 +18,27 @@ import { Button } from '../../../components/button';
|
|||
import EmojiPickerDropdown from '../containers/emoji_picker_dropdown_container';
|
||||
import LanguageDropdown from '../containers/language_dropdown_container';
|
||||
import PollButtonContainer from '../containers/poll_button_container';
|
||||
import PollFormContainer from '../containers/poll_form_container';
|
||||
import PrivacyDropdownContainer from '../containers/privacy_dropdown_container';
|
||||
import ReplyIndicatorContainer from '../containers/reply_indicator_container';
|
||||
import SpoilerButtonContainer from '../containers/spoiler_button_container';
|
||||
import UploadButtonContainer from '../containers/upload_button_container';
|
||||
import UploadFormContainer from '../containers/upload_form_container';
|
||||
import WarningContainer from '../containers/warning_container';
|
||||
import { countableText } from '../util/counter';
|
||||
|
||||
import CharacterCounter from './character_counter';
|
||||
import { CharacterCounter } from './character_counter';
|
||||
import { EditIndicator } from './edit_indicator';
|
||||
import { NavigationBar } from './navigation_bar';
|
||||
import { PollForm } from "./poll_form";
|
||||
import { ReplyIndicator } from './reply_indicator';
|
||||
|
||||
const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
|
||||
|
||||
const messages = defineMessages({
|
||||
placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' },
|
||||
spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' },
|
||||
publish: { id: 'compose_form.publish', defaultMessage: 'Publish' },
|
||||
publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' },
|
||||
saveChanges: { id: 'compose_form.save_changes', defaultMessage: 'Save changes' },
|
||||
spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Content warning (optional)' },
|
||||
publish: { id: 'compose_form.publish', defaultMessage: 'Post' },
|
||||
saveChanges: { id: 'compose_form.save_changes', defaultMessage: 'Update' },
|
||||
reply: { id: 'compose_form.reply', defaultMessage: 'Reply' },
|
||||
});
|
||||
|
||||
class ComposeForm extends ImmutablePureComponent {
|
||||
|
@ -65,10 +65,12 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
onPaste: PropTypes.func.isRequired,
|
||||
onPickEmoji: PropTypes.func.isRequired,
|
||||
autoFocus: PropTypes.bool,
|
||||
withoutNavigation: PropTypes.bool,
|
||||
anyMedia: PropTypes.bool,
|
||||
isInReply: PropTypes.bool,
|
||||
singleColumn: PropTypes.bool,
|
||||
lang: PropTypes.string,
|
||||
maxChars: PropTypes.number,
|
||||
...WithOptionalRouterPropTypes
|
||||
};
|
||||
|
||||
|
@ -100,11 +102,11 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
canSubmit = () => {
|
||||
const { isSubmitting, isChangingUpload, isUploading, anyMedia } = this.props;
|
||||
const { isSubmitting, isChangingUpload, isUploading, anyMedia, maxChars } = this.props;
|
||||
const fulltext = this.getFulltextForCharacterCounting();
|
||||
const isOnlyWhitespace = fulltext.length !== 0 && fulltext.trim().length === 0;
|
||||
|
||||
return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > 1000 || (isOnlyWhitespace && !anyMedia));
|
||||
return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > maxChars || (isOnlyWhitespace && !anyMedia));
|
||||
};
|
||||
|
||||
handleSubmit = (e) => {
|
||||
|
@ -223,93 +225,90 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
render () {
|
||||
const { intl, onPaste, autoFocus } = this.props;
|
||||
const { intl, onPaste, autoFocus, withoutNavigation, maxChars } = this.props;
|
||||
const { highlighted } = this.state;
|
||||
const disabled = this.props.isSubmitting;
|
||||
|
||||
let publishText = '';
|
||||
|
||||
if (this.props.isEditing) {
|
||||
publishText = intl.formatMessage(messages.saveChanges);
|
||||
} else if (this.props.privacy === 'private' || this.props.privacy === 'direct') {
|
||||
publishText = <><Icon id='lock' icon={LockIcon} /> {intl.formatMessage(messages.publish)}</>;
|
||||
} else {
|
||||
publishText = this.props.privacy !== 'unlisted' ? intl.formatMessage(messages.publishLoud, { publish: intl.formatMessage(messages.publish) }) : intl.formatMessage(messages.publish);
|
||||
}
|
||||
|
||||
return (
|
||||
<form className='compose-form' onSubmit={this.handleSubmit}>
|
||||
<ReplyIndicator />
|
||||
{!withoutNavigation && <NavigationBar />}
|
||||
<WarningContainer />
|
||||
|
||||
<ReplyIndicatorContainer />
|
||||
<div className={classNames('compose-form__highlightable', { active: highlighted })} ref={this.setRef}>
|
||||
<div className='compose-form__scrollable'>
|
||||
<EditIndicator />
|
||||
|
||||
<div className={`spoiler-input ${this.props.spoiler ? 'spoiler-input--visible' : ''}`} ref={this.setRef} aria-hidden={!this.props.spoiler}>
|
||||
<AutosuggestInput
|
||||
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
|
||||
value={this.props.spoilerText}
|
||||
onChange={this.handleChangeSpoilerText}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
disabled={!this.props.spoiler}
|
||||
ref={this.setSpoilerText}
|
||||
suggestions={this.props.suggestions}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSpoilerSuggestionSelected}
|
||||
searchTokens={[':']}
|
||||
id='cw-spoiler-input'
|
||||
className='spoiler-input__input'
|
||||
lang={this.props.lang}
|
||||
spellCheck
|
||||
/>
|
||||
</div>
|
||||
{this.props.spoiler && (
|
||||
<div className='spoiler-input'>
|
||||
<div className='spoiler-input__border' />
|
||||
|
||||
<div className={classNames('compose-form__highlightable', { active: highlighted })}>
|
||||
<AutosuggestTextarea
|
||||
ref={this.textareaRef}
|
||||
placeholder={intl.formatMessage(messages.placeholder)}
|
||||
disabled={disabled}
|
||||
value={this.props.text}
|
||||
onChange={this.handleChange}
|
||||
suggestions={this.props.suggestions}
|
||||
onFocus={this.handleFocus}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
onPaste={onPaste}
|
||||
autoFocus={autoFocus}
|
||||
lang={this.props.lang}
|
||||
>
|
||||
<div className='compose-form__modifiers'>
|
||||
<UploadFormContainer />
|
||||
<PollFormContainer />
|
||||
</div>
|
||||
</AutosuggestTextarea>
|
||||
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
|
||||
<AutosuggestInput
|
||||
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
|
||||
value={this.props.spoilerText}
|
||||
disabled={disabled}
|
||||
onChange={this.handleChangeSpoilerText}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
ref={this.setSpoilerText}
|
||||
suggestions={this.props.suggestions}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSpoilerSuggestionSelected}
|
||||
searchTokens={[':']}
|
||||
id='cw-spoiler-input'
|
||||
className='spoiler-input__input'
|
||||
lang={this.props.lang}
|
||||
spellCheck
|
||||
/>
|
||||
|
||||
<div className='compose-form__buttons-wrapper'>
|
||||
<div className='compose-form__buttons'>
|
||||
<UploadButtonContainer />
|
||||
<PollButtonContainer />
|
||||
<div className='spoiler-input__border' />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<AutosuggestTextarea
|
||||
ref={this.textareaRef}
|
||||
placeholder={intl.formatMessage(messages.placeholder)}
|
||||
disabled={disabled}
|
||||
value={this.props.text}
|
||||
onChange={this.handleChange}
|
||||
suggestions={this.props.suggestions}
|
||||
onFocus={this.handleFocus}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
onPaste={onPaste}
|
||||
autoFocus={autoFocus}
|
||||
lang={this.props.lang}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<UploadFormContainer />
|
||||
<PollForm />
|
||||
|
||||
<div className='compose-form__footer'>
|
||||
<div className='compose-form__dropdowns'>
|
||||
<PrivacyDropdownContainer disabled={this.props.isEditing} />
|
||||
<SpoilerButtonContainer />
|
||||
<LanguageDropdown />
|
||||
</div>
|
||||
|
||||
<div className='character-counter__wrapper'>
|
||||
<CharacterCounter max={1000} text={this.getFulltextForCharacterCounting()} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='compose-form__actions'>
|
||||
<div className='compose-form__buttons'>
|
||||
<UploadButtonContainer />
|
||||
<PollButtonContainer />
|
||||
<SpoilerButtonContainer />
|
||||
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
|
||||
<CharacterCounter max={maxChars} text={this.getFulltextForCharacterCounting()} />
|
||||
</div>
|
||||
|
||||
<div className='compose-form__publish'>
|
||||
<div className='compose-form__publish-button-wrapper'>
|
||||
<Button
|
||||
type='submit'
|
||||
text={publishText}
|
||||
disabled={!this.canSubmit()}
|
||||
block
|
||||
/>
|
||||
<div className='compose-form__submit'>
|
||||
<Button
|
||||
type='submit'
|
||||
text={intl.formatMessage(this.props.isEditing ? messages.saveChanges : (this.props.isInReply ? messages.reply : messages.publish))}
|
||||
disabled={!this.canSubmit()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
import { useCallback } from 'react';
|
||||
|
||||
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import BarChart4BarsIcon from 'mastodon/../material-icons/400-24px/bar_chart_4_bars.svg?react';
|
||||
import CloseIcon from 'mastodon/../material-icons/400-24px/close.svg?react';
|
||||
import PhotoLibraryIcon from 'mastodon/../material-icons/400-24px/photo_library.svg?react';
|
||||
import { cancelReplyCompose } from 'mastodon/actions/compose';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { IconButton } from 'mastodon/components/icon_button';
|
||||
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
||||
|
||||
const messages = defineMessages({
|
||||
cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },
|
||||
});
|
||||
|
||||
export const EditIndicator = () => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
const id = useSelector(state => state.getIn(['compose', 'id']));
|
||||
const status = useSelector(state => state.getIn(['statuses', id]));
|
||||
const account = useSelector(state => state.getIn(['accounts', status?.get('account')]));
|
||||
|
||||
const handleCancelClick = useCallback(() => {
|
||||
dispatch(cancelReplyCompose());
|
||||
}, [dispatch]);
|
||||
|
||||
if (!status) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const content = { __html: status.get('contentHtml') };
|
||||
|
||||
return (
|
||||
<div className='edit-indicator'>
|
||||
<div className='edit-indicator__header'>
|
||||
<div className='edit-indicator__display-name'>
|
||||
<Link to={`/@${account.get('acct')}`}>@{account.get('acct')}</Link>
|
||||
·
|
||||
<Link to={`/@${account.get('acct')}/${status.get('id')}`}><RelativeTimestamp timestamp={status.get('created_at')} /></Link>
|
||||
</div>
|
||||
|
||||
<div className='edit-indicator__cancel'>
|
||||
<IconButton title={intl.formatMessage(messages.cancel)} icon='times' iconComponent={CloseIcon} onClick={handleCancelClick} inverted />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='edit-indicator__content translate' dangerouslySetInnerHTML={content} />
|
||||
|
||||
{(status.get('poll') || status.get('media_attachments').size > 0) && (
|
||||
<div className='edit-indicator__attachments'>
|
||||
{status.get('poll') && <><Icon icon={BarChart4BarsIcon} /><FormattedMessage id='reply_indicator.poll' defaultMessage='Poll' /></>}
|
||||
{status.get('media_attachments').size > 0 && <><Icon icon={PhotoLibraryIcon} /><FormattedMessage id='reply_indicator.attachments' defaultMessage='{count, plural, one {# attachment} other {# attachments}}' values={{ count: status.get('media_attachments').size }} /></>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -10,6 +10,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
|
||||
import MoodIcon from 'mastodon/../material-icons/400-24px/mood.svg?react';
|
||||
import { IconButton } from 'mastodon/components/icon_button';
|
||||
import { assetHost } from 'mastodon/utils/config';
|
||||
|
||||
import { buildCustomEmojis, categoriesFromEmojis } from '../../emoji/emoji';
|
||||
|
@ -162,6 +164,7 @@ class EmojiPickerMenuImpl extends PureComponent {
|
|||
intl: PropTypes.object.isRequired,
|
||||
skinTone: PropTypes.number.isRequired,
|
||||
onSkinTone: PropTypes.func.isRequired,
|
||||
pickerButtonRef: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -176,7 +179,7 @@ class EmojiPickerMenuImpl extends PureComponent {
|
|||
};
|
||||
|
||||
handleDocumentClick = e => {
|
||||
if (this.node && !this.node.contains(e.target)) {
|
||||
if (this.node && !this.node.contains(e.target) && !this.props.pickerButtonRef.contains(e.target)) {
|
||||
this.props.onClose();
|
||||
}
|
||||
};
|
||||
|
@ -231,6 +234,7 @@ class EmojiPickerMenuImpl extends PureComponent {
|
|||
emoji.native = emoji.colons;
|
||||
}
|
||||
if (!(event.ctrlKey || event.metaKey)) {
|
||||
|
||||
this.props.onClose();
|
||||
}
|
||||
this.props.onPick(emoji);
|
||||
|
@ -321,7 +325,6 @@ class EmojiPickerDropdown extends PureComponent {
|
|||
onPickEmoji: PropTypes.func.isRequired,
|
||||
onSkinTone: PropTypes.func.isRequired,
|
||||
skinTone: PropTypes.number.isRequired,
|
||||
button: PropTypes.node,
|
||||
};
|
||||
|
||||
state = {
|
||||
|
@ -379,23 +382,24 @@ class EmojiPickerDropdown extends PureComponent {
|
|||
};
|
||||
|
||||
render () {
|
||||
const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis, button } = this.props;
|
||||
const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis } = this.props;
|
||||
const title = intl.formatMessage(messages.emoji);
|
||||
const { active, loading } = this.state;
|
||||
|
||||
return (
|
||||
<div className='emoji-picker-dropdown' onKeyDown={this.handleKeyDown}>
|
||||
<div ref={this.setTargetRef} className='emoji-button' title={title} aria-label={title} aria-expanded={active} role='button' onClick={this.onToggle} onKeyDown={this.onToggle} tabIndex={0}>
|
||||
{button || <img
|
||||
className={classNames('emojione', { 'pulse-loading': active && loading })}
|
||||
alt='🙂'
|
||||
src={`${assetHost}/emoji/1f642.svg`}
|
||||
/>}
|
||||
</div>
|
||||
<div className='emoji-picker-dropdown' onKeyDown={this.handleKeyDown} ref={this.setTargetRef}>
|
||||
<IconButton
|
||||
title={title}
|
||||
aria-expanded={active}
|
||||
active={active}
|
||||
iconComponent={MoodIcon}
|
||||
onClick={this.onToggle}
|
||||
inverted
|
||||
/>
|
||||
|
||||
<Overlay show={active} placement={'bottom'} target={this.findTarget} popperConfig={{ strategy: 'fixed' }}>
|
||||
{({ props, placement })=> (
|
||||
<div {...props} style={{ ...props.style, width: 299 }}>
|
||||
<div {...props} style={{ ...props.style }}>
|
||||
<div className={`dropdown-animation ${placement}`}>
|
||||
<EmojiPickerMenu
|
||||
custom_emojis={this.props.custom_emojis}
|
||||
|
@ -405,6 +409,7 @@ class EmojiPickerDropdown extends PureComponent {
|
|||
onSkinTone={onSkinTone}
|
||||
skinTone={skinTone}
|
||||
frequentlyUsedEmojis={frequentlyUsedEmojis}
|
||||
pickerButtonRef={this.target}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,10 +9,11 @@ import { supportsPassiveEvents } from 'detect-passive-events';
|
|||
import fuzzysort from 'fuzzysort';
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
|
||||
import CancelIcon from 'mastodon/../material-icons/400-24px/cancel-fill.svg?react';
|
||||
import SearchIcon from 'mastodon/../material-icons/400-24px/search.svg?react';
|
||||
import TranslateIcon from 'mastodon/../material-icons/400-24px/translate.svg?react';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { languages as preloadedLanguages } from 'mastodon/initial_state';
|
||||
import { loupeIcon, deleteIcon } from 'mastodon/utils/icons';
|
||||
|
||||
import TextIconButton from './text_icon_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
changeLanguage: { id: 'compose.language.change', defaultMessage: 'Change language' },
|
||||
|
@ -231,7 +232,7 @@ class LanguageDropdownMenu extends PureComponent {
|
|||
<div ref={this.setRef}>
|
||||
<div className='emoji-mart-search'>
|
||||
<input type='search' value={searchValue} onChange={this.handleSearchChange} onKeyDown={this.handleSearchKeyDown} placeholder={intl.formatMessage(messages.search)} />
|
||||
<button type='button' className='emoji-mart-search-icon' disabled={!isSearching} aria-label={intl.formatMessage(messages.clear)} onClick={this.handleClear}>{!isSearching ? loupeIcon : deleteIcon}</button>
|
||||
<button type='button' className='emoji-mart-search-icon' disabled={!isSearching} aria-label={intl.formatMessage(messages.clear)} onClick={this.handleClear}><Icon icon={!isSearching ? SearchIcon : CancelIcon} /></button>
|
||||
</div>
|
||||
|
||||
<div className='language-dropdown__dropdown__results emoji-mart-scroll' role='listbox' ref={this.setListRef}>
|
||||
|
@ -297,20 +298,24 @@ class LanguageDropdown extends PureComponent {
|
|||
render () {
|
||||
const { value, intl, frequentlyUsedLanguages } = this.props;
|
||||
const { open, placement } = this.state;
|
||||
const current = preloadedLanguages.find(lang => lang[0] === value) ?? [];
|
||||
|
||||
return (
|
||||
<div className={classNames('privacy-dropdown', placement, { active: open })}>
|
||||
<div className='privacy-dropdown__value' ref={this.setTargetRef} >
|
||||
<TextIconButton
|
||||
className='privacy-dropdown__value-icon'
|
||||
label={value && value.toUpperCase()}
|
||||
title={intl.formatMessage(messages.changeLanguage)}
|
||||
active={open}
|
||||
onClick={this.handleToggle}
|
||||
/>
|
||||
</div>
|
||||
<div ref={this.setTargetRef} onKeyDown={this.handleKeyDown}>
|
||||
<button
|
||||
type='button'
|
||||
title={intl.formatMessage(messages.changeLanguage)}
|
||||
aria-expanded={open}
|
||||
onClick={this.handleToggle}
|
||||
onMouseDown={this.handleMouseDown}
|
||||
onKeyDown={this.handleButtonKeyDown}
|
||||
className={classNames('dropdown-button', { active: open })}
|
||||
>
|
||||
<Icon icon={TranslateIcon} />
|
||||
<span className='dropdown-button__label'>{current[2] ?? value}</span>
|
||||
</button>
|
||||
|
||||
<Overlay show={open} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}>
|
||||
<Overlay show={open} offset={[5, 5]} placement={placement} flip target={this.findTarget} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}>
|
||||
{({ props, placement }) => (
|
||||
<div {...props}>
|
||||
<div className={`dropdown-animation language-dropdown__dropdown ${placement}`} >
|
||||
|
|
|
@ -1,50 +1,36 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { useIntl, defineMessages } from 'react-intl';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import CloseIcon from 'mastodon/../material-icons/400-24px/close.svg?react';
|
||||
import { cancelReplyCompose } from 'mastodon/actions/compose';
|
||||
import Account from 'mastodon/components/account';
|
||||
import { IconButton } from 'mastodon/components/icon_button';
|
||||
import { me } from 'mastodon/initial_state';
|
||||
|
||||
import { Avatar } from '../../../components/avatar';
|
||||
import { ActionBar } from './action_bar';
|
||||
|
||||
import ActionBar from './action_bar';
|
||||
|
||||
export default class NavigationBar extends ImmutablePureComponent {
|
||||
const messages = defineMessages({
|
||||
cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },
|
||||
});
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.record.isRequired,
|
||||
onLogout: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func,
|
||||
};
|
||||
export const NavigationBar = () => {
|
||||
const dispatch = useDispatch();
|
||||
const intl = useIntl();
|
||||
const account = useSelector(state => state.getIn(['accounts', me]));
|
||||
const isReplying = useSelector(state => !!state.getIn(['compose', 'in_reply_to']));
|
||||
|
||||
render () {
|
||||
const username = this.props.account.get('acct');
|
||||
return (
|
||||
<div className='navigation-bar'>
|
||||
<Link to={`/@${username}`}>
|
||||
<span style={{ display: 'none' }}>{username}</span>
|
||||
<Avatar account={this.props.account} size={46} />
|
||||
</Link>
|
||||
const handleCancelClick = useCallback(() => {
|
||||
dispatch(cancelReplyCompose());
|
||||
}, [dispatch]);
|
||||
|
||||
<div className='navigation-bar__profile'>
|
||||
<span>
|
||||
<Link to={`/@${username}`}>
|
||||
<strong className='navigation-bar__profile-account'>@{username}</strong>
|
||||
</Link>
|
||||
</span>
|
||||
|
||||
<span>
|
||||
<a href='/settings/profile' className='navigation-bar__profile-edit'><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className='navigation-bar__actions'>
|
||||
<ActionBar account={this.props.account} onLogout={this.props.onLogout} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
return (
|
||||
<div className='navigation-bar'>
|
||||
<Account account={account} minimal />
|
||||
{isReplying ? <IconButton title={intl.formatMessage(messages.cancel)} iconComponent={CloseIcon} onClick={handleCancelClick} /> : <ActionBar />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -3,11 +3,10 @@ import { PureComponent } from 'react';
|
|||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
|
||||
import BarChart4BarsIcon from '@/material-icons/400-24px/bar_chart_4_bars.svg?react';
|
||||
|
||||
import { IconButton } from '../../../components/icon_button';
|
||||
|
||||
|
||||
const messages = defineMessages({
|
||||
add_poll: { id: 'poll_button.add_poll', defaultMessage: 'Add a poll' },
|
||||
remove_poll: { id: 'poll_button.remove_poll', defaultMessage: 'Remove poll' },
|
||||
|
@ -22,7 +21,6 @@ class PollButton extends PureComponent {
|
|||
|
||||
static propTypes = {
|
||||
disabled: PropTypes.bool,
|
||||
unavailable: PropTypes.bool,
|
||||
active: PropTypes.bool,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
|
@ -33,17 +31,13 @@ class PollButton extends PureComponent {
|
|||
};
|
||||
|
||||
render () {
|
||||
const { intl, active, unavailable, disabled } = this.props;
|
||||
|
||||
if (unavailable) {
|
||||
return null;
|
||||
}
|
||||
const { intl, active, disabled } = this.props;
|
||||
|
||||
return (
|
||||
<div className='compose-form__poll-button'>
|
||||
<IconButton
|
||||
icon='tasks'
|
||||
iconComponent={InsertChartIcon}
|
||||
iconComponent={BarChart4BarsIcon}
|
||||
title={intl.formatMessage(active ? messages.remove_poll : messages.add_poll)}
|
||||
disabled={disabled}
|
||||
onClick={this.handleClick}
|
||||
|
|
|
@ -1,189 +1,160 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import AddIcon from '@/material-icons/400-24px/add.svg?react';
|
||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
import {
|
||||
changePollSettings,
|
||||
changePollOption,
|
||||
clearComposeSuggestions,
|
||||
fetchComposeSuggestions,
|
||||
selectComposeSuggestion,
|
||||
} from 'mastodon/actions/compose';
|
||||
import AutosuggestInput from 'mastodon/components/autosuggest_input';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { IconButton } from 'mastodon/components/icon_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
option_placeholder: { id: 'compose_form.poll.option_placeholder', defaultMessage: 'Choice {number}' },
|
||||
add_option: { id: 'compose_form.poll.add_option', defaultMessage: 'Add a choice' },
|
||||
remove_option: { id: 'compose_form.poll.remove_option', defaultMessage: 'Remove this choice' },
|
||||
poll_duration: { id: 'compose_form.poll.duration', defaultMessage: 'Poll duration' },
|
||||
option_placeholder: { id: 'compose_form.poll.option_placeholder', defaultMessage: 'Option {number}' },
|
||||
duration: { id: 'compose_form.poll.duration', defaultMessage: 'Poll length' },
|
||||
type: { id: 'compose_form.poll.type', defaultMessage: 'Style' },
|
||||
switchToMultiple: { id: 'compose_form.poll.switch_to_multiple', defaultMessage: 'Change poll to allow multiple choices' },
|
||||
switchToSingle: { id: 'compose_form.poll.switch_to_single', defaultMessage: 'Change poll to allow for a single choice' },
|
||||
minutes: { id: 'intervals.full.minutes', defaultMessage: '{number, plural, one {# minute} other {# minutes}}' },
|
||||
hours: { id: 'intervals.full.hours', defaultMessage: '{number, plural, one {# hour} other {# hours}}' },
|
||||
days: { id: 'intervals.full.days', defaultMessage: '{number, plural, one {# day} other {# days}}' },
|
||||
singleChoice: { id: 'compose_form.poll.single', defaultMessage: 'Pick one' },
|
||||
multipleChoice: { id: 'compose_form.poll.multiple', defaultMessage: 'Multiple choice' },
|
||||
});
|
||||
|
||||
class OptionIntl extends PureComponent {
|
||||
const Select = ({ label, options, value, onChange }) => {
|
||||
return (
|
||||
<label className='compose-form__poll__select'>
|
||||
<span className='compose-form__poll__select__label'>{label}</span>
|
||||
|
||||
static propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
lang: PropTypes.string,
|
||||
index: PropTypes.number.isRequired,
|
||||
isPollMultiple: PropTypes.bool,
|
||||
autoFocus: PropTypes.bool,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onRemove: PropTypes.func.isRequired,
|
||||
onToggleMultiple: PropTypes.func.isRequired,
|
||||
suggestions: ImmutablePropTypes.list,
|
||||
onClearSuggestions: PropTypes.func.isRequired,
|
||||
onFetchSuggestions: PropTypes.func.isRequired,
|
||||
onSuggestionSelected: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
<select className='compose-form__poll__select__value' value={value} onChange={onChange}>
|
||||
{options.map((option, i) => (
|
||||
<option key={i} value={option.value}>{option.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
);
|
||||
};
|
||||
|
||||
handleOptionTitleChange = e => {
|
||||
this.props.onChange(this.props.index, e.target.value);
|
||||
};
|
||||
Select.propTypes = {
|
||||
label: PropTypes.node,
|
||||
value: PropTypes.any,
|
||||
onChange: PropTypes.func,
|
||||
options: PropTypes.arrayOf(PropTypes.shape({
|
||||
label: PropTypes.node,
|
||||
value: PropTypes.any,
|
||||
})),
|
||||
};
|
||||
|
||||
handleOptionRemove = () => {
|
||||
this.props.onRemove(this.props.index);
|
||||
};
|
||||
const Option = ({ multipleChoice, index, title, autoFocus }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
const suggestions = useSelector(state => state.getIn(['compose', 'suggestions']));
|
||||
const lang = useSelector(state => state.getIn(['compose', 'language']));
|
||||
|
||||
const handleChange = useCallback(({ target: { value } }) => {
|
||||
dispatch(changePollOption(index, value));
|
||||
}, [dispatch, index]);
|
||||
|
||||
handleToggleMultiple = e => {
|
||||
this.props.onToggleMultiple();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
const handleSuggestionsFetchRequested = useCallback(token => {
|
||||
dispatch(fetchComposeSuggestions(token));
|
||||
}, [dispatch]);
|
||||
|
||||
handleCheckboxKeypress = e => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
this.handleToggleMultiple(e);
|
||||
}
|
||||
};
|
||||
const handleSuggestionsClearRequested = useCallback(() => {
|
||||
dispatch(clearComposeSuggestions());
|
||||
}, [dispatch]);
|
||||
|
||||
onSuggestionsClearRequested = () => {
|
||||
this.props.onClearSuggestions();
|
||||
};
|
||||
const handleSuggestionSelected = useCallback((tokenStart, token, value) => {
|
||||
dispatch(selectComposeSuggestion(tokenStart, token, value, ['poll', 'options', index]));
|
||||
}, [dispatch, index]);
|
||||
|
||||
onSuggestionsFetchRequested = (token) => {
|
||||
this.props.onFetchSuggestions(token);
|
||||
};
|
||||
return (
|
||||
<label className={classNames('poll__option editable', { empty: index > 1 && title.length === 0 })}>
|
||||
<span className={classNames('poll__input', { checkbox: multipleChoice })} />
|
||||
|
||||
onSuggestionSelected = (tokenStart, token, value) => {
|
||||
this.props.onSuggestionSelected(tokenStart, token, value, ['poll', 'options', this.props.index]);
|
||||
};
|
||||
<AutosuggestInput
|
||||
placeholder={intl.formatMessage(messages.option_placeholder, { number: index + 1 })}
|
||||
maxLength={50}
|
||||
value={title}
|
||||
lang={lang}
|
||||
spellCheck
|
||||
onChange={handleChange}
|
||||
suggestions={suggestions}
|
||||
onSuggestionsFetchRequested={handleSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={handleSuggestionsClearRequested}
|
||||
onSuggestionSelected={handleSuggestionSelected}
|
||||
searchTokens={[':']}
|
||||
autoFocus={autoFocus}
|
||||
/>
|
||||
</label>
|
||||
);
|
||||
};
|
||||
|
||||
render () {
|
||||
const { isPollMultiple, title, lang, index, autoFocus, intl } = this.props;
|
||||
Option.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
index: PropTypes.number.isRequired,
|
||||
multipleChoice: PropTypes.bool,
|
||||
autoFocus: PropTypes.bool,
|
||||
};
|
||||
|
||||
return (
|
||||
<li>
|
||||
<label className='poll__option editable'>
|
||||
<span
|
||||
className={classNames('poll__input', { checkbox: isPollMultiple })}
|
||||
onClick={this.handleToggleMultiple}
|
||||
onKeyPress={this.handleCheckboxKeypress}
|
||||
role='button'
|
||||
tabIndex={0}
|
||||
title={intl.formatMessage(isPollMultiple ? messages.switchToSingle : messages.switchToMultiple)}
|
||||
aria-label={intl.formatMessage(isPollMultiple ? messages.switchToSingle : messages.switchToMultiple)}
|
||||
/>
|
||||
export const PollForm = () => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
const poll = useSelector(state => state.getIn(['compose', 'poll']));
|
||||
const options = poll?.get('options');
|
||||
const expiresIn = poll?.get('expires_in');
|
||||
const isMultiple = poll?.get('multiple');
|
||||
|
||||
<AutosuggestInput
|
||||
placeholder={intl.formatMessage(messages.option_placeholder, { number: index + 1 })}
|
||||
maxLength={50}
|
||||
value={title}
|
||||
lang={lang}
|
||||
spellCheck
|
||||
onChange={this.handleOptionTitleChange}
|
||||
suggestions={this.props.suggestions}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
searchTokens={[':']}
|
||||
autoFocus={autoFocus}
|
||||
/>
|
||||
</label>
|
||||
const handleDurationChange = useCallback(({ target: { value } }) => {
|
||||
dispatch(changePollSettings(value, isMultiple));
|
||||
}, [dispatch, isMultiple]);
|
||||
|
||||
<div className='poll__cancel'>
|
||||
<IconButton disabled={index <= 1} title={intl.formatMessage(messages.remove_option)} icon='times' iconComponent={CloseIcon} onClick={this.handleOptionRemove} />
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
const handleTypeChange = useCallback(({ target: { value } }) => {
|
||||
dispatch(changePollSettings(expiresIn, value === 'true'));
|
||||
}, [dispatch, expiresIn]);
|
||||
|
||||
if (poll === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
return (
|
||||
<div className='compose-form__poll'>
|
||||
{options.map((title, i) => (
|
||||
<Option
|
||||
title={title}
|
||||
key={i}
|
||||
index={i}
|
||||
multipleChoice={isMultiple}
|
||||
autoFocus={i === 0}
|
||||
/>
|
||||
))}
|
||||
|
||||
const Option = injectIntl(OptionIntl);
|
||||
<div className='compose-form__poll__footer'>
|
||||
<Select label={intl.formatMessage(messages.duration)} options={[
|
||||
{ value: 300, label: intl.formatMessage(messages.minutes, { number: 5 })},
|
||||
{ value: 1800, label: intl.formatMessage(messages.minutes, { number: 30 })},
|
||||
{ value: 3600, label: intl.formatMessage(messages.hours, { number: 1 })},
|
||||
{ value: 21600, label: intl.formatMessage(messages.hours, { number: 6 })},
|
||||
{ value: 43200, label: intl.formatMessage(messages.hours, { number: 12 })},
|
||||
{ value: 86400, label: intl.formatMessage(messages.days, { number: 1 })},
|
||||
{ value: 259200, label: intl.formatMessage(messages.days, { number: 3 })},
|
||||
{ value: 604800, label: intl.formatMessage(messages.days, { number: 7 })},
|
||||
]} value={expiresIn} onChange={handleDurationChange} />
|
||||
|
||||
class PollForm extends ImmutablePureComponent {
|
||||
<div className='compose-form__poll__footer__sep' />
|
||||
|
||||
static propTypes = {
|
||||
options: ImmutablePropTypes.list,
|
||||
lang: PropTypes.string,
|
||||
expiresIn: PropTypes.number,
|
||||
isMultiple: PropTypes.bool,
|
||||
onChangeOption: PropTypes.func.isRequired,
|
||||
onAddOption: PropTypes.func.isRequired,
|
||||
onRemoveOption: PropTypes.func.isRequired,
|
||||
onChangeSettings: PropTypes.func.isRequired,
|
||||
suggestions: ImmutablePropTypes.list,
|
||||
onClearSuggestions: PropTypes.func.isRequired,
|
||||
onFetchSuggestions: PropTypes.func.isRequired,
|
||||
onSuggestionSelected: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
handleAddOption = () => {
|
||||
this.props.onAddOption('');
|
||||
};
|
||||
|
||||
handleSelectDuration = e => {
|
||||
this.props.onChangeSettings(e.target.value, this.props.isMultiple);
|
||||
};
|
||||
|
||||
handleToggleMultiple = () => {
|
||||
this.props.onChangeSettings(this.props.expiresIn, !this.props.isMultiple);
|
||||
};
|
||||
|
||||
render () {
|
||||
const { options, lang, expiresIn, isMultiple, onChangeOption, onRemoveOption, intl, ...other } = this.props;
|
||||
|
||||
if (!options) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const autoFocusIndex = options.indexOf('');
|
||||
|
||||
return (
|
||||
<div className='compose-form__poll-wrapper'>
|
||||
<ul>
|
||||
{options.map((title, i) => <Option title={title} lang={lang} key={i} index={i} onChange={onChangeOption} onRemove={onRemoveOption} isPollMultiple={isMultiple} onToggleMultiple={this.handleToggleMultiple} autoFocus={i === autoFocusIndex} {...other} />)}
|
||||
</ul>
|
||||
|
||||
<div className='poll__footer'>
|
||||
<button type='button' disabled={options.size >= 4} className='button button-secondary' onClick={this.handleAddOption}><Icon id='plus' icon={AddIcon} /> <FormattedMessage {...messages.add_option} /></button>
|
||||
|
||||
{/* eslint-disable-next-line jsx-a11y/no-onchange */}
|
||||
<select value={expiresIn} onChange={this.handleSelectDuration}>
|
||||
<option value={300}>{intl.formatMessage(messages.minutes, { number: 5 })}</option>
|
||||
<option value={1800}>{intl.formatMessage(messages.minutes, { number: 30 })}</option>
|
||||
<option value={3600}>{intl.formatMessage(messages.hours, { number: 1 })}</option>
|
||||
<option value={21600}>{intl.formatMessage(messages.hours, { number: 6 })}</option>
|
||||
<option value={43200}>{intl.formatMessage(messages.hours, { number: 12 })}</option>
|
||||
<option value={86400}>{intl.formatMessage(messages.days, { number: 1 })}</option>
|
||||
<option value={259200}>{intl.formatMessage(messages.days, { number: 3 })}</option>
|
||||
<option value={604800}>{intl.formatMessage(messages.days, { number: 7 })}</option>
|
||||
</select>
|
||||
</div>
|
||||
<Select label={intl.formatMessage(messages.type)} options={[
|
||||
{ value: false, label: intl.formatMessage(messages.singleChoice) },
|
||||
{ value: true, label: intl.formatMessage(messages.multipleChoice) },
|
||||
]} value={isMultiple} onChange={handleTypeChange} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default injectIntl(PollForm);
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -5,28 +5,27 @@ import { injectIntl, defineMessages } from 'react-intl';
|
|||
|
||||
import classNames from 'classnames';
|
||||
|
||||
|
||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
|
||||
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
|
||||
import InfoIcon from '@/material-icons/400-24px/info.svg?react';
|
||||
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
||||
import LockOpenIcon from '@/material-icons/400-24px/lock_open.svg?react';
|
||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||
import QuietTimeIcon from '@/material-icons/400-24px/quiet_time.svg?react';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
import { IconButton } from '../../../components/icon_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
|
||||
public_long: { id: 'privacy.public.long', defaultMessage: 'Visible for all' },
|
||||
unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
|
||||
unlisted_long: { id: 'privacy.unlisted.long', defaultMessage: 'Visible for all, but opted-out of discovery features' },
|
||||
private_short: { id: 'privacy.private.short', defaultMessage: 'Followers only' },
|
||||
private_long: { id: 'privacy.private.long', defaultMessage: 'Visible for followers only' },
|
||||
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Mentioned people only' },
|
||||
direct_long: { id: 'privacy.direct.long', defaultMessage: 'Visible for mentioned users only' },
|
||||
change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust status privacy' },
|
||||
public_long: { id: 'privacy.public.long', defaultMessage: 'Anyone on and off Mastodon' },
|
||||
unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Quiet public' },
|
||||
unlisted_long: { id: 'privacy.unlisted.long', defaultMessage: 'Fewer algorithmic fanfares' },
|
||||
private_short: { id: 'privacy.private.short', defaultMessage: 'Followers' },
|
||||
private_long: { id: 'privacy.private.long', defaultMessage: 'Only your followers' },
|
||||
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Specific people' },
|
||||
direct_long: { id: 'privacy.direct.long', defaultMessage: 'Everyone mentioned in the post' },
|
||||
change_privacy: { id: 'privacy.change', defaultMessage: 'Change post privacy' },
|
||||
unlisted_extra: { id: 'privacy.unlisted.additional', defaultMessage: 'This behaves exactly like public, except the post will not appear in live feeds or hashtags, explore, or Mastodon search, even if you are opted-in account-wide.' },
|
||||
});
|
||||
|
||||
const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
|
||||
|
@ -135,6 +134,12 @@ class PrivacyDropdownMenu extends PureComponent {
|
|||
<strong>{item.text}</strong>
|
||||
{item.meta}
|
||||
</div>
|
||||
|
||||
{item.extra && (
|
||||
<div className='privacy-dropdown__option__additional' title={item.extra}>
|
||||
<Icon id='info-circle' icon={InfoIcon} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
@ -163,30 +168,11 @@ class PrivacyDropdown extends PureComponent {
|
|||
};
|
||||
|
||||
handleToggle = () => {
|
||||
if (this.props.isUserTouching && this.props.isUserTouching()) {
|
||||
if (this.state.open) {
|
||||
this.props.onModalClose();
|
||||
} else {
|
||||
this.props.onModalOpen({
|
||||
actions: this.options.map(option => ({ ...option, active: option.value === this.props.value })),
|
||||
onClick: this.handleModalActionClick,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (this.state.open && this.activeElement) {
|
||||
this.activeElement.focus({ preventScroll: true });
|
||||
}
|
||||
this.setState({ open: !this.state.open });
|
||||
if (this.state.open && this.activeElement) {
|
||||
this.activeElement.focus({ preventScroll: true });
|
||||
}
|
||||
};
|
||||
|
||||
handleModalActionClick = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const { value } = this.options[e.currentTarget.getAttribute('data-index')];
|
||||
|
||||
this.props.onModalClose();
|
||||
this.props.onChange(value);
|
||||
this.setState({ open: !this.state.open });
|
||||
};
|
||||
|
||||
handleKeyDown = e => {
|
||||
|
@ -228,7 +214,7 @@ class PrivacyDropdown extends PureComponent {
|
|||
|
||||
this.options = [
|
||||
{ icon: 'globe', iconComponent: PublicIcon, value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) },
|
||||
{ icon: 'unlock', iconComponent: LockOpenIcon, value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) },
|
||||
{ icon: 'unlock', iconComponent: QuietTimeIcon, value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long), extra: formatMessage(messages.unlisted_extra) },
|
||||
{ icon: 'lock', iconComponent: LockIcon, value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) },
|
||||
];
|
||||
|
||||
|
@ -259,23 +245,21 @@ class PrivacyDropdown extends PureComponent {
|
|||
|
||||
return (
|
||||
<div ref={this.setTargetRef} onKeyDown={this.handleKeyDown}>
|
||||
<IconButton
|
||||
className='privacy-dropdown__value-icon'
|
||||
icon={valueOption.icon}
|
||||
iconComponent={valueOption.iconComponent}
|
||||
<button
|
||||
type='button'
|
||||
title={intl.formatMessage(messages.change_privacy)}
|
||||
size={18}
|
||||
expanded={open}
|
||||
active={open}
|
||||
inverted
|
||||
aria-expanded={open}
|
||||
onClick={this.handleToggle}
|
||||
onMouseDown={this.handleMouseDown}
|
||||
onKeyDown={this.handleButtonKeyDown}
|
||||
style={{ height: null, lineHeight: '27px' }}
|
||||
disabled={disabled}
|
||||
/>
|
||||
className={classNames('dropdown-button', { active: open })}
|
||||
>
|
||||
<Icon id={valueOption.icon} icon={valueOption.iconComponent} />
|
||||
<span className='dropdown-button__label'>{valueOption.text}</span>
|
||||
</button>
|
||||
|
||||
<Overlay show={open} placement={placement} flip target={this.findTarget} container={container} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}>
|
||||
<Overlay show={open} offset={[5, 5]} placement={placement} flip target={this.findTarget} container={container} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}>
|
||||
{({ props, placement }) => (
|
||||
<div {...props}>
|
||||
<div className={`dropdown-animation privacy-dropdown__dropdown ${placement}`}>
|
||||
|
|
|
@ -1,74 +1,48 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
import AttachmentList from 'mastodon/components/attachment_list';
|
||||
import { WithOptionalRouterPropTypes, withOptionalRouter } from 'mastodon/utils/react_router';
|
||||
import BarChart4BarsIcon from '@/material-icons/400-24px/bar_chart_4_bars.svg?react';
|
||||
import PhotoLibraryIcon from '@/material-icons/400-24px/photo_library.svg?react';
|
||||
import { Avatar } from 'mastodon/components/avatar';
|
||||
import { DisplayName } from 'mastodon/components/display_name';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
import { Avatar } from '../../../components/avatar';
|
||||
import { DisplayName } from '../../../components/display_name';
|
||||
import { IconButton } from '../../../components/icon_button';
|
||||
export const ReplyIndicator = () => {
|
||||
const inReplyToId = useSelector(state => state.getIn(['compose', 'in_reply_to']));
|
||||
const status = useSelector(state => state.getIn(['statuses', inReplyToId]));
|
||||
const account = useSelector(state => state.getIn(['accounts', status?.get('account')]));
|
||||
|
||||
const messages = defineMessages({
|
||||
cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },
|
||||
});
|
||||
if (!status) {
|
||||
return null;
|
||||
}
|
||||
|
||||
class ReplyIndicator extends ImmutablePureComponent {
|
||||
const content = { __html: status.get('contentHtml') };
|
||||
|
||||
static propTypes = {
|
||||
status: ImmutablePropTypes.map,
|
||||
onCancel: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
...WithOptionalRouterPropTypes,
|
||||
};
|
||||
return (
|
||||
<div className='reply-indicator'>
|
||||
<div className='reply-indicator__line' />
|
||||
|
||||
handleClick = () => {
|
||||
this.props.onCancel();
|
||||
};
|
||||
<Link to={`/@${account.get('acct')}`} className='detailed-status__display-avatar'>
|
||||
<Avatar account={account} size={46} />
|
||||
</Link>
|
||||
|
||||
handleAccountClick = (e) => {
|
||||
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
this.props.history?.push(`/@${this.props.status.getIn(['account', 'acct'])}`);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { status, intl } = this.props;
|
||||
|
||||
if (!status) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const content = { __html: status.get('contentHtml') };
|
||||
|
||||
return (
|
||||
<div className='reply-indicator'>
|
||||
<div className='reply-indicator__header'>
|
||||
<div className='reply-indicator__cancel'><IconButton title={intl.formatMessage(messages.cancel)} icon='times' iconComponent={CloseIcon} onClick={this.handleClick} inverted /></div>
|
||||
|
||||
<a href={`/@${status.getIn(['account', 'acct'])}`} onClick={this.handleAccountClick} className='reply-indicator__display-name'>
|
||||
<div className='reply-indicator__display-avatar'><Avatar account={status.get('account')} size={24} /></div>
|
||||
<DisplayName account={status.get('account')} />
|
||||
</a>
|
||||
</div>
|
||||
<div className='reply-indicator__main'>
|
||||
<Link to={`/@${account.get('acct')}`} className='detailed-status__display-name'>
|
||||
<DisplayName account={account} />
|
||||
</Link>
|
||||
|
||||
<div className='reply-indicator__content translate' dangerouslySetInnerHTML={content} />
|
||||
|
||||
{status.get('media_attachments').size > 0 && (
|
||||
<AttachmentList
|
||||
compact
|
||||
media={status.get('media_attachments')}
|
||||
/>
|
||||
{(status.get('poll') || status.get('media_attachments').size > 0) && (
|
||||
<div className='reply-indicator__attachments'>
|
||||
{status.get('poll') && <><Icon icon={BarChart4BarsIcon} /><FormattedMessage id='reply_indicator.poll' defaultMessage='Poll' /></>}
|
||||
{status.get('media_attachments').size > 0 && <><Icon icon={PhotoLibraryIcon} /><FormattedMessage id='reply_indicator.attachments' defaultMessage='{count, plural, one {# attachment} other {# attachments}}' values={{ count: status.get('media_attachments').size }} /></>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default withOptionalRouter(injectIntl(ReplyIndicator));
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -2,6 +2,8 @@ import PropTypes from 'prop-types';
|
|||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
|
@ -9,7 +11,8 @@ import spring from 'react-motion/lib/spring';
|
|||
|
||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
|
||||
import InfoIcon from '@/material-icons/400-24px/info.svg?react';
|
||||
import WarningIcon from '@/material-icons/400-24px/warning.svg?react';
|
||||
import { Blurhash } from 'mastodon/components/blurhash';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
import Motion from '../../ui/util/optional_motion';
|
||||
|
@ -18,6 +21,7 @@ export default class Upload extends ImmutablePureComponent {
|
|||
|
||||
static propTypes = {
|
||||
media: ImmutablePropTypes.map.isRequired,
|
||||
sensitive: PropTypes.bool,
|
||||
onUndo: PropTypes.func.isRequired,
|
||||
onOpenFocalPoint: PropTypes.func.isRequired,
|
||||
};
|
||||
|
@ -33,7 +37,7 @@ export default class Upload extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
render () {
|
||||
const { media } = this.props;
|
||||
const { media, sensitive } = this.props;
|
||||
|
||||
if (!media) {
|
||||
return null;
|
||||
|
@ -43,22 +47,26 @@ export default class Upload extends ImmutablePureComponent {
|
|||
const focusY = media.getIn(['meta', 'focus', 'y']);
|
||||
const x = ((focusX / 2) + .5) * 100;
|
||||
const y = ((focusY / -2) + .5) * 100;
|
||||
const missingDescription = (media.get('description') || '').length === 0;
|
||||
|
||||
return (
|
||||
<div className='compose-form__upload'>
|
||||
<Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12 }) }}>
|
||||
{({ scale }) => (
|
||||
<div className='compose-form__upload-thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})`, backgroundPosition: `${x}% ${y}%` }}>
|
||||
<div className='compose-form__upload__thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: !sensitive ? `url(${media.get('preview_url')})` : null, backgroundPosition: `${x}% ${y}%` }}>
|
||||
{sensitive && <Blurhash
|
||||
hash={media.get('blurhash')}
|
||||
className='compose-form__upload__preview'
|
||||
/>}
|
||||
|
||||
<div className='compose-form__upload__actions'>
|
||||
<button type='button' className='icon-button' onClick={this.handleUndoClick}><Icon id='times' icon={CloseIcon} /> <FormattedMessage id='upload_form.undo' defaultMessage='Delete' /></button>
|
||||
<button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='pencil' icon={EditIcon} /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button>
|
||||
<button type='button' className='icon-button compose-form__upload__delete' onClick={this.handleUndoClick}><Icon icon={CloseIcon} /></button>
|
||||
<button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon icon={EditIcon} /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button>
|
||||
</div>
|
||||
|
||||
{(media.get('description') || '').length === 0 && (
|
||||
<div className='compose-form__upload__warning'>
|
||||
<button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='info-circle' icon={InfoIcon} /> <FormattedMessage id='upload_form.description_missing' defaultMessage='No description added' /></button>
|
||||
</div>
|
||||
)}
|
||||
<div className='compose-form__upload__warning'>
|
||||
<button type='button' className={classNames('icon-button', { active: missingDescription })} onClick={this.handleFocalPointClick}>{missingDescription && <Icon icon={WarningIcon} />} ALT</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Motion>
|
||||
|
|
|
@ -6,9 +6,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import AddPhotoAlternateIcon from '@/material-icons/400-24px/add_photo_alternate.svg?react';
|
||||
|
||||
import { IconButton } from '../../../components/icon_button';
|
||||
import PhotoLibraryIcon from '@/material-icons/400-24px/photo_library.svg?react';
|
||||
import { IconButton } from 'mastodon/components/icon_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
upload: { id: 'upload_button.label', defaultMessage: 'Add images, a video or an audio file' },
|
||||
|
@ -31,7 +30,6 @@ class UploadButton extends ImmutablePureComponent {
|
|||
|
||||
static propTypes = {
|
||||
disabled: PropTypes.bool,
|
||||
unavailable: PropTypes.bool,
|
||||
onSelectFile: PropTypes.func.isRequired,
|
||||
style: PropTypes.object,
|
||||
resetFileKey: PropTypes.number,
|
||||
|
@ -54,17 +52,13 @@ class UploadButton extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
render () {
|
||||
const { intl, resetFileKey, unavailable, disabled, acceptContentTypes } = this.props;
|
||||
|
||||
if (unavailable) {
|
||||
return null;
|
||||
}
|
||||
const { intl, resetFileKey, disabled, acceptContentTypes } = this.props;
|
||||
|
||||
const message = intl.formatMessage(messages.upload);
|
||||
|
||||
return (
|
||||
<div className='compose-form__upload-button'>
|
||||
<IconButton icon='paperclip' iconComponent={AddPhotoAlternateIcon} title={message} disabled={disabled} onClick={this.handleClick} className='compose-form__upload-button-icon' size={18} inverted style={iconStyle} />
|
||||
<IconButton icon='paperclip' iconComponent={PhotoLibraryIcon} title={message} disabled={disabled} onClick={this.handleClick} className='compose-form__upload-button-icon' size={18} inverted style={iconStyle} />
|
||||
<label>
|
||||
<span style={{ display: 'none' }}>{message}</span>
|
||||
<input
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import SensitiveButtonContainer from '../containers/sensitive_button_container';
|
||||
import UploadContainer from '../containers/upload_container';
|
||||
import UploadProgressContainer from '../containers/upload_progress_container';
|
||||
|
||||
|
@ -15,17 +14,17 @@ export default class UploadForm extends ImmutablePureComponent {
|
|||
const { mediaIds } = this.props;
|
||||
|
||||
return (
|
||||
<div className='compose-form__upload-wrapper'>
|
||||
<>
|
||||
<UploadProgressContainer />
|
||||
|
||||
<div className='compose-form__uploads-wrapper'>
|
||||
{mediaIds.map(id => (
|
||||
<UploadContainer id={id} key={id} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{!mediaIds.isEmpty() && <SensitiveButtonContainer />}
|
||||
</div>
|
||||
{mediaIds.size > 0 && (
|
||||
<div className='compose-form__uploads'>
|
||||
{mediaIds.map(id => (
|
||||
<UploadContainer id={id} key={id} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,9 +35,7 @@ export default class UploadProgress extends PureComponent {
|
|||
|
||||
return (
|
||||
<div className='upload-progress'>
|
||||
<div className='upload-progress__icon'>
|
||||
<Icon id='upload' icon={UploadFileIcon} />
|
||||
</div>
|
||||
<Icon id='upload' icon={UploadFileIcon} />
|
||||
|
||||
<div className='upload-progress__message'>
|
||||
{message}
|
||||
|
|
|
@ -28,6 +28,7 @@ const mapStateToProps = state => ({
|
|||
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
|
||||
isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
|
||||
lang: state.getIn(['compose', 'language']),
|
||||
maxChars: state.getIn(['server', 'server', 'configuration', 'statuses', 'max_characters'], 500),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import { logOut } from 'mastodon/utils/log_out';
|
||||
|
||||
import { me } from '../../../initial_state';
|
||||
import NavigationBar from '../components/navigation_bar';
|
||||
|
||||
const messages = defineMessages({
|
||||
logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' },
|
||||
logoutConfirm: { id: 'confirmations.logout.confirm', defaultMessage: 'Log out' },
|
||||
});
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
account: state.getIn(['accounts', me]),
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||
onLogout () {
|
||||
dispatch(openModal({
|
||||
modalType: 'CONFIRM',
|
||||
modalProps: {
|
||||
message: intl.formatMessage(messages.logoutMessage),
|
||||
confirm: intl.formatMessage(messages.logoutConfirm),
|
||||
closeWhenConfirm: false,
|
||||
onConfirm: () => logOut(),
|
||||
},
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
||||
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(NavigationBar));
|
|
@ -4,7 +4,7 @@ import { addPoll, removePoll } from '../../../actions/compose';
|
|||
import PollButton from '../components/poll_button';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
unavailable: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 0),
|
||||
disabled: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 0),
|
||||
active: state.getIn(['compose', 'poll']) !== null,
|
||||
});
|
||||
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
import { connect } from 'react-redux';
|
||||
|
||||
import {
|
||||
addPollOption,
|
||||
removePollOption,
|
||||
changePollOption,
|
||||
changePollSettings,
|
||||
clearComposeSuggestions,
|
||||
fetchComposeSuggestions,
|
||||
selectComposeSuggestion,
|
||||
} from '../../../actions/compose';
|
||||
import PollForm from '../components/poll_form';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
suggestions: state.getIn(['compose', 'suggestions']),
|
||||
options: state.getIn(['compose', 'poll', 'options']),
|
||||
lang: state.getIn(['compose', 'language']),
|
||||
expiresIn: state.getIn(['compose', 'poll', 'expires_in']),
|
||||
isMultiple: state.getIn(['compose', 'poll', 'multiple']),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onAddOption(title) {
|
||||
dispatch(addPollOption(title));
|
||||
},
|
||||
|
||||
onRemoveOption(index) {
|
||||
dispatch(removePollOption(index));
|
||||
},
|
||||
|
||||
onChangeOption(index, title) {
|
||||
dispatch(changePollOption(index, title));
|
||||
},
|
||||
|
||||
onChangeSettings(expiresIn, isMultiple) {
|
||||
dispatch(changePollSettings(expiresIn, isMultiple));
|
||||
},
|
||||
|
||||
onClearSuggestions () {
|
||||
dispatch(clearComposeSuggestions());
|
||||
},
|
||||
|
||||
onFetchSuggestions (token) {
|
||||
dispatch(fetchComposeSuggestions(token));
|
||||
},
|
||||
|
||||
onSuggestionSelected (position, token, accountId, path) {
|
||||
dispatch(selectComposeSuggestion(position, token, accountId, path));
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(PollForm);
|
|
@ -1,36 +0,0 @@
|
|||
import { connect } from 'react-redux';
|
||||
|
||||
import { cancelReplyCompose } from '../../../actions/compose';
|
||||
import { makeGetStatus } from '../../../selectors';
|
||||
import ReplyIndicator from '../components/reply_indicator';
|
||||
|
||||
const makeMapStateToProps = () => {
|
||||
const getStatus = makeGetStatus();
|
||||
|
||||
const mapStateToProps = state => {
|
||||
let statusId = state.getIn(['compose', 'id'], null);
|
||||
let editing = true;
|
||||
|
||||
if (statusId === null) {
|
||||
statusId = state.getIn(['compose', 'in_reply_to']);
|
||||
editing = false;
|
||||
}
|
||||
|
||||
return {
|
||||
status: getStatus(state, { id: statusId }),
|
||||
editing,
|
||||
};
|
||||
};
|
||||
|
||||
return mapStateToProps;
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
|
||||
onCancel () {
|
||||
dispatch(cancelReplyCompose());
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
export default connect(makeMapStateToProps, mapDispatchToProps)(ReplyIndicator);
|
|
@ -1,73 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { changeComposeSensitivity } from 'mastodon/actions/compose';
|
||||
|
||||
const messages = defineMessages({
|
||||
marked: {
|
||||
id: 'compose_form.sensitive.marked',
|
||||
defaultMessage: '{count, plural, one {Media is marked as sensitive} other {Media is marked as sensitive}}',
|
||||
},
|
||||
unmarked: {
|
||||
id: 'compose_form.sensitive.unmarked',
|
||||
defaultMessage: '{count, plural, one {Media is not marked as sensitive} other {Media is not marked as sensitive}}',
|
||||
},
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
active: state.getIn(['compose', 'sensitive']),
|
||||
disabled: state.getIn(['compose', 'spoiler']),
|
||||
mediaCount: state.getIn(['compose', 'media_attachments']).size,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
|
||||
onClick () {
|
||||
dispatch(changeComposeSensitivity());
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
class SensitiveButton extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
active: PropTypes.bool,
|
||||
disabled: PropTypes.bool,
|
||||
mediaCount: PropTypes.number,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { active, disabled, mediaCount, onClick, intl } = this.props;
|
||||
|
||||
return (
|
||||
<div className='compose-form__sensitive-button'>
|
||||
<label className={classNames('icon-button', { active })} title={intl.formatMessage(active ? messages.marked : messages.unmarked, { count: mediaCount })}>
|
||||
<input
|
||||
name='mark-sensitive'
|
||||
type='checkbox'
|
||||
checked={active}
|
||||
onChange={onClick}
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
||||
<FormattedMessage
|
||||
id='compose_form.sensitive.hide'
|
||||
defaultMessage='{count, plural, one {Mark media as sensitive} other {Mark media as sensitive}}'
|
||||
values={{ count: mediaCount }}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(SensitiveButton));
|
|
@ -2,8 +2,10 @@ import { injectIntl, defineMessages } from 'react-intl';
|
|||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import WarningIcon from 'mastodon/../material-icons/400-24px/warning.svg?react';
|
||||
import { IconButton } from 'mastodon/components/icon_button';
|
||||
|
||||
import { changeComposeSpoilerness } from '../../../actions/compose';
|
||||
import TextIconButton from '../components/text_icon_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
marked: { id: 'compose_form.spoiler.marked', defaultMessage: 'Text is hidden behind warning' },
|
||||
|
@ -11,10 +13,12 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
const mapStateToProps = (state, { intl }) => ({
|
||||
label: 'CW',
|
||||
iconComponent: WarningIcon,
|
||||
title: intl.formatMessage(state.getIn(['compose', 'spoiler']) ? messages.marked : messages.unmarked),
|
||||
active: state.getIn(['compose', 'spoiler']),
|
||||
ariaControls: 'cw-spoiler-input',
|
||||
size: 18,
|
||||
inverted: true,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
|
@ -25,4 +29,4 @@ const mapDispatchToProps = dispatch => ({
|
|||
|
||||
});
|
||||
|
||||
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(TextIconButton));
|
||||
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(IconButton));
|
||||
|
|
|
@ -4,8 +4,7 @@ import { uploadCompose } from '../../../actions/compose';
|
|||
import UploadButton from '../components/upload_button';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
disabled: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size + state.getIn(['compose', 'pending_media_attachments']) > 3 || state.getIn(['compose', 'media_attachments']).some(m => ['video', 'audio'].includes(m.get('type')))),
|
||||
unavailable: state.getIn(['compose', 'poll']) !== null,
|
||||
disabled: state.getIn(['compose', 'poll']) !== null || state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size + state.getIn(['compose', 'pending_media_attachments']) > 3 || state.getIn(['compose', 'media_attachments']).some(m => ['video', 'audio'].includes(m.get('type')))),
|
||||
resetFileKey: state.getIn(['compose', 'resetFileKey']),
|
||||
});
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import Upload from '../components/upload';
|
|||
|
||||
const mapStateToProps = (state, { id }) => ({
|
||||
media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id),
|
||||
sensitive: state.getIn(['compose', 'spoiler']),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
|
|
|
@ -30,7 +30,6 @@ import { isMobile } from '../../is_mobile';
|
|||
import Motion from '../ui/util/optional_motion';
|
||||
|
||||
import ComposeFormContainer from './containers/compose_form_container';
|
||||
import NavigationContainer from './containers/navigation_container';
|
||||
import SearchContainer from './containers/search_container';
|
||||
import SearchResultsContainer from './containers/search_results_container';
|
||||
|
||||
|
@ -129,8 +128,6 @@ class Compose extends PureComponent {
|
|||
|
||||
<div className='drawer__pager'>
|
||||
<div className='drawer__inner' onFocus={this.onFocus}>
|
||||
<NavigationContainer onClose={this.onBlur} />
|
||||
|
||||
<ComposeFormContainer autoFocus={!isMobile(window.innerWidth)} />
|
||||
|
||||
<div className='drawer__inner__mastodon'>
|
||||
|
@ -152,7 +149,6 @@ class Compose extends PureComponent {
|
|||
|
||||
return (
|
||||
<Column onFocus={this.onFocus}>
|
||||
<NavigationContainer onClose={this.onBlur} />
|
||||
<ComposeFormContainer />
|
||||
|
||||
<Helmet>
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { Link, withRouter } from 'react-router-dom';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
|
||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
|
||||
import { replyCompose } from 'mastodon/actions/compose';
|
||||
import { markConversationRead, deleteConversation } from 'mastodon/actions/conversations';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import { muteStatus, unmuteStatus, revealStatus, hideStatus } from 'mastodon/actions/statuses';
|
||||
import AttachmentList from 'mastodon/components/attachment_list';
|
||||
import AvatarComposite from 'mastodon/components/avatar_composite';
|
||||
import { IconButton } from 'mastodon/components/icon_button';
|
||||
|
@ -19,7 +26,7 @@ import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
|||
import StatusContent from 'mastodon/components/status_content';
|
||||
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
|
||||
import { autoPlayGif } from 'mastodon/initial_state';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
import { makeGetStatus } from 'mastodon/selectors';
|
||||
|
||||
const messages = defineMessages({
|
||||
more: { id: 'status.more', defaultMessage: 'More' },
|
||||
|
@ -29,25 +36,31 @@ const messages = defineMessages({
|
|||
delete: { id: 'conversation.delete', defaultMessage: 'Delete conversation' },
|
||||
muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
|
||||
unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
|
||||
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
||||
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
||||
});
|
||||
|
||||
class Conversation extends ImmutablePureComponent {
|
||||
const getAccounts = createSelector(
|
||||
(state) => state.get('accounts'),
|
||||
(_, accountIds) => accountIds,
|
||||
(accounts, accountIds) =>
|
||||
accountIds.map(id => accounts.get(id))
|
||||
);
|
||||
|
||||
static propTypes = {
|
||||
conversationId: PropTypes.string.isRequired,
|
||||
accounts: ImmutablePropTypes.list.isRequired,
|
||||
lastStatus: ImmutablePropTypes.map,
|
||||
unread:PropTypes.bool.isRequired,
|
||||
scrollKey: PropTypes.string,
|
||||
onMoveUp: PropTypes.func,
|
||||
onMoveDown: PropTypes.func,
|
||||
markRead: PropTypes.func.isRequired,
|
||||
delete: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
const getStatus = makeGetStatus();
|
||||
|
||||
handleMouseEnter = ({ currentTarget }) => {
|
||||
export const Conversation = ({ conversation, scrollKey, onMoveUp, onMoveDown }) => {
|
||||
const id = conversation.get('id');
|
||||
const unread = conversation.get('unread');
|
||||
const lastStatusId = conversation.get('last_status');
|
||||
const accountIds = conversation.get('accounts');
|
||||
const intl = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
const history = useHistory();
|
||||
const lastStatus = useSelector(state => getStatus(state, { id: lastStatusId }));
|
||||
const accounts = useSelector(state => getAccounts(state, accountIds));
|
||||
|
||||
const handleMouseEnter = useCallback(({ currentTarget }) => {
|
||||
if (autoPlayGif) {
|
||||
return;
|
||||
}
|
||||
|
@ -58,9 +71,9 @@ class Conversation extends ImmutablePureComponent {
|
|||
let emoji = emojis[i];
|
||||
emoji.src = emoji.getAttribute('data-original');
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
handleMouseLeave = ({ currentTarget }) => {
|
||||
const handleMouseLeave = useCallback(({ currentTarget }) => {
|
||||
if (autoPlayGif) {
|
||||
return;
|
||||
}
|
||||
|
@ -71,136 +84,161 @@ class Conversation extends ImmutablePureComponent {
|
|||
let emoji = emojis[i];
|
||||
emoji.src = emoji.getAttribute('data-static');
|
||||
}
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
if (!this.props.history) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { lastStatus, unread, markRead } = this.props;
|
||||
}, []);
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
if (unread) {
|
||||
markRead();
|
||||
dispatch(markConversationRead(id));
|
||||
}
|
||||
|
||||
this.props.history.push(`/@${lastStatus.getIn(['account', 'acct'])}/${lastStatus.get('id')}`);
|
||||
};
|
||||
history.push(`/@${lastStatus.getIn(['account', 'acct'])}/${lastStatus.get('id')}`);
|
||||
}, [dispatch, history, unread, id, lastStatus]);
|
||||
|
||||
handleMarkAsRead = () => {
|
||||
this.props.markRead();
|
||||
};
|
||||
const handleMarkAsRead = useCallback(() => {
|
||||
dispatch(markConversationRead(id));
|
||||
}, [dispatch, id]);
|
||||
|
||||
handleReply = () => {
|
||||
this.props.reply(this.props.lastStatus, this.props.history);
|
||||
};
|
||||
const handleReply = useCallback(() => {
|
||||
dispatch((_, getState) => {
|
||||
let state = getState();
|
||||
|
||||
handleDelete = () => {
|
||||
this.props.delete();
|
||||
};
|
||||
if (state.getIn(['compose', 'text']).trim().length !== 0) {
|
||||
dispatch(openModal({
|
||||
modalType: 'CONFIRM',
|
||||
modalProps: {
|
||||
message: intl.formatMessage(messages.replyMessage),
|
||||
confirm: intl.formatMessage(messages.replyConfirm),
|
||||
onConfirm: () => dispatch(replyCompose(lastStatus, history)),
|
||||
},
|
||||
}));
|
||||
} else {
|
||||
dispatch(replyCompose(lastStatus, history));
|
||||
}
|
||||
});
|
||||
}, [dispatch, lastStatus, history, intl]);
|
||||
|
||||
handleHotkeyMoveUp = () => {
|
||||
this.props.onMoveUp(this.props.conversationId);
|
||||
};
|
||||
const handleDelete = useCallback(() => {
|
||||
dispatch(deleteConversation(id));
|
||||
}, [dispatch, id]);
|
||||
|
||||
handleHotkeyMoveDown = () => {
|
||||
this.props.onMoveDown(this.props.conversationId);
|
||||
};
|
||||
const handleHotkeyMoveUp = useCallback(() => {
|
||||
onMoveUp(id);
|
||||
}, [id, onMoveUp]);
|
||||
|
||||
handleConversationMute = () => {
|
||||
this.props.onMute(this.props.lastStatus);
|
||||
};
|
||||
const handleHotkeyMoveDown = useCallback(() => {
|
||||
onMoveDown(id);
|
||||
}, [id, onMoveDown]);
|
||||
|
||||
handleShowMore = () => {
|
||||
this.props.onToggleHidden(this.props.lastStatus);
|
||||
};
|
||||
|
||||
render () {
|
||||
const { accounts, lastStatus, unread, scrollKey, intl } = this.props;
|
||||
|
||||
if (lastStatus === null) {
|
||||
return null;
|
||||
const handleConversationMute = useCallback(() => {
|
||||
if (lastStatus.get('muted')) {
|
||||
dispatch(unmuteStatus(lastStatus.get('id')));
|
||||
} else {
|
||||
dispatch(muteStatus(lastStatus.get('id')));
|
||||
}
|
||||
}, [dispatch, lastStatus]);
|
||||
|
||||
const menu = [
|
||||
{ text: intl.formatMessage(messages.open), action: this.handleClick },
|
||||
null,
|
||||
];
|
||||
|
||||
menu.push({ text: intl.formatMessage(lastStatus.get('muted') ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMute });
|
||||
|
||||
if (unread) {
|
||||
menu.push({ text: intl.formatMessage(messages.markAsRead), action: this.handleMarkAsRead });
|
||||
menu.push(null);
|
||||
const handleShowMore = useCallback(() => {
|
||||
if (lastStatus.get('hidden')) {
|
||||
dispatch(revealStatus(lastStatus.get('id')));
|
||||
} else {
|
||||
dispatch(hideStatus(lastStatus.get('id')));
|
||||
}
|
||||
}, [dispatch, lastStatus]);
|
||||
|
||||
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDelete });
|
||||
if (!lastStatus) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const names = accounts.map(a => <Link to={`/@${a.get('acct')}`} key={a.get('id')} title={a.get('acct')}><bdi><strong className='display-name__html' dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }} /></bdi></Link>).reduce((prev, cur) => [prev, ', ', cur]);
|
||||
const menu = [
|
||||
{ text: intl.formatMessage(messages.open), action: handleClick },
|
||||
null,
|
||||
{ text: intl.formatMessage(lastStatus.get('muted') ? messages.unmuteConversation : messages.muteConversation), action: handleConversationMute },
|
||||
];
|
||||
|
||||
const handlers = {
|
||||
reply: this.handleReply,
|
||||
open: this.handleClick,
|
||||
moveUp: this.handleHotkeyMoveUp,
|
||||
moveDown: this.handleHotkeyMoveDown,
|
||||
toggleHidden: this.handleShowMore,
|
||||
};
|
||||
if (unread) {
|
||||
menu.push({ text: intl.formatMessage(messages.markAsRead), action: handleMarkAsRead });
|
||||
menu.push(null);
|
||||
}
|
||||
|
||||
return (
|
||||
<HotKeys handlers={handlers}>
|
||||
<div className={classNames('conversation focusable muted', { 'conversation--unread': unread })} tabIndex={0}>
|
||||
<div className='conversation__avatar' onClick={this.handleClick} role='presentation'>
|
||||
<AvatarComposite accounts={accounts} size={48} />
|
||||
</div>
|
||||
menu.push({ text: intl.formatMessage(messages.delete), action: handleDelete });
|
||||
|
||||
<div className='conversation__content'>
|
||||
<div className='conversation__content__info'>
|
||||
<div className='conversation__content__relative-time'>
|
||||
{unread && <span className='conversation__unread' />} <RelativeTimestamp timestamp={lastStatus.get('created_at')} />
|
||||
</div>
|
||||
const names = accounts.map(a => (
|
||||
<Link to={`/@${a.get('acct')}`} key={a.get('id')} title={a.get('acct')}>
|
||||
<bdi>
|
||||
<strong
|
||||
className='display-name__html'
|
||||
dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }}
|
||||
/>
|
||||
</bdi>
|
||||
</Link>
|
||||
)).reduce((prev, cur) => [prev, ', ', cur]);
|
||||
|
||||
<div className='conversation__content__names' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
||||
<FormattedMessage id='conversation.with' defaultMessage='With {names}' values={{ names: <span>{names}</span> }} />
|
||||
</div>
|
||||
const handlers = {
|
||||
reply: handleReply,
|
||||
open: handleClick,
|
||||
moveUp: handleHotkeyMoveUp,
|
||||
moveDown: handleHotkeyMoveDown,
|
||||
toggleHidden: handleShowMore,
|
||||
};
|
||||
|
||||
return (
|
||||
<HotKeys handlers={handlers}>
|
||||
<div className={classNames('conversation focusable muted', { 'conversation--unread': unread })} tabIndex={0}>
|
||||
<div className='conversation__avatar' onClick={handleClick} role='presentation'>
|
||||
<AvatarComposite accounts={accounts} size={48} />
|
||||
</div>
|
||||
|
||||
<div className='conversation__content'>
|
||||
<div className='conversation__content__info'>
|
||||
<div className='conversation__content__relative-time'>
|
||||
{unread && <span className='conversation__unread' />} <RelativeTimestamp timestamp={lastStatus.get('created_at')} />
|
||||
</div>
|
||||
|
||||
<StatusContent
|
||||
status={lastStatus}
|
||||
onClick={this.handleClick}
|
||||
expanded={!lastStatus.get('hidden')}
|
||||
onExpandedToggle={this.handleShowMore}
|
||||
collapsible
|
||||
<div className='conversation__content__names' onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
|
||||
<FormattedMessage id='conversation.with' defaultMessage='With {names}' values={{ names: <span>{names}</span> }} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<StatusContent
|
||||
status={lastStatus}
|
||||
onClick={handleClick}
|
||||
expanded={!lastStatus.get('hidden')}
|
||||
onExpandedToggle={handleShowMore}
|
||||
collapsible
|
||||
/>
|
||||
|
||||
{lastStatus.get('media_attachments').size > 0 && (
|
||||
<AttachmentList
|
||||
compact
|
||||
media={lastStatus.get('media_attachments')}
|
||||
/>
|
||||
)}
|
||||
|
||||
{lastStatus.get('media_attachments').size > 0 && (
|
||||
<AttachmentList
|
||||
compact
|
||||
media={lastStatus.get('media_attachments')}
|
||||
<div className='status__action-bar'>
|
||||
<IconButton className='status__action-bar-button' title={intl.formatMessage(messages.reply)} icon='reply' iconComponent={ReplyIcon} onClick={handleReply} />
|
||||
|
||||
<div className='status__action-bar-dropdown'>
|
||||
<DropdownMenuContainer
|
||||
scrollKey={scrollKey}
|
||||
status={lastStatus}
|
||||
items={menu}
|
||||
icon='ellipsis-h'
|
||||
iconComponent={MoreHorizIcon}
|
||||
size={18}
|
||||
direction='right'
|
||||
title={intl.formatMessage(messages.more)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className='status__action-bar'>
|
||||
<IconButton className='status__action-bar-button' title={intl.formatMessage(messages.reply)} icon='reply' iconComponent={ReplyIcon} onClick={this.handleReply} />
|
||||
|
||||
<div className='status__action-bar-dropdown'>
|
||||
<DropdownMenuContainer
|
||||
scrollKey={scrollKey}
|
||||
status={lastStatus}
|
||||
items={menu}
|
||||
icon='ellipsis-h'
|
||||
iconComponent={MoreHorizIcon}
|
||||
size={18}
|
||||
direction='right'
|
||||
title={intl.formatMessage(messages.more)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</HotKeys>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
</HotKeys>
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export default withRouter(injectIntl(Conversation));
|
||||
Conversation.propTypes = {
|
||||
conversation: ImmutablePropTypes.map.isRequired,
|
||||
scrollKey: PropTypes.string,
|
||||
onMoveUp: PropTypes.func,
|
||||
onMoveDown: PropTypes.func,
|
||||
};
|
||||
|
|
|
@ -1,77 +1,72 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { useRef, useMemo, useCallback } from 'react';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import ScrollableList from '../../../components/scrollable_list';
|
||||
import ConversationContainer from '../containers/conversation_container';
|
||||
import { expandConversations } from 'mastodon/actions/conversations';
|
||||
import ScrollableList from 'mastodon/components/scrollable_list';
|
||||
|
||||
export default class ConversationsList extends ImmutablePureComponent {
|
||||
import { Conversation } from './conversation';
|
||||
|
||||
static propTypes = {
|
||||
conversations: ImmutablePropTypes.list.isRequired,
|
||||
scrollKey: PropTypes.string.isRequired,
|
||||
hasMore: PropTypes.bool,
|
||||
isLoading: PropTypes.bool,
|
||||
onLoadMore: PropTypes.func,
|
||||
};
|
||||
const focusChild = (node, index, alignTop) => {
|
||||
const element = node.querySelector(`article:nth-of-type(${index + 1}) .focusable`);
|
||||
|
||||
getCurrentIndex = id => this.props.conversations.findIndex(x => x.get('id') === id);
|
||||
|
||||
handleMoveUp = id => {
|
||||
const elementIndex = this.getCurrentIndex(id) - 1;
|
||||
this._selectChild(elementIndex, true);
|
||||
};
|
||||
|
||||
handleMoveDown = id => {
|
||||
const elementIndex = this.getCurrentIndex(id) + 1;
|
||||
this._selectChild(elementIndex, false);
|
||||
};
|
||||
|
||||
_selectChild (index, align_top) {
|
||||
const container = this.node.node;
|
||||
const element = container.querySelector(`article:nth-of-type(${index + 1}) .focusable`);
|
||||
|
||||
if (element) {
|
||||
if (align_top && container.scrollTop > element.offsetTop) {
|
||||
element.scrollIntoView(true);
|
||||
} else if (!align_top && container.scrollTop + container.clientHeight < element.offsetTop + element.offsetHeight) {
|
||||
element.scrollIntoView(false);
|
||||
}
|
||||
element.focus();
|
||||
if (element) {
|
||||
if (alignTop && node.scrollTop > element.offsetTop) {
|
||||
element.scrollIntoView(true);
|
||||
} else if (!alignTop && node.scrollTop + node.clientHeight < element.offsetTop + element.offsetHeight) {
|
||||
element.scrollIntoView(false);
|
||||
}
|
||||
|
||||
element.focus();
|
||||
}
|
||||
};
|
||||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
};
|
||||
export const ConversationsList = ({ scrollKey, ...other }) => {
|
||||
const listRef = useRef();
|
||||
const conversations = useSelector(state => state.getIn(['conversations', 'items']));
|
||||
const isLoading = useSelector(state => state.getIn(['conversations', 'isLoading'], true));
|
||||
const hasMore = useSelector(state => state.getIn(['conversations', 'hasMore'], false));
|
||||
const dispatch = useDispatch();
|
||||
const lastStatusId = conversations.last()?.get('last_status');
|
||||
|
||||
handleLoadOlder = debounce(() => {
|
||||
const last = this.props.conversations.last();
|
||||
const handleMoveUp = useCallback(id => {
|
||||
const elementIndex = conversations.findIndex(x => x.get('id') === id) - 1;
|
||||
focusChild(listRef.current.node, elementIndex, true);
|
||||
}, [listRef, conversations]);
|
||||
|
||||
if (last && last.get('last_status')) {
|
||||
this.props.onLoadMore(last.get('last_status'));
|
||||
const handleMoveDown = useCallback(id => {
|
||||
const elementIndex = conversations.findIndex(x => x.get('id') === id) + 1;
|
||||
focusChild(listRef.current.node, elementIndex, false);
|
||||
}, [listRef, conversations]);
|
||||
|
||||
const debouncedLoadMore = useMemo(() => debounce(id => {
|
||||
dispatch(expandConversations({ maxId: id }));
|
||||
}, 300, { leading: true }), [dispatch]);
|
||||
|
||||
const handleLoadMore = useCallback(() => {
|
||||
if (lastStatusId) {
|
||||
debouncedLoadMore(lastStatusId);
|
||||
}
|
||||
}, 300, { leading: true });
|
||||
}, [debouncedLoadMore, lastStatusId]);
|
||||
|
||||
render () {
|
||||
const { conversations, isLoading, onLoadMore, ...other } = this.props;
|
||||
return (
|
||||
<ScrollableList {...other} scrollKey={scrollKey} isLoading={isLoading} showLoading={isLoading && conversations.isEmpty()} hasMore={hasMore} onLoadMore={handleLoadMore} ref={listRef}>
|
||||
{conversations.map(item => (
|
||||
<Conversation
|
||||
key={item.get('id')}
|
||||
conversation={item}
|
||||
onMoveUp={handleMoveUp}
|
||||
onMoveDown={handleMoveDown}
|
||||
scrollKey={scrollKey}
|
||||
/>
|
||||
))}
|
||||
</ScrollableList>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ScrollableList {...other} isLoading={isLoading} showLoading={isLoading && conversations.isEmpty()} onLoadMore={onLoadMore && this.handleLoadOlder} ref={this.setRef}>
|
||||
{conversations.map(item => (
|
||||
<ConversationContainer
|
||||
key={item.get('id')}
|
||||
conversationId={item.get('id')}
|
||||
onMoveUp={this.handleMoveUp}
|
||||
onMoveDown={this.handleMoveDown}
|
||||
scrollKey={this.props.scrollKey}
|
||||
/>
|
||||
))}
|
||||
</ScrollableList>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
ConversationsList.propTypes = {
|
||||
scrollKey: PropTypes.string.isRequired,
|
||||
};
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { replyCompose } from 'mastodon/actions/compose';
|
||||
import { markConversationRead, deleteConversation } from 'mastodon/actions/conversations';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import { muteStatus, unmuteStatus, hideStatus, revealStatus } from 'mastodon/actions/statuses';
|
||||
import { makeGetStatus } from 'mastodon/selectors';
|
||||
|
||||
import Conversation from '../components/conversation';
|
||||
|
||||
const messages = defineMessages({
|
||||
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
||||
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
||||
});
|
||||
|
||||
const mapStateToProps = () => {
|
||||
const getStatus = makeGetStatus();
|
||||
|
||||
return (state, { conversationId }) => {
|
||||
const conversation = state.getIn(['conversations', 'items']).find(x => x.get('id') === conversationId);
|
||||
const lastStatusId = conversation.get('last_status', null);
|
||||
|
||||
return {
|
||||
accounts: conversation.get('accounts').map(accountId => state.getIn(['accounts', accountId], null)),
|
||||
unread: conversation.get('unread'),
|
||||
lastStatus: lastStatusId && getStatus(state, { id: lastStatusId }),
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch, { intl, conversationId }) => ({
|
||||
|
||||
markRead () {
|
||||
dispatch(markConversationRead(conversationId));
|
||||
},
|
||||
|
||||
reply (status, router) {
|
||||
dispatch((_, getState) => {
|
||||
let state = getState();
|
||||
|
||||
if (state.getIn(['compose', 'text']).trim().length !== 0) {
|
||||
dispatch(openModal({
|
||||
modalType: 'CONFIRM',
|
||||
modalProps: {
|
||||
message: intl.formatMessage(messages.replyMessage),
|
||||
confirm: intl.formatMessage(messages.replyConfirm),
|
||||
onConfirm: () => dispatch(replyCompose(status, router)),
|
||||
},
|
||||
}));
|
||||
} else {
|
||||
dispatch(replyCompose(status, router));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
delete () {
|
||||
dispatch(deleteConversation(conversationId));
|
||||
},
|
||||
|
||||
onMute (status) {
|
||||
if (status.get('muted')) {
|
||||
dispatch(unmuteStatus(status.get('id')));
|
||||
} else {
|
||||
dispatch(muteStatus(status.get('id')));
|
||||
}
|
||||
},
|
||||
|
||||
onToggleHidden (status) {
|
||||
if (status.get('hidden')) {
|
||||
dispatch(revealStatus(status.get('id')));
|
||||
} else {
|
||||
dispatch(hideStatus(status.get('id')));
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(Conversation));
|
|
@ -1,16 +0,0 @@
|
|||
import { connect } from 'react-redux';
|
||||
|
||||
import { expandConversations } from '../../../actions/conversations';
|
||||
import ConversationsList from '../components/conversations_list';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
conversations: state.getIn(['conversations', 'items']),
|
||||
isLoading: state.getIn(['conversations', 'isLoading'], true),
|
||||
hasMore: state.getIn(['conversations', 'hasMore'], false),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onLoadMore: maxId => dispatch(expandConversations({ maxId })),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ConversationsList);
|
|
@ -1,11 +1,11 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import { useRef, useCallback, useEffect } from 'react';
|
||||
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
|
||||
import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns';
|
||||
|
@ -14,103 +14,79 @@ import { connectDirectStream } from 'mastodon/actions/streaming';
|
|||
import Column from 'mastodon/components/column';
|
||||
import ColumnHeader from 'mastodon/components/column_header';
|
||||
|
||||
import ConversationsListContainer from './containers/conversations_list_container';
|
||||
import { ConversationsList } from './components/conversations_list';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'column.direct', defaultMessage: 'Private mentions' },
|
||||
});
|
||||
|
||||
class DirectTimeline extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
columnId: PropTypes.string,
|
||||
intl: PropTypes.object.isRequired,
|
||||
hasUnread: PropTypes.bool,
|
||||
multiColumn: PropTypes.bool,
|
||||
};
|
||||
|
||||
handlePin = () => {
|
||||
const { columnId, dispatch } = this.props;
|
||||
const DirectTimeline = ({ columnId, multiColumn }) => {
|
||||
const columnRef = useRef();
|
||||
const intl = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
const pinned = !!columnId;
|
||||
|
||||
const handlePin = useCallback(() => {
|
||||
if (columnId) {
|
||||
dispatch(removeColumn(columnId));
|
||||
} else {
|
||||
dispatch(addColumn('DIRECT', {}));
|
||||
}
|
||||
};
|
||||
}, [dispatch, columnId]);
|
||||
|
||||
handleMove = (dir) => {
|
||||
const { columnId, dispatch } = this.props;
|
||||
const handleMove = useCallback((dir) => {
|
||||
dispatch(moveColumn(columnId, dir));
|
||||
};
|
||||
}, [dispatch, columnId]);
|
||||
|
||||
handleHeaderClick = () => {
|
||||
this.column.scrollTop();
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
const { dispatch } = this.props;
|
||||
const handleHeaderClick = useCallback(() => {
|
||||
columnRef.current.scrollTop();
|
||||
}, [columnRef]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(mountConversations());
|
||||
dispatch(expandConversations());
|
||||
this.disconnect = dispatch(connectDirectStream());
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.props.dispatch(unmountConversations());
|
||||
const disconnect = dispatch(connectDirectStream());
|
||||
|
||||
if (this.disconnect) {
|
||||
this.disconnect();
|
||||
this.disconnect = null;
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
dispatch(unmountConversations());
|
||||
disconnect();
|
||||
};
|
||||
}, [dispatch]);
|
||||
|
||||
setRef = c => {
|
||||
this.column = c;
|
||||
};
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} ref={columnRef} label={intl.formatMessage(messages.title)}>
|
||||
<ColumnHeader
|
||||
icon='at'
|
||||
iconComponent={AlternateEmailIcon}
|
||||
title={intl.formatMessage(messages.title)}
|
||||
onPin={handlePin}
|
||||
onMove={handleMove}
|
||||
onClick={handleHeaderClick}
|
||||
pinned={pinned}
|
||||
multiColumn={multiColumn}
|
||||
/>
|
||||
|
||||
handleLoadMore = maxId => {
|
||||
this.props.dispatch(expandConversations({ maxId }));
|
||||
};
|
||||
<ConversationsList
|
||||
trackScroll={!pinned}
|
||||
scrollKey={`direct_timeline-${columnId}`}
|
||||
emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any private mentions yet. When you send or receive one, it will show up here." />}
|
||||
bindToDocument={!multiColumn}
|
||||
prepend={<div className='follow_requests-unlocked_explanation'><span><FormattedMessage id='compose_form.encryption_warning' defaultMessage='Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.' /> <a href='/terms' target='_blank'><FormattedMessage id='compose_form.direct_message_warning_learn_more' defaultMessage='Learn more' /></a></span></div>}
|
||||
alwaysPrepend
|
||||
/>
|
||||
|
||||
render () {
|
||||
const { intl, hasUnread, columnId, multiColumn } = this.props;
|
||||
const pinned = !!columnId;
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.title)}</title>
|
||||
<meta name='robots' content='noindex' />
|
||||
</Helmet>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||
<ColumnHeader
|
||||
icon='at'
|
||||
iconComponent={AlternateEmailIcon}
|
||||
active={hasUnread}
|
||||
title={intl.formatMessage(messages.title)}
|
||||
onPin={this.handlePin}
|
||||
onMove={this.handleMove}
|
||||
onClick={this.handleHeaderClick}
|
||||
pinned={pinned}
|
||||
multiColumn={multiColumn}
|
||||
/>
|
||||
DirectTimeline.propTypes = {
|
||||
columnId: PropTypes.string,
|
||||
multiColumn: PropTypes.bool,
|
||||
};
|
||||
|
||||
<ConversationsListContainer
|
||||
trackScroll={!pinned}
|
||||
scrollKey={`direct_timeline-${columnId}`}
|
||||
timelineId='direct'
|
||||
bindToDocument={!multiColumn}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
prepend={<div className='follow_requests-unlocked_explanation'><span><FormattedMessage id='compose_form.encryption_warning' defaultMessage='Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.' /> <a href='/terms' target='_blank'><FormattedMessage id='compose_form.direct_message_warning_learn_more' defaultMessage='Learn more' /></a></span></div>}
|
||||
alwaysPrepend
|
||||
emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any private mentions yet. When you send or receive one, it will show up here." />}
|
||||
/>
|
||||
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.title)}</title>
|
||||
<meta name='robots' content='noindex' />
|
||||
</Helmet>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect()(injectIntl(DirectTimeline));
|
||||
export default DirectTimeline;
|
||||
|
|
|
@ -26,7 +26,7 @@ import ColumnHeader from 'mastodon/components/column_header';
|
|||
import LinkFooter from 'mastodon/features/ui/components/link_footer';
|
||||
|
||||
import { me, showTrends } from '../../initial_state';
|
||||
import NavigationContainer from '../compose/containers/navigation_container';
|
||||
import { NavigationBar } from '../compose/components/navigation_bar';
|
||||
import ColumnLink from '../ui/components/column_link';
|
||||
import ColumnSubheading from '../ui/components/column_subheading';
|
||||
|
||||
|
@ -143,7 +143,7 @@ class GettingStarted extends ImmutablePureComponent {
|
|||
|
||||
return (
|
||||
<Column>
|
||||
{(signedIn && !multiColumn) ? <NavigationContainer /> : <ColumnHeader title={intl.formatMessage(messages.menu)} icon='bars' iconComponent={MenuIcon} multiColumn={multiColumn} />}
|
||||
{(signedIn && !multiColumn) ? <NavigationBar /> : <ColumnHeader title={intl.formatMessage(messages.menu)} icon='bars' iconComponent={MenuIcon} multiColumn={multiColumn} />}
|
||||
|
||||
<div className='getting-started scrollable scrollable--flex'>
|
||||
<div className='getting-started__wrapper'>
|
||||
|
|
|
@ -1,21 +1,15 @@
|
|||
import { PureComponent } from 'react';
|
||||
import ComposeFormContainer from 'mastodon/features/compose/containers/compose_form_container';
|
||||
import LoadingBarContainer from 'mastodon/features/ui/containers/loading_bar_container';
|
||||
import ModalContainer from 'mastodon/features/ui/containers/modal_container';
|
||||
import NotificationsContainer from 'mastodon/features/ui/containers/notifications_container';
|
||||
|
||||
import ComposeFormContainer from '../../compose/containers/compose_form_container';
|
||||
import LoadingBarContainer from '../../ui/containers/loading_bar_container';
|
||||
import ModalContainer from '../../ui/containers/modal_container';
|
||||
import NotificationsContainer from '../../ui/containers/notifications_container';
|
||||
const Compose = () => (
|
||||
<>
|
||||
<ComposeFormContainer autoFocus withoutNavigation />
|
||||
<NotificationsContainer />
|
||||
<ModalContainer />
|
||||
<LoadingBarContainer className='loading-bar' />
|
||||
</>
|
||||
);
|
||||
|
||||
export default class Compose extends PureComponent {
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<ComposeFormContainer autoFocus />
|
||||
<NotificationsContainer />
|
||||
<ModalContainer />
|
||||
<LoadingBarContainer className='loading-bar' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
export default Compose;
|
||||
|
|
|
@ -17,8 +17,10 @@ import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
|
|||
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
|
||||
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
||||
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
|
||||
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 { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
|
@ -296,7 +298,7 @@ class ActionBar extends PureComponent {
|
|||
|
||||
if (status.get('reblogged')) {
|
||||
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
|
||||
reblogIconComponent = publicStatus ? RepeatIcon : RepeatPrivateIcon;
|
||||
reblogIconComponent = publicStatus ? RepeatActiveIcon : RepeatPrivateActiveIcon;
|
||||
} else if (publicStatus) {
|
||||
reblogTitle = intl.formatMessage(messages.reblog);
|
||||
reblogIconComponent = RepeatIcon;
|
||||
|
|
|
@ -6,7 +6,6 @@ import { connect } from 'react-redux';
|
|||
import { changeComposing, mountCompose, unmountCompose } from 'mastodon/actions/compose';
|
||||
import ServerBanner from 'mastodon/components/server_banner';
|
||||
import ComposeFormContainer from 'mastodon/features/compose/containers/compose_form_container';
|
||||
import NavigationContainer from 'mastodon/features/compose/containers/navigation_container';
|
||||
import SearchContainer from 'mastodon/features/compose/containers/search_container';
|
||||
|
||||
import LinkFooter from './link_footer';
|
||||
|
@ -56,10 +55,7 @@ class ComposePanel extends PureComponent {
|
|||
)}
|
||||
|
||||
{signedIn && (
|
||||
<>
|
||||
<NavigationContainer onClose={this.onBlur} />
|
||||
<ComposeFormContainer singleColumn />
|
||||
</>
|
||||
<ComposeFormContainer singleColumn />
|
||||
)}
|
||||
|
||||
<LinkFooter />
|
||||
|
|
|
@ -21,7 +21,7 @@ import { Button } from 'mastodon/components/button';
|
|||
import { GIFV } from 'mastodon/components/gifv';
|
||||
import { IconButton } from 'mastodon/components/icon_button';
|
||||
import Audio from 'mastodon/features/audio';
|
||||
import CharacterCounter from 'mastodon/features/compose/components/character_counter';
|
||||
import { CharacterCounter } from 'mastodon/features/compose/components/character_counter';
|
||||
import UploadProgress from 'mastodon/features/compose/components/upload_progress';
|
||||
import { Tesseract as fetchTesseract } from 'mastodon/features/ui/util/async-components';
|
||||
import { me } from 'mastodon/initial_state';
|
||||
|
|
|
@ -108,7 +108,6 @@ class MuteModal extends PureComponent {
|
|||
<div>
|
||||
<span><FormattedMessage id='mute_modal.duration' defaultMessage='Duration' />: </span>
|
||||
|
||||
{/* eslint-disable-next-line jsx-a11y/no-onchange */}
|
||||
<select value={muteDuration} onChange={this.changeMuteDuration}>
|
||||
<option value={0}>{intl.formatMessage(messages.indefinite)}</option>
|
||||
<option value={300}>{intl.formatMessage(messages.minutes, { number: 5 })}</option>
|
||||
|
|
|
@ -79,7 +79,6 @@ class NavigationPanel extends Component {
|
|||
<div className='navigation-panel'>
|
||||
<div className='navigation-panel__logo'>
|
||||
<Link to='/' className='column-link column-link--logo'><WordmarkLogo /></Link>
|
||||
{!banner && <hr />}
|
||||
</div>
|
||||
|
||||
{banner &&
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"about.contact": "Kontak:",
|
||||
"about.disclaimer": "Mastodon is gratis oopbronsagteware en ’n handelsmerk van Mastodon gGmbH.",
|
||||
"about.domain_blocks.no_reason_available": "Rede nie beskikbaar nie",
|
||||
"about.domain_blocks.preamble": "Mastodon generally allows you to view content from and interact with users from any other server in the fediverse. These are the exceptions that have been made on this particular server.",
|
||||
"about.domain_blocks.silenced.title": "Beperk",
|
||||
"about.domain_blocks.suspended.title": "Opgeskort",
|
||||
"about.not_available": "Hierdie inligting is nie op hierdie bediener beskikbaar gestel nie.",
|
||||
|
@ -65,7 +66,6 @@
|
|||
"alert.unexpected.message": "Iets het skeefgeloop.",
|
||||
"alert.unexpected.title": "Oeps!",
|
||||
"announcement.announcement": "Aankondiging",
|
||||
"autosuggest_hashtag.per_week": "{count} per week",
|
||||
"boost_modal.combo": "Druk {combo} om dit volgende keer oor te slaan",
|
||||
"bundle_column_error.error.title": "Ag nee!",
|
||||
"bundle_column_error.network.title": "Netwerkfout",
|
||||
|
@ -110,19 +110,12 @@
|
|||
"compose_form.lock_disclaimer": "Jou rekening is nie {locked} nie. Enigiemand kan jou volg en sien wat jy vir jou volgers plaas.",
|
||||
"compose_form.lock_disclaimer.lock": "gesluit",
|
||||
"compose_form.placeholder": "Wat wil jy deel?",
|
||||
"compose_form.poll.add_option": "Voeg ’n keuse by",
|
||||
"compose_form.poll.duration": "Duur van peiling",
|
||||
"compose_form.poll.option_placeholder": "Keuse {number}",
|
||||
"compose_form.poll.remove_option": "Verwyder hierdie keuse",
|
||||
"compose_form.poll.switch_to_multiple": "Verander peiling om meer as een keuse toe te laat",
|
||||
"compose_form.poll.switch_to_single": "Verander die peiling om slegs een keuse toe te laat",
|
||||
"compose_form.publish": "Publiseer",
|
||||
"compose_form.publish_form": "Publiseer",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Stoor veranderinge",
|
||||
"compose_form.spoiler.marked": "Verwyder inhoudswaarskuwing",
|
||||
"compose_form.spoiler.unmarked": "Voeg inhoudswaarskuwing by",
|
||||
"compose_form.spoiler_placeholder": "Skryf jou waarskuwing hier",
|
||||
"confirmation_modal.cancel": "Kanselleer",
|
||||
"confirmations.block.block_and_report": "Blokkeer en rapporteer",
|
||||
"confirmations.block.confirm": "Blokkeer",
|
||||
|
@ -233,7 +226,6 @@
|
|||
"navigation_bar.community_timeline": "Plaaslike tydlyn",
|
||||
"navigation_bar.compose": "Skep nuwe plasing",
|
||||
"navigation_bar.domain_blocks": "Geblokkeerde domeine",
|
||||
"navigation_bar.edit_profile": "Redigeer profiel",
|
||||
"navigation_bar.lists": "Lyste",
|
||||
"navigation_bar.logout": "Teken uit",
|
||||
"navigation_bar.personal": "Persoonlik",
|
||||
|
@ -266,14 +258,7 @@
|
|||
"onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
|
||||
"onboarding.steps.share_profile.title": "Share your profile",
|
||||
"privacy.change": "Verander privaatheid van plasing",
|
||||
"privacy.direct.long": "Slegs sigbaar vir genoemde gebruikers",
|
||||
"privacy.direct.short": "Slegs genoemde persone",
|
||||
"privacy.private.long": "Slegs sigbaar vir volgelinge",
|
||||
"privacy.private.short": "Slegs volgelinge",
|
||||
"privacy.public.long": "Sigbaar vir almal",
|
||||
"privacy.public.short": "Publiek",
|
||||
"privacy.unlisted.long": "Sigbaar vir almal, maar onttrek uit verkennings-kenmerke",
|
||||
"privacy.unlisted.short": "Ongelys",
|
||||
"privacy_policy.last_updated": "Laaste bywerking op {date}",
|
||||
"privacy_policy.title": "Privaatheidsbeleid",
|
||||
"regeneration_indicator.sublabel": "Jou tuis-voer word voorberei!",
|
||||
|
|
|
@ -75,7 +75,6 @@
|
|||
"announcement.announcement": "Anuncio",
|
||||
"attachments_list.unprocessed": "(sin procesar)",
|
||||
"audio.hide": "Amagar audio",
|
||||
"autosuggest_hashtag.per_week": "{count} per semana",
|
||||
"boost_modal.combo": "Puetz fer clic en {combo} pa blincar este aviso la proxima vegada",
|
||||
"bundle_column_error.copy_stacktrace": "Copiar informe d'error",
|
||||
"bundle_column_error.error.body": "La pachina solicitada no podió estar renderizada. Podría deber-se a una error en o nuestro codigo u a un problema de compatibilidat con o navegador.",
|
||||
|
@ -126,22 +125,12 @@
|
|||
"compose_form.lock_disclaimer": "La tuya cuenta no ye {locked}. Totz pueden seguir-te pa veyer las tuyas publicacions nomás pa seguidores.",
|
||||
"compose_form.lock_disclaimer.lock": "blocau",
|
||||
"compose_form.placeholder": "En qué yes pensando?",
|
||||
"compose_form.poll.add_option": "Anyadir una opción",
|
||||
"compose_form.poll.duration": "Duración d'a enqüesta",
|
||||
"compose_form.poll.option_placeholder": "Elección {number}",
|
||||
"compose_form.poll.remove_option": "Eliminar esta opción",
|
||||
"compose_form.poll.switch_to_multiple": "Modificar enqüesta pa permitir multiples opcions",
|
||||
"compose_form.poll.switch_to_single": "Modificar enqüesta pa permitir una sola opción",
|
||||
"compose_form.publish": "Publicar",
|
||||
"compose_form.publish_form": "Publicar",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Alzar cambios",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Marcar material como sensible} other {Marcar material como sensible}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {Material marcau como sensible} other {Material marcau como sensible}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Material no marcau como sensible} other {Material no marcau como sensible}}",
|
||||
"compose_form.spoiler.marked": "Texto amagau dimpués de l'alvertencia",
|
||||
"compose_form.spoiler.unmarked": "Texto no amagau",
|
||||
"compose_form.spoiler_placeholder": "Alvertencia de conteniu",
|
||||
"confirmation_modal.cancel": "Cancelar",
|
||||
"confirmations.block.block_and_report": "Blocar y Denunciar",
|
||||
"confirmations.block.confirm": "Blocar",
|
||||
|
@ -345,7 +334,6 @@
|
|||
"navigation_bar.compose": "Escribir nueva publicación",
|
||||
"navigation_bar.discover": "Descubrir",
|
||||
"navigation_bar.domain_blocks": "Dominios amagaus",
|
||||
"navigation_bar.edit_profile": "Editar perfil",
|
||||
"navigation_bar.explore": "Explorar",
|
||||
"navigation_bar.filters": "Parolas silenciadas",
|
||||
"navigation_bar.follow_requests": "Solicitutz pa seguir-te",
|
||||
|
@ -429,14 +417,7 @@
|
|||
"poll_button.add_poll": "Anyadir una enqüesta",
|
||||
"poll_button.remove_poll": "Eliminar enqüesta",
|
||||
"privacy.change": "Achustar privacidat",
|
||||
"privacy.direct.long": "Nomás amostrar a los usuarios mencionaus",
|
||||
"privacy.direct.short": "Nomás contas mencionadas",
|
||||
"privacy.private.long": "Nomás amostrar a seguidores",
|
||||
"privacy.private.short": "Solo seguidores",
|
||||
"privacy.public.long": "Visible pa totz",
|
||||
"privacy.public.short": "Publico",
|
||||
"privacy.unlisted.long": "Visible pa totz, pero excluyiu d'as funcions d'escubrimiento",
|
||||
"privacy.unlisted.short": "No listau",
|
||||
"privacy_policy.last_updated": "Ultima vegada actualizau {date}",
|
||||
"privacy_policy.title": "Politica de Privacidat",
|
||||
"refresh": "Actualizar",
|
||||
|
@ -590,10 +571,8 @@
|
|||
"upload_error.poll": "Puyada de fichers no permitida con enqüestas.",
|
||||
"upload_form.audio_description": "Describir pa personas con problemas auditivos",
|
||||
"upload_form.description": "Describir pa los usuarios con dificultat visual",
|
||||
"upload_form.description_missing": "Garra descripción anyadida",
|
||||
"upload_form.edit": "Editar",
|
||||
"upload_form.thumbnail": "Cambiar miniatura",
|
||||
"upload_form.undo": "Borrar",
|
||||
"upload_form.video_description": "Describir pa personas con problemas auditivos u visuals",
|
||||
"upload_modal.analyzing_picture": "Analisando imachen…",
|
||||
"upload_modal.apply": "Aplicar",
|
||||
|
|
|
@ -86,7 +86,6 @@
|
|||
"announcement.announcement": "إعلان",
|
||||
"attachments_list.unprocessed": "(غير معالَج)",
|
||||
"audio.hide": "إخفاء المقطع الصوتي",
|
||||
"autosuggest_hashtag.per_week": "{count} في الأسبوع",
|
||||
"boost_modal.combo": "يُمكنك الضّغط على {combo} لتخطي هذا في المرة المُقبلة",
|
||||
"bundle_column_error.copy_stacktrace": "انسخ تقرير الخطأ",
|
||||
"bundle_column_error.error.body": "لا يمكن تقديم الصفحة المطلوبة. قد يكون بسبب خطأ في التعليمات البرمجية، أو مشكلة توافق المتصفح.",
|
||||
|
@ -143,22 +142,12 @@
|
|||
"compose_form.lock_disclaimer": "حسابُك غير {locked}. يُمكن لأي شخص مُتابعتك لرؤية (منشورات المتابعين فقط).",
|
||||
"compose_form.lock_disclaimer.lock": "مُقفَل",
|
||||
"compose_form.placeholder": "فِيمَ تُفكِّر؟",
|
||||
"compose_form.poll.add_option": "إضافة خيار",
|
||||
"compose_form.poll.duration": "مُدَّة اِستطلاع الرأي",
|
||||
"compose_form.poll.option_placeholder": "الخيار {number}",
|
||||
"compose_form.poll.remove_option": "إزالة هذا الخيار",
|
||||
"compose_form.poll.switch_to_multiple": "تغيِير الاستطلاع للسماح باِخيارات مُتعدِّدة",
|
||||
"compose_form.poll.switch_to_single": "تغيِير الاستطلاع للسماح باِخيار واحد فقط",
|
||||
"compose_form.publish": "نشر",
|
||||
"compose_form.publish_form": "منشور جديد",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "احفظ التعديلات",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {الإشارة إلى الوَسط كمُحتوى حسّاس} two{الإشارة إلى الوسطان كمُحتويان حسّاسان} other {الإشارة إلى الوسائط كمُحتويات حسّاسة}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {تمَّ الإشارة إلى الوسط كمُحتوى حسّاس} two{تمَّ الإشارة إلى الوسطان كمُحتويان حسّاسان} other {تمَّ الإشارة إلى الوسائط كمُحتويات حسّاسة}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {لم تَتِمّ الإشارة إلى الوسط كمُحتوى حسّاس} two{لم تَتِمّ الإشارة إلى الوسطان كمُحتويان حسّاسان} other {لم تَتِمّ الإشارة إلى الوسائط كمُحتويات حسّاسة}}",
|
||||
"compose_form.spoiler.marked": "إزالة تحذير المحتوى",
|
||||
"compose_form.spoiler.unmarked": "إضافة تحذير للمحتوى",
|
||||
"compose_form.spoiler_placeholder": "اُكتُب تحذيركَ هُنا",
|
||||
"confirmation_modal.cancel": "إلغاء",
|
||||
"confirmations.block.block_and_report": "حظره والإبلاغ عنه",
|
||||
"confirmations.block.confirm": "حظر",
|
||||
|
@ -402,7 +391,6 @@
|
|||
"navigation_bar.direct": "الإشارات الخاصة",
|
||||
"navigation_bar.discover": "اكتشف",
|
||||
"navigation_bar.domain_blocks": "النطاقات المحظورة",
|
||||
"navigation_bar.edit_profile": "عدّل الملف التعريفي",
|
||||
"navigation_bar.explore": "استكشف",
|
||||
"navigation_bar.favourites": "المفضلة",
|
||||
"navigation_bar.filters": "الكلمات المكتومة",
|
||||
|
@ -509,14 +497,7 @@
|
|||
"poll_button.add_poll": "إضافة استطلاع للرأي",
|
||||
"poll_button.remove_poll": "إزالة استطلاع الرأي",
|
||||
"privacy.change": "اضبط خصوصية المنشور",
|
||||
"privacy.direct.long": "مرئي للمستخدمين المذكورين فقط",
|
||||
"privacy.direct.short": "الأشخاص المشار إليهم فقط",
|
||||
"privacy.private.long": "أنشر لمتابعيك فقط",
|
||||
"privacy.private.short": "للمتابِعين فقط",
|
||||
"privacy.public.long": "مرئي للكل",
|
||||
"privacy.public.short": "للعامة",
|
||||
"privacy.unlisted.long": "مرئي للجميع، ولكن مِن دون ميزات الاكتشاف",
|
||||
"privacy.unlisted.short": "غير مدرج",
|
||||
"privacy_policy.last_updated": "آخر تحديث {date}",
|
||||
"privacy_policy.title": "سياسة الخصوصية",
|
||||
"refresh": "أنعِش",
|
||||
|
@ -696,10 +677,8 @@
|
|||
"upload_error.poll": "لا يمكن إدراج ملفات في استطلاعات الرأي.",
|
||||
"upload_form.audio_description": "وصف للأشخاص ذي قِصر السمع",
|
||||
"upload_form.description": "وصف للمعاقين بصريا",
|
||||
"upload_form.description_missing": "لم يُضف وصف",
|
||||
"upload_form.edit": "تعديل",
|
||||
"upload_form.thumbnail": "غيّر الصورة المصغرة",
|
||||
"upload_form.undo": "حذف",
|
||||
"upload_form.video_description": "وصف للمعاقين بصريا أو لِذي قِصر السمع",
|
||||
"upload_modal.analyzing_picture": "جارٍ فحص الصورة…",
|
||||
"upload_modal.apply": "طبّق",
|
||||
|
|
|
@ -66,7 +66,6 @@
|
|||
"alert.unexpected.title": "¡Meca!",
|
||||
"announcement.announcement": "Anunciu",
|
||||
"attachments_list.unprocessed": "(ensin procesar)",
|
||||
"autosuggest_hashtag.per_week": "{count} per selmana",
|
||||
"bundle_column_error.error.body": "La páxina solicitada nun se pudo renderizar. Ye posible que seya pola mor d'un fallu nel códigu o por un problema de compatibilidá del restolador.",
|
||||
"bundle_column_error.error.title": "¡Oh, non!",
|
||||
"bundle_column_error.network.body": "Hebo un error al tentar de cargar esta páxina. Esto pudo ser pola mor d'un problema temporal cola conexón a internet o con esti sirvidor.",
|
||||
|
@ -109,13 +108,9 @@
|
|||
"compose_form.lock_disclaimer": "La to cuenta nun ye {locked}. Cualesquier perfil pue siguite pa ver los artículos que son namás pa siguidores.",
|
||||
"compose_form.lock_disclaimer.lock": "privada",
|
||||
"compose_form.placeholder": "¿En qué pienses?",
|
||||
"compose_form.poll.add_option": "Amestar una opción",
|
||||
"compose_form.poll.option_placeholder": "Opción {number}",
|
||||
"compose_form.poll.remove_option": "Quitar esta opción",
|
||||
"compose_form.publish": "Espublizar",
|
||||
"compose_form.poll.type": "Estilu",
|
||||
"compose_form.publish_form": "Artículu nuevu",
|
||||
"compose_form.publish_loud": "¡{publish}!",
|
||||
"compose_form.save_changes": "Guardar los cambeos",
|
||||
"confirmation_modal.cancel": "Encaboxar",
|
||||
"confirmations.block.block_and_report": "Bloquiar ya informar",
|
||||
"confirmations.block.confirm": "Bloquiar",
|
||||
|
@ -286,7 +281,6 @@
|
|||
"navigation_bar.community_timeline": "Llinia de tiempu llocal",
|
||||
"navigation_bar.direct": "Menciones privaes",
|
||||
"navigation_bar.domain_blocks": "Dominios bloquiaos",
|
||||
"navigation_bar.edit_profile": "Editar el perfil",
|
||||
"navigation_bar.explore": "Esploración",
|
||||
"navigation_bar.filters": "Pallabres desactivaes",
|
||||
"navigation_bar.follow_requests": "Solicitúes de siguimientu",
|
||||
|
@ -346,12 +340,7 @@
|
|||
"poll_button.add_poll": "Amestar una encuesta",
|
||||
"poll_button.remove_poll": "Quitar la encuesta",
|
||||
"privacy.change": "Configurar la privacidá del artículu",
|
||||
"privacy.direct.long": "Artículu visible namás pa los perfiles mentaos",
|
||||
"privacy.private.long": "Artículu visible namás pa los perfiles siguidores",
|
||||
"privacy.private.short": "Namás pa siguidores",
|
||||
"privacy.public.long": "Tol mundu pue ver l'artículu",
|
||||
"privacy.public.short": "Artículu públicu",
|
||||
"privacy.unlisted.long": "Artículu visible pa tol mundu mas escluyíu de les funciones de descubrimientu",
|
||||
"privacy_policy.last_updated": "Data del últimu anovamientu: {date}",
|
||||
"privacy_policy.title": "Política de privacidá",
|
||||
"refresh": "Anovar",
|
||||
|
@ -368,6 +357,7 @@
|
|||
"relative_time.seconds": "{number} s",
|
||||
"relative_time.today": "güei",
|
||||
"reply_indicator.cancel": "Encaboxar",
|
||||
"reply_indicator.poll": "Encuesta",
|
||||
"report.block": "Bloquiar",
|
||||
"report.categories.spam": "Spam",
|
||||
"report.categories.violation": "El conteníu incumple una o más normes del sirvidor",
|
||||
|
@ -492,9 +482,7 @@
|
|||
"upload_button.label": "Amestar ficheros multimedia",
|
||||
"upload_error.poll": "La xuba de ficheros nun ta permitida coles encuestes.",
|
||||
"upload_form.audio_description": "Describi'l conteníu pa persones sordes ya/o ciegues",
|
||||
"upload_form.description_missing": "Nun s'amestó la descripción",
|
||||
"upload_form.edit": "Editar",
|
||||
"upload_form.undo": "Desaniciar",
|
||||
"upload_modal.analyzing_picture": "Analizando la semeya…",
|
||||
"upload_modal.apply": "Aplicar",
|
||||
"upload_modal.applying": "Aplicando…",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Аб'ява",
|
||||
"attachments_list.unprocessed": "(неапрацаваны)",
|
||||
"audio.hide": "Схаваць аўдыя",
|
||||
"autosuggest_hashtag.per_week": "{count} за тыдзень",
|
||||
"boost_modal.combo": "Націсніце {combo}, каб прапусціць наступным разам",
|
||||
"bundle_column_error.copy_stacktrace": "Скапіраваць справаздачу пра памылку",
|
||||
"bundle_column_error.error.body": "Запытаная старонка не можа быць адлюстраваная. Гэта магло стацца праз хібу ў нашым кодзе, або праз памылку сумяшчальнасці з браўзерам.",
|
||||
|
@ -146,22 +145,12 @@
|
|||
"compose_form.lock_disclaimer": "Ваш уліковы запіс не {locked}. Усе могуць падпісацца на вас, каб бачыць допісы толькі для падпісчыкаў.",
|
||||
"compose_form.lock_disclaimer.lock": "закрыты",
|
||||
"compose_form.placeholder": "Што здарылася?",
|
||||
"compose_form.poll.add_option": "Дадаць варыянт",
|
||||
"compose_form.poll.duration": "Працягласць апытання",
|
||||
"compose_form.poll.option_placeholder": "Варыянт {number}",
|
||||
"compose_form.poll.remove_option": "Выдаліць гэты варыянт",
|
||||
"compose_form.poll.switch_to_multiple": "Змяніце апытанне, каб дазволіць некалькі варыянтаў адказу",
|
||||
"compose_form.poll.switch_to_single": "Змяніце апытанне, каб дазволіць адзіны варыянт адказу",
|
||||
"compose_form.publish": "Апублікаваць",
|
||||
"compose_form.publish_form": "Апублікаваць",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Захаваць змены",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Пазначыць кантэнт як далікатны} other {Пазначыць кантэнт як далікатны}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {Кантэнт пазначаны як далікатны} other {Кантэнт пазначаны як далікатны}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Кантэнт не пазначаны як далікатны} other {Кантэнт не пазначаны як далікатны}}",
|
||||
"compose_form.spoiler.marked": "Выдаліць папярэджанне аб змесціве",
|
||||
"compose_form.spoiler.unmarked": "Дадаць папярэджанне аб змесціве",
|
||||
"compose_form.spoiler_placeholder": "Напішыце сваё папярэджанне тут",
|
||||
"confirmation_modal.cancel": "Скасаваць",
|
||||
"confirmations.block.block_and_report": "Заблакіраваць і паскардзіцца",
|
||||
"confirmations.block.confirm": "Заблакіраваць",
|
||||
|
@ -408,7 +397,6 @@
|
|||
"navigation_bar.direct": "Асабістыя згадванні",
|
||||
"navigation_bar.discover": "Даведайцесь",
|
||||
"navigation_bar.domain_blocks": "Заблакіраваныя дамены",
|
||||
"navigation_bar.edit_profile": "Рэдагаваць профіль",
|
||||
"navigation_bar.explore": "Агляд",
|
||||
"navigation_bar.favourites": "Упадабанае",
|
||||
"navigation_bar.filters": "Ігнараваныя словы",
|
||||
|
@ -526,14 +514,7 @@
|
|||
"poll_button.add_poll": "Дадаць апытанне",
|
||||
"poll_button.remove_poll": "Выдаліць апытанне",
|
||||
"privacy.change": "Змяніць прыватнасць допісу",
|
||||
"privacy.direct.long": "Паказаць толькі згаданым карыстальнікам",
|
||||
"privacy.direct.short": "Толькі згаданыя людзі",
|
||||
"privacy.private.long": "Паказаць толькі падпісчыкам",
|
||||
"privacy.private.short": "Толькі для падпісчыкаў",
|
||||
"privacy.public.long": "Бачны для ўсіх",
|
||||
"privacy.public.short": "Публічны",
|
||||
"privacy.unlisted.long": "Бачна для ўсіх, але не праз магчымасці агляду",
|
||||
"privacy.unlisted.short": "Не ў стужках",
|
||||
"privacy_policy.last_updated": "Адноўлена {date}",
|
||||
"privacy_policy.title": "Палітыка канфідэнцыйнасці",
|
||||
"recommended": "Рэкамендуем",
|
||||
|
@ -715,10 +696,8 @@
|
|||
"upload_error.poll": "Немагчыма прымацаваць файл да апытання.",
|
||||
"upload_form.audio_description": "Апісанне для людзей з парушэннямі слыху",
|
||||
"upload_form.description": "Апісаць для людзей са слабым зрокам",
|
||||
"upload_form.description_missing": "Апісанне адсутнічае",
|
||||
"upload_form.edit": "Рэдагаваць",
|
||||
"upload_form.thumbnail": "Змяніць мініяцюру",
|
||||
"upload_form.undo": "Выдаліць",
|
||||
"upload_form.video_description": "Апісанне для людзей з парушэннямі зроку і слыху",
|
||||
"upload_modal.analyzing_picture": "Аналіз выявы…",
|
||||
"upload_modal.apply": "Ужыць",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Оповестяване",
|
||||
"attachments_list.unprocessed": "(необработено)",
|
||||
"audio.hide": "Скриване на звука",
|
||||
"autosuggest_hashtag.per_week": "{count} на седмица",
|
||||
"boost_modal.combo": "Можете да натиснете {combo}, за да пропуснете това следващия път",
|
||||
"bundle_column_error.copy_stacktrace": "Копиране на доклада за грешката",
|
||||
"bundle_column_error.error.body": "Заявената страница не може да се изобрази. Това може да е заради грешка в кода ни или проблем със съвместимостта на браузъра.",
|
||||
|
@ -148,20 +147,20 @@
|
|||
"compose_form.placeholder": "Какво мислите?",
|
||||
"compose_form.poll.add_option": "Добавяне на избор",
|
||||
"compose_form.poll.duration": "Времетраене на анкетата",
|
||||
"compose_form.poll.multiple": "Множествен избор",
|
||||
"compose_form.poll.option_placeholder": "Избор {number}",
|
||||
"compose_form.poll.remove_option": "Премахване на този избор",
|
||||
"compose_form.poll.remove_option": "Премахване на тази възможност",
|
||||
"compose_form.poll.single": "Подберете нещо",
|
||||
"compose_form.poll.switch_to_multiple": "Промяна на анкетата, за да се позволят множество възможни избора",
|
||||
"compose_form.poll.switch_to_single": "Промяна на анкетата, за да се позволи един възможен избор",
|
||||
"compose_form.publish": "Публикуване",
|
||||
"compose_form.poll.type": "Стил",
|
||||
"compose_form.publish": "Публикация",
|
||||
"compose_form.publish_form": "Публикуване",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Запазване на промените",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Маркиране на мултимедията като деликатна} other {Маркиране на мултимедиите като деликатни}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {мултимедия е означена като деликатна} other {мултимедии са означени като деликатни}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Мултимедията не е маркирана като деликатна} other {Мултимедиите не са маркирани като деликатни}}",
|
||||
"compose_form.reply": "Отговор",
|
||||
"compose_form.save_changes": "Обновяване",
|
||||
"compose_form.spoiler.marked": "Отстраняване на предупреждение за съдържание",
|
||||
"compose_form.spoiler.unmarked": "Добавяне на предупреждение за съдържание",
|
||||
"compose_form.spoiler_placeholder": "Тук напишете предупреждението си",
|
||||
"compose_form.spoiler_placeholder": "Предупреждение за съдържание (по избор)",
|
||||
"confirmation_modal.cancel": "Отказ",
|
||||
"confirmations.block.block_and_report": "Блокиране и докладване",
|
||||
"confirmations.block.confirm": "Блокиране",
|
||||
|
@ -408,7 +407,6 @@
|
|||
"navigation_bar.direct": "Частни споменавания",
|
||||
"navigation_bar.discover": "Откриване",
|
||||
"navigation_bar.domain_blocks": "Блокирани домейни",
|
||||
"navigation_bar.edit_profile": "Редактиране на профила",
|
||||
"navigation_bar.explore": "Изследване",
|
||||
"navigation_bar.favourites": "Любими",
|
||||
"navigation_bar.filters": "Заглушени думи",
|
||||
|
@ -526,14 +524,14 @@
|
|||
"poll_button.add_poll": "Анкетиране",
|
||||
"poll_button.remove_poll": "Премахване на анкета",
|
||||
"privacy.change": "Промяна на поверителността на публикация",
|
||||
"privacy.direct.long": "Видимо само за споменатите потребители",
|
||||
"privacy.direct.short": "Само споменатите хора",
|
||||
"privacy.private.long": "Видимо само за последователите",
|
||||
"privacy.private.short": "Само последователи",
|
||||
"privacy.public.long": "Видимо за всички",
|
||||
"privacy.direct.long": "Всеки споменат в публикацията",
|
||||
"privacy.direct.short": "Определени хора",
|
||||
"privacy.private.long": "Само последователите ви",
|
||||
"privacy.private.short": "Последователи",
|
||||
"privacy.public.long": "Всеки във и извън Mastodon",
|
||||
"privacy.public.short": "Публично",
|
||||
"privacy.unlisted.long": "Видимо за всички, но не чрез възможността за откриване",
|
||||
"privacy.unlisted.short": "Несписъчно",
|
||||
"privacy.unlisted.long": "По-малко алгоритмични фанфари",
|
||||
"privacy.unlisted.short": "Тиха публика",
|
||||
"privacy_policy.last_updated": "Последно осъвременяване на {date}",
|
||||
"privacy_policy.title": "Политика за поверителност",
|
||||
"recommended": "Препоръчано",
|
||||
|
@ -551,7 +549,9 @@
|
|||
"relative_time.minutes": "{number}м.",
|
||||
"relative_time.seconds": "{number}с.",
|
||||
"relative_time.today": "днес",
|
||||
"reply_indicator.attachments": "{count, plural, one {# прикаване} other {# прикачвания}}",
|
||||
"reply_indicator.cancel": "Отказ",
|
||||
"reply_indicator.poll": "Анкета",
|
||||
"report.block": "Блокиране",
|
||||
"report.block_explanation": "Няма да им виждате публикациите. Те няма да могат да виждат публикациите ви или да ви последват. Те ще могат да казват, че са били блокирани.",
|
||||
"report.categories.legal": "Правни въпроси",
|
||||
|
@ -715,10 +715,8 @@
|
|||
"upload_error.poll": "Качването на файлове не е позволено с анкети.",
|
||||
"upload_form.audio_description": "Опишете за хора, които са глухи или трудно чуват",
|
||||
"upload_form.description": "Опишете за хора, които са слепи или имат слабо зрение",
|
||||
"upload_form.description_missing": "Няма добавен опис",
|
||||
"upload_form.edit": "Редактиране",
|
||||
"upload_form.thumbnail": "Промяна на миниобраза",
|
||||
"upload_form.undo": "Изтриване",
|
||||
"upload_form.video_description": "Опишете за хора, които са глухи или трудно чуват, слепи или имат слабо зрение",
|
||||
"upload_modal.analyzing_picture": "Снимков анализ…",
|
||||
"upload_modal.apply": "Прилагане",
|
||||
|
|
|
@ -85,7 +85,6 @@
|
|||
"announcement.announcement": "ঘোষণা",
|
||||
"attachments_list.unprocessed": "(প্রক্রিয়া করা যায়নি)",
|
||||
"audio.hide": "অডিও লুকান",
|
||||
"autosuggest_hashtag.per_week": "প্রতি সপ্তাহে {count}",
|
||||
"boost_modal.combo": "পরেরবার আপনি {combo} টিপলে এটি আর আসবে না",
|
||||
"bundle_column_error.copy_stacktrace": "এরর রিপোর্ট কপি করুন",
|
||||
"bundle_column_error.error.body": "অনুরোধ করা পৃষ্ঠাটি রেন্ডার করা যায়নি। এটি আমাদের কোডে একটি বাগ বা ব্রাউজার সামঞ্জস্যের সমস্যার কারণে হতে পারে।",
|
||||
|
@ -142,22 +141,12 @@
|
|||
"compose_form.lock_disclaimer": "আপনার নিবন্ধনে তালা দেওয়া নেই, যে কেও আপনাকে অনুসরণ করতে পারবে এবং অনুশারকদের জন্য লেখা দেখতে পারবে।",
|
||||
"compose_form.lock_disclaimer.lock": "তালা দেওয়া",
|
||||
"compose_form.placeholder": "আপনি কি ভাবছেন ?",
|
||||
"compose_form.poll.add_option": "আরেকটি বিকল্প যোগ করুন",
|
||||
"compose_form.poll.duration": "ভোটগ্রহনের সময়",
|
||||
"compose_form.poll.option_placeholder": "বিকল্প {number}",
|
||||
"compose_form.poll.remove_option": "এই বিকল্পটি মুছে ফেলুন",
|
||||
"compose_form.poll.switch_to_multiple": "একাধিক পছন্দ অনুমতি দেওয়ার জন্য পোল পরিবর্তন করুন",
|
||||
"compose_form.poll.switch_to_single": "একটি একক পছন্দের অনুমতি দেওয়ার জন্য পোল পরিবর্তন করুন",
|
||||
"compose_form.publish": "প্রকাশ করুন",
|
||||
"compose_form.publish_form": "প্রকাশ করুন",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "পরিবর্তনগুলো প্রকাশ করুন",
|
||||
"compose_form.sensitive.hide": "এই ছবি বা ভিডিওটি সংবেদনশীল হিসেবে চিহ্নিত করতে",
|
||||
"compose_form.sensitive.marked": "এই ছবি বা ভিডিওটি সংবেদনশীল হিসেবে চিহ্নিত করা হয়েছে",
|
||||
"compose_form.sensitive.unmarked": "এই ছবি বা ভিডিওটি সংবেদনশীল হিসেবে চিহ্নিত করা হয়নি",
|
||||
"compose_form.spoiler.marked": "সতর্কতার পিছনে লেখানটি লুকানো আছে",
|
||||
"compose_form.spoiler.unmarked": "লেখাটি লুকানো নেই",
|
||||
"compose_form.spoiler_placeholder": "আপনার লেখা দেখার সাবধানবাণী লিখুন",
|
||||
"confirmation_modal.cancel": "বাতিল করুন",
|
||||
"confirmations.block.block_and_report": "ব্লক করুন এবং রিপোর্ট করুন",
|
||||
"confirmations.block.confirm": "ব্লক করুন",
|
||||
|
@ -324,7 +313,6 @@
|
|||
"navigation_bar.compose": "নতুন টুট লিখুন",
|
||||
"navigation_bar.discover": "ঘুরে দেখুন",
|
||||
"navigation_bar.domain_blocks": "লুকানো ডোমেনগুলি",
|
||||
"navigation_bar.edit_profile": "নিজের পাতা সম্পাদনা করতে",
|
||||
"navigation_bar.explore": "পরিব্রাজন",
|
||||
"navigation_bar.favourites": "পছন্দসমূহ",
|
||||
"navigation_bar.filters": "বন্ধ করা শব্দ",
|
||||
|
@ -395,12 +383,7 @@
|
|||
"poll_button.add_poll": "একটা নির্বাচন যোগ করতে",
|
||||
"poll_button.remove_poll": "নির্বাচন বাদ দিতে",
|
||||
"privacy.change": "লেখার গোপনীয়তা অবস্থা ঠিক করতে",
|
||||
"privacy.direct.long": "শুধুমাত্র উল্লেখিত ব্যবহারকারীদের কাছে লিখতে",
|
||||
"privacy.direct.short": "Direct",
|
||||
"privacy.private.long": "শুধুমাত্র আপনার অনুসরণকারীদের লিখতে",
|
||||
"privacy.private.short": "Followers-only",
|
||||
"privacy.public.short": "সর্বজনীন প্রকাশ্য",
|
||||
"privacy.unlisted.short": "প্রকাশ্য নয়",
|
||||
"refresh": "সতেজ করা",
|
||||
"regeneration_indicator.label": "আসছে…",
|
||||
"regeneration_indicator.sublabel": "আপনার বাড়ির-সময়রেখা প্রস্তূত করা হচ্ছে!",
|
||||
|
@ -506,7 +489,6 @@
|
|||
"upload_form.description": "যারা দেখতে পায়না তাদের জন্য এটা বর্ণনা করতে",
|
||||
"upload_form.edit": "সম্পাদন",
|
||||
"upload_form.thumbnail": "থাম্বনেল পরিবর্তন করুন",
|
||||
"upload_form.undo": "মুছে ফেলতে",
|
||||
"upload_form.video_description": "শ্রবণশক্তি হ্রাস বা চাক্ষুষ প্রতিবন্ধী ব্যক্তিদের জন্য বর্ণনা করুন",
|
||||
"upload_modal.analyzing_picture": "চিত্র বিশ্লেষণ করা হচ্ছে…",
|
||||
"upload_modal.apply": "প্রয়োগ করুন",
|
||||
|
|
|
@ -87,7 +87,6 @@
|
|||
"announcement.announcement": "Kemennad",
|
||||
"attachments_list.unprocessed": "(ket meret)",
|
||||
"audio.hide": "Kuzhat ar c'hleved",
|
||||
"autosuggest_hashtag.per_week": "{count} bep sizhun",
|
||||
"boost_modal.combo": "Ar wezh kentañ e c'halliot gwaskañ war {combo} evit tremen hebiou",
|
||||
"bundle_column_error.copy_stacktrace": "Eilañ an danevell fazi",
|
||||
"bundle_column_error.error.body": "N'haller ket skrammañ ar bajenn goulennet. Gallout a ra bezañ abalamour d'ur beug er c'hod pe d'ur gudenn keverlec'hded gant ar merdeer.",
|
||||
|
@ -143,22 +142,12 @@
|
|||
"compose_form.lock_disclaimer": "N'eo ket {locked} ho kont. An holl a c'hal ho heuliañ evit gwelet ho toudoù prevez.",
|
||||
"compose_form.lock_disclaimer.lock": "prennet",
|
||||
"compose_form.placeholder": "Petra emaoc'h o soñjal e-barzh ?",
|
||||
"compose_form.poll.add_option": "Ouzhpenniñ un dibab",
|
||||
"compose_form.poll.duration": "Pad ar sontadeg",
|
||||
"compose_form.poll.option_placeholder": "Dibab {number}",
|
||||
"compose_form.poll.remove_option": "Lemel an dibab-mañ",
|
||||
"compose_form.poll.switch_to_multiple": "Kemmañ ar sontadeg evit aotren meur a zibab",
|
||||
"compose_form.poll.switch_to_single": "Kemmañ ar sontadeg evit aotren un dibab hepken",
|
||||
"compose_form.publish": "Embann",
|
||||
"compose_form.publish_form": "Embann",
|
||||
"compose_form.publish_loud": "{publish} !",
|
||||
"compose_form.save_changes": "Enrollañ ar cheñchamantoù",
|
||||
"compose_form.sensitive.hide": "Merkañ ar media evel kizidik",
|
||||
"compose_form.sensitive.marked": "Merket eo ar media evel kizidik",
|
||||
"compose_form.sensitive.unmarked": "N'eo ket merket ar media evel kizidik",
|
||||
"compose_form.spoiler.marked": "Kuzhet eo an destenn a-dreñv ur c'hemenn",
|
||||
"compose_form.spoiler.unmarked": "N'eo ket kuzhet an destenn",
|
||||
"compose_form.spoiler_placeholder": "Skrivit ho kemenn diwall amañ",
|
||||
"confirmation_modal.cancel": "Nullañ",
|
||||
"confirmations.block.block_and_report": "Berzañ ha Disklêriañ",
|
||||
"confirmations.block.confirm": "Stankañ",
|
||||
|
@ -383,7 +372,6 @@
|
|||
"navigation_bar.direct": "Menegoù prevez",
|
||||
"navigation_bar.discover": "Dizoleiñ",
|
||||
"navigation_bar.domain_blocks": "Domanioù kuzhet",
|
||||
"navigation_bar.edit_profile": "Kemmañ ar profil",
|
||||
"navigation_bar.explore": "Furchal",
|
||||
"navigation_bar.favourites": "Muiañ-karet",
|
||||
"navigation_bar.filters": "Gerioù kuzhet",
|
||||
|
@ -487,14 +475,7 @@
|
|||
"poll_button.add_poll": "Ouzhpennañ ur sontadeg",
|
||||
"poll_button.remove_poll": "Dilemel ar sontadeg",
|
||||
"privacy.change": "Cheñch prevezded an toud",
|
||||
"privacy.direct.long": "Embann evit an implijer·ezed·ien meneget hepken",
|
||||
"privacy.direct.short": "Tud meneget hepken",
|
||||
"privacy.private.long": "Embann evit ar re a heuilh ac'hanon hepken",
|
||||
"privacy.private.short": "Tud koumanantet hepken",
|
||||
"privacy.public.long": "Gwelus d'an holl",
|
||||
"privacy.public.short": "Publik",
|
||||
"privacy.unlisted.long": "Gwelus gant an holl, met hep arc'hweladur dizoleiñ",
|
||||
"privacy.unlisted.short": "Anlistennet",
|
||||
"privacy_policy.last_updated": "Hizivadenn ziwezhañ {date}",
|
||||
"privacy_policy.title": "Reolennoù Prevezded",
|
||||
"recommended": "Erbedet",
|
||||
|
@ -667,10 +648,8 @@
|
|||
"upload_error.poll": "Pellgargañ restroù n'eo ket aotreet gant sontadegoù.",
|
||||
"upload_form.audio_description": "Diskrivañ evit tud a zo kollet o c'hlev",
|
||||
"upload_form.description": "Diskrivañ evit tud a zo kollet o gweled",
|
||||
"upload_form.description_missing": "Deskrivadur diank",
|
||||
"upload_form.edit": "Kemmañ",
|
||||
"upload_form.thumbnail": "Kemmañ ar velvenn",
|
||||
"upload_form.undo": "Dilemel",
|
||||
"upload_form.video_description": "Diskrivañ evit tud a zo kollet o gweled pe o c'hlev",
|
||||
"upload_modal.analyzing_picture": "O tielfennañ ar skeudenn…",
|
||||
"upload_modal.apply": "Arloañ",
|
||||
|
|
|
@ -66,8 +66,6 @@
|
|||
"onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
|
||||
"onboarding.steps.share_profile.title": "Share your profile",
|
||||
"privacy.change": "Adjust status privacy",
|
||||
"privacy.direct.short": "Direct",
|
||||
"privacy.private.short": "Followers-only",
|
||||
"report.placeholder": "Type or paste additional comments",
|
||||
"report.submit": "Submit report",
|
||||
"report.target": "Report {target}",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Anunci",
|
||||
"attachments_list.unprocessed": "(sense processar)",
|
||||
"audio.hide": "Amaga l'àudio",
|
||||
"autosuggest_hashtag.per_week": "{count} per setmana",
|
||||
"boost_modal.combo": "Pots prémer {combo} per a evitar-ho el pròxim cop",
|
||||
"bundle_column_error.copy_stacktrace": "Copia l'informe d'error",
|
||||
"bundle_column_error.error.body": "No s'ha pogut renderitzar la pàgina sol·licitada. Podria ser per un error en el nostre codi o per un problema de compatibilitat del navegador.",
|
||||
|
@ -146,22 +145,22 @@
|
|||
"compose_form.lock_disclaimer": "El teu compte no està {locked}. Tothom pot seguir-te i veure els tuts de només per a seguidors.",
|
||||
"compose_form.lock_disclaimer.lock": "blocat",
|
||||
"compose_form.placeholder": "Què tens al cap?",
|
||||
"compose_form.poll.add_option": "Afegeix una opció",
|
||||
"compose_form.poll.add_option": "Afegiu una opció",
|
||||
"compose_form.poll.duration": "Durada de l'enquesta",
|
||||
"compose_form.poll.multiple": "Opcions múltiples",
|
||||
"compose_form.poll.option_placeholder": "Opció {number}",
|
||||
"compose_form.poll.remove_option": "Elimina aquesta opció",
|
||||
"compose_form.poll.remove_option": "Treu aquesta opció",
|
||||
"compose_form.poll.single": "Trieu-ne una",
|
||||
"compose_form.poll.switch_to_multiple": "Canvia l’enquesta per a permetre múltiples opcions",
|
||||
"compose_form.poll.switch_to_single": "Canvia l’enquesta per a permetre una única opció",
|
||||
"compose_form.publish": "Tut",
|
||||
"compose_form.poll.type": "Estil",
|
||||
"compose_form.publish": "Publica",
|
||||
"compose_form.publish_form": "Nou tut",
|
||||
"compose_form.publish_loud": "Tut!",
|
||||
"compose_form.save_changes": "Desa els canvis",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Marca mèdia com a sensible} other {Marca mèdia com a sensible}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {Contingut marcat com a sensible} other {Contingut marcat com a sensible}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Contingut no marcat com a sensible} other {Contingut no marcat com a sensible}}",
|
||||
"compose_form.reply": "Responeu",
|
||||
"compose_form.save_changes": "Actualitza",
|
||||
"compose_form.spoiler.marked": "Elimina l'avís de contingut",
|
||||
"compose_form.spoiler.unmarked": "Afegeix avís de contingut",
|
||||
"compose_form.spoiler_placeholder": "Escriu l'avís aquí",
|
||||
"compose_form.spoiler_placeholder": "Avís de contingut (opcional)",
|
||||
"confirmation_modal.cancel": "Cancel·la",
|
||||
"confirmations.block.block_and_report": "Bloca i denuncia",
|
||||
"confirmations.block.confirm": "Bloca",
|
||||
|
@ -408,7 +407,6 @@
|
|||
"navigation_bar.direct": "Mencions privades",
|
||||
"navigation_bar.discover": "Descobreix",
|
||||
"navigation_bar.domain_blocks": "Dominis blocats",
|
||||
"navigation_bar.edit_profile": "Edita el perfil",
|
||||
"navigation_bar.explore": "Explora",
|
||||
"navigation_bar.favourites": "Favorits",
|
||||
"navigation_bar.filters": "Paraules silenciades",
|
||||
|
@ -521,19 +519,17 @@
|
|||
"poll.total_people": "{count, plural, one {# persona} other {# persones}}",
|
||||
"poll.total_votes": "{count, plural, one {# vot} other {# vots}}",
|
||||
"poll.vote": "Vota",
|
||||
"poll.voted": "Vas votar per aquesta resposta",
|
||||
"poll.voted": "Vau votar aquesta resposta",
|
||||
"poll.votes": "{votes, plural, one {# vot} other {# vots}}",
|
||||
"poll_button.add_poll": "Afegeix una enquesta",
|
||||
"poll_button.remove_poll": "Elimina l'enquesta",
|
||||
"privacy.change": "Canvia la privacitat del tut",
|
||||
"privacy.direct.long": "Visible només per als usuaris esmentats",
|
||||
"privacy.direct.short": "Només gent mencionada",
|
||||
"privacy.private.long": "Visible només per als seguidors",
|
||||
"privacy.private.short": "Només seguidors",
|
||||
"privacy.public.long": "Visible per a tothom",
|
||||
"privacy.direct.long": "Tothom mencionat en aquesta publicació",
|
||||
"privacy.direct.short": "Persones concretes",
|
||||
"privacy.private.long": "Només els vostres seguidors",
|
||||
"privacy.private.short": "Seguidors",
|
||||
"privacy.public.long": "Tothom dins o fora Mastodon",
|
||||
"privacy.public.short": "Públic",
|
||||
"privacy.unlisted.long": "Visible per a tothom però exclosa de les funcions de descobriment",
|
||||
"privacy.unlisted.short": "No llistada",
|
||||
"privacy_policy.last_updated": "Darrera actualització {date}",
|
||||
"privacy_policy.title": "Política de Privacitat",
|
||||
"recommended": "Recomanat",
|
||||
|
@ -715,10 +711,8 @@
|
|||
"upload_error.poll": "No es permet carregar fitxers a les enquestes.",
|
||||
"upload_form.audio_description": "Descriu-ho per a persones amb problemes d'audició",
|
||||
"upload_form.description": "Descriu-ho per a persones amb problemes de visió",
|
||||
"upload_form.description_missing": "No s'hi ha afegit cap descripció",
|
||||
"upload_form.edit": "Edita",
|
||||
"upload_form.thumbnail": "Canvia la miniatura",
|
||||
"upload_form.undo": "Elimina",
|
||||
"upload_form.video_description": "Descriu-ho per a persones amb problemes de visió o audició",
|
||||
"upload_modal.analyzing_picture": "S'analitza la imatge…",
|
||||
"upload_modal.apply": "Aplica",
|
||||
|
|
|
@ -76,7 +76,6 @@
|
|||
"announcement.announcement": "بانگەواز",
|
||||
"attachments_list.unprocessed": "(unprocessed)",
|
||||
"audio.hide": "شاردنەوەی دەنگ",
|
||||
"autosuggest_hashtag.per_week": "{count} هەرهەفتە",
|
||||
"boost_modal.combo": "دەتوانیت دەست بنێی بە سەر {combo} بۆ بازدان لە جاری داهاتوو",
|
||||
"bundle_column_error.copy_stacktrace": "ڕاپۆرتی هەڵەی کۆپی بکە",
|
||||
"bundle_column_error.error.body": "لاپەڕەی داواکراو نەتوانرا ڕەندەر بکرێت. دەکرێت بەهۆی هەڵەیەکی کۆدەکەمانەوە بێت، یان کێشەی گونجانی وێبگەڕ.",
|
||||
|
@ -128,22 +127,12 @@
|
|||
"compose_form.lock_disclaimer": "هەژمێرەکەی لە حاڵەتی {locked}. هەر کەسێک دەتوانێت شوێنت بکەوێت بۆ پیشاندانی بابەتەکانی تەنها دوایخۆی.",
|
||||
"compose_form.lock_disclaimer.lock": "قفڵ دراوە",
|
||||
"compose_form.placeholder": "چی لە مێشکتدایە?",
|
||||
"compose_form.poll.add_option": "زیادکردنی هەڵبژاردەیەک",
|
||||
"compose_form.poll.duration": "ماوەی ڕاپرسی",
|
||||
"compose_form.poll.option_placeholder": "هەڵبژاردن {number}",
|
||||
"compose_form.poll.remove_option": "لابردنی ئەم هەڵبژاردەیە",
|
||||
"compose_form.poll.switch_to_multiple": "ڕاپرسی بگۆڕە بۆ ڕێگەدان بە چەند هەڵبژاردنێک",
|
||||
"compose_form.poll.switch_to_single": "گۆڕینی ڕاپرسی بۆ ڕێگەدان بە تاکە هەڵبژاردنێک",
|
||||
"compose_form.publish": "بڵاوی بکەوە",
|
||||
"compose_form.publish_form": "بڵاوی بکەوە",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "پاشکەوتی گۆڕانکاریەکان",
|
||||
"compose_form.sensitive.hide": "نیشانکردنی میدیا وەک هەستیار",
|
||||
"compose_form.sensitive.marked": "وادەی کۆتایی",
|
||||
"compose_form.sensitive.unmarked": "میدیا وەک هەستیار نیشان نەکراوە",
|
||||
"compose_form.spoiler.marked": "دەق لە پشت ئاگاداریدا شاراوەتەوە",
|
||||
"compose_form.spoiler.unmarked": "دەق شاراوە نییە",
|
||||
"compose_form.spoiler_placeholder": "ئاگاداریەکەت لێرە بنووسە",
|
||||
"confirmation_modal.cancel": "هەڵوەشاندنەوه",
|
||||
"confirmations.block.block_and_report": "بلۆک & گوزارشت",
|
||||
"confirmations.block.confirm": "بلۆک",
|
||||
|
@ -354,7 +343,6 @@
|
|||
"navigation_bar.direct": "ئاماژەی تایبەت",
|
||||
"navigation_bar.discover": "دۆزینەوە",
|
||||
"navigation_bar.domain_blocks": "دۆمەینە بلۆک کراوەکان",
|
||||
"navigation_bar.edit_profile": "دەستکاری پرۆفایل بکە",
|
||||
"navigation_bar.explore": "گەڕان",
|
||||
"navigation_bar.filters": "وشە کپەکان",
|
||||
"navigation_bar.follow_requests": "بەدواداچوی داواکاریەکان بکە",
|
||||
|
@ -439,14 +427,7 @@
|
|||
"poll_button.add_poll": "ڕاپرسییەک زیاد بکە",
|
||||
"poll_button.remove_poll": "دهنگدان بسڕهوه",
|
||||
"privacy.change": "ڕێکخستنی تایبەتمەندی توت",
|
||||
"privacy.direct.long": "تەنیا بۆ بەکارهێنەرانی ناوبراو",
|
||||
"privacy.direct.short": "تەنها کەسانی باس کراو",
|
||||
"privacy.private.long": "بینراو تەنها بۆ شوێنکەوتوان",
|
||||
"privacy.private.short": "تەنیا شوێنکەوتووان",
|
||||
"privacy.public.long": "بۆ هەمووان دیارە",
|
||||
"privacy.public.short": "گشتی",
|
||||
"privacy.unlisted.long": "بۆ هەمووان دیارە، بەڵام لە تایبەتمەندییەکانی دۆزینەوە دەرچووە",
|
||||
"privacy.unlisted.short": "لە لیست نەکراو",
|
||||
"privacy_policy.last_updated": "دوایین نوێکردنەوە {date}",
|
||||
"privacy_policy.title": "سیاسەتی تایبەتێتی",
|
||||
"refresh": "نوێکردنەوە",
|
||||
|
@ -610,10 +591,8 @@
|
|||
"upload_error.poll": "فایل و ڕاپرسی پێکەوە ڕێپێنەدراون.",
|
||||
"upload_form.audio_description": "پەیامەکەت بۆ نابیستەکان",
|
||||
"upload_form.description": "پەیامەکەت بۆ نابیناکان",
|
||||
"upload_form.description_missing": "هیچ وەسفێک زیاد نەکراوە",
|
||||
"upload_form.edit": "دەستکاری",
|
||||
"upload_form.thumbnail": "گۆڕانی وینۆچکە",
|
||||
"upload_form.undo": "بیسڕەوە",
|
||||
"upload_form.video_description": "پەیامەکەت بۆ نابیست و نابیناکان",
|
||||
"upload_modal.analyzing_picture": "وێنەکە شی دەکرێتەوە…",
|
||||
"upload_modal.apply": "بیسەپێنە",
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
"alert.unexpected.title": "Uups!",
|
||||
"announcement.announcement": "Annunziu",
|
||||
"attachments_list.unprocessed": "(micca trattata)",
|
||||
"autosuggest_hashtag.per_week": "{count} per settimana",
|
||||
"boost_modal.combo": "Pudete appughjà nant'à {combo} per saltà quessa a prussima volta",
|
||||
"bundle_column_error.retry": "Pruvà torna",
|
||||
"bundle_modal_error.close": "Chjudà",
|
||||
|
@ -80,20 +79,12 @@
|
|||
"compose_form.lock_disclaimer": "U vostru contu ùn hè micca {locked}. Tuttu u mondu pò seguitavi è vede i vostri statuti privati.",
|
||||
"compose_form.lock_disclaimer.lock": "privatu",
|
||||
"compose_form.placeholder": "À chè pensate?",
|
||||
"compose_form.poll.add_option": "Aghjunghje scelta",
|
||||
"compose_form.poll.duration": "Durata di u scandagliu",
|
||||
"compose_form.poll.option_placeholder": "Scelta {number}",
|
||||
"compose_form.poll.remove_option": "Toglie sta scelta",
|
||||
"compose_form.poll.switch_to_multiple": "Cambià u scandagliu per accittà parechje scelte",
|
||||
"compose_form.poll.switch_to_single": "Cambià u scandagliu per ùn accittà ch'una scelta",
|
||||
"compose_form.publish_form": "Publish",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Indicà u media cum'è sensibile} other {Indicà i media cum'è sensibili}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {Media indicatu cum'è sensibile} other {Media indicati cum'è sensibili}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Media micca indicatu cum'è sensibile} other {Media micca indicati cum'è sensibili}}",
|
||||
"compose_form.spoiler.marked": "Testu piattatu daret'à un'avertimentu",
|
||||
"compose_form.spoiler.unmarked": "Testu micca piattatu",
|
||||
"compose_form.spoiler_placeholder": "Scrive u vostr'avertimentu quì",
|
||||
"confirmation_modal.cancel": "Annullà",
|
||||
"confirmations.block.block_and_report": "Bluccà è signalà",
|
||||
"confirmations.block.confirm": "Bluccà",
|
||||
|
@ -245,7 +236,6 @@
|
|||
"navigation_bar.compose": "Scrive un novu statutu",
|
||||
"navigation_bar.discover": "Scopre",
|
||||
"navigation_bar.domain_blocks": "Duminii piattati",
|
||||
"navigation_bar.edit_profile": "Mudificà u prufile",
|
||||
"navigation_bar.filters": "Parolle silenzate",
|
||||
"navigation_bar.follow_requests": "Dumande d'abbunamentu",
|
||||
"navigation_bar.follows_and_followers": "Abbunati è abbunamenti",
|
||||
|
@ -318,12 +308,7 @@
|
|||
"poll_button.add_poll": "Aghjunghje",
|
||||
"poll_button.remove_poll": "Toglie u scandagliu",
|
||||
"privacy.change": "Mudificà a cunfidenzialità di u statutu",
|
||||
"privacy.direct.long": "Mandà solu à quelli chì so mintuvati",
|
||||
"privacy.direct.short": "Direct",
|
||||
"privacy.private.long": "Mustrà solu à l'abbunati",
|
||||
"privacy.private.short": "Followers-only",
|
||||
"privacy.public.short": "Pubblicu",
|
||||
"privacy.unlisted.short": "Micca listatu",
|
||||
"refresh": "Attualizà",
|
||||
"regeneration_indicator.label": "Caricamentu…",
|
||||
"regeneration_indicator.sublabel": "Priparazione di a vostra pagina d'accolta!",
|
||||
|
@ -409,7 +394,6 @@
|
|||
"upload_form.description": "Discrizzione per i malvistosi",
|
||||
"upload_form.edit": "Mudificà",
|
||||
"upload_form.thumbnail": "Cambià vignetta",
|
||||
"upload_form.undo": "Sguassà",
|
||||
"upload_form.video_description": "Discrizzione per i ciochi o cechi",
|
||||
"upload_modal.analyzing_picture": "Analisi di u ritrattu…",
|
||||
"upload_modal.apply": "Affettà",
|
||||
|
|
|
@ -87,7 +87,6 @@
|
|||
"announcement.announcement": "Oznámení",
|
||||
"attachments_list.unprocessed": "(nezpracováno)",
|
||||
"audio.hide": "Skrýt zvuk",
|
||||
"autosuggest_hashtag.per_week": "{count} za týden",
|
||||
"boost_modal.combo": "Příště můžete pro přeskočení stisknout {combo}",
|
||||
"bundle_column_error.copy_stacktrace": "Zkopírovat zprávu o chybě",
|
||||
"bundle_column_error.error.body": "Požadovanou stránku nelze vykreslit. Může to být způsobeno chybou v našem kódu nebo problémem s kompatibilitou prohlížeče.",
|
||||
|
@ -144,22 +143,12 @@
|
|||
"compose_form.lock_disclaimer": "Váš účet není {locked}. Kdokoliv vás může sledovat a vidět vaše příspěvky učené pouze pro sledující.",
|
||||
"compose_form.lock_disclaimer.lock": "zamčený",
|
||||
"compose_form.placeholder": "Co se vám honí hlavou?",
|
||||
"compose_form.poll.add_option": "Přidat volbu",
|
||||
"compose_form.poll.duration": "Doba trvání ankety",
|
||||
"compose_form.poll.option_placeholder": "Volba {number}",
|
||||
"compose_form.poll.remove_option": "Odebrat tuto volbu",
|
||||
"compose_form.poll.switch_to_multiple": "Povolit u ankety výběr více voleb",
|
||||
"compose_form.poll.switch_to_single": "Povolit u ankety výběr pouze jedné volby",
|
||||
"compose_form.publish": "Zveřejnit",
|
||||
"compose_form.publish_form": "Zveřejnit",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Uložit změny",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Označit média za citlivá} few {Označit média za citlivá} many {Označit média za citlivá} other {Označit média za citlivá}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {Média jsou označena za citlivá} few {Média jsou označena za citlivá} many {Média jsou označena za citlivá} other {Média jsou označena za citlivá}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Média nejsou označena za citlivá} few {Média nejsou označena za citlivá} many {Média nejsou označena za citlivá} other {Média nejsou označena za citlivá}}",
|
||||
"compose_form.spoiler.marked": "Odebrat varování o obsahu",
|
||||
"compose_form.spoiler.unmarked": "Přidat varování o obsahu",
|
||||
"compose_form.spoiler_placeholder": "Sem napište vaše varování",
|
||||
"confirmation_modal.cancel": "Zrušit",
|
||||
"confirmations.block.block_and_report": "Blokovat a nahlásit",
|
||||
"confirmations.block.confirm": "Blokovat",
|
||||
|
@ -401,7 +390,6 @@
|
|||
"navigation_bar.direct": "Soukromé zmínky",
|
||||
"navigation_bar.discover": "Objevit",
|
||||
"navigation_bar.domain_blocks": "Blokované domény",
|
||||
"navigation_bar.edit_profile": "Upravit profil",
|
||||
"navigation_bar.explore": "Prozkoumat",
|
||||
"navigation_bar.favourites": "Oblíbené",
|
||||
"navigation_bar.filters": "Skrytá slova",
|
||||
|
@ -508,14 +496,7 @@
|
|||
"poll_button.add_poll": "Přidat anketu",
|
||||
"poll_button.remove_poll": "Odebrat anketu",
|
||||
"privacy.change": "Změnit soukromí příspěvku",
|
||||
"privacy.direct.long": "Viditelný pouze pro zmíněné uživatele",
|
||||
"privacy.direct.short": "Pouze zmínění lidé",
|
||||
"privacy.private.long": "Viditelný pouze pro sledující",
|
||||
"privacy.private.short": "Pouze sledující",
|
||||
"privacy.public.long": "Viditelný pro všechny",
|
||||
"privacy.public.short": "Veřejné",
|
||||
"privacy.unlisted.long": "Viditelný pro všechny, ale vyňat z funkcí objevování",
|
||||
"privacy.unlisted.short": "Neveřejný",
|
||||
"privacy_policy.last_updated": "Naposledy aktualizováno {date}",
|
||||
"privacy_policy.title": "Zásady ochrany osobních údajů",
|
||||
"refresh": "Obnovit",
|
||||
|
@ -695,10 +676,8 @@
|
|||
"upload_error.poll": "Nahrávání souborů není povoleno s anketami.",
|
||||
"upload_form.audio_description": "Popis pro sluchově postižené",
|
||||
"upload_form.description": "Popis pro zrakově postižené",
|
||||
"upload_form.description_missing": "Nebyl přidán popis",
|
||||
"upload_form.edit": "Upravit",
|
||||
"upload_form.thumbnail": "Změnit miniaturu",
|
||||
"upload_form.undo": "Smazat",
|
||||
"upload_form.video_description": "Popis pro sluchově či zrakově postižené",
|
||||
"upload_modal.analyzing_picture": "Analyzuji obrázek…",
|
||||
"upload_modal.apply": "Použít",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Cyhoeddiad",
|
||||
"attachments_list.unprocessed": "(heb eu prosesu)",
|
||||
"audio.hide": "Cuddio sain",
|
||||
"autosuggest_hashtag.per_week": "{count} yr wythnos",
|
||||
"boost_modal.combo": "Mae modd pwyso {combo} er mwyn hepgor hyn tro nesa",
|
||||
"bundle_column_error.copy_stacktrace": "Copïo'r adroddiad gwall",
|
||||
"bundle_column_error.error.body": "Nid oedd modd cynhyrchu'r dudalen honno. Gall fod oherwydd gwall yn ein cod neu fater cydnawsedd porwr.",
|
||||
|
@ -146,22 +145,12 @@
|
|||
"compose_form.lock_disclaimer": "Nid yw eich cyfri wedi'i {locked}. Gall unrhyw un eich dilyn i weld eich postiadau dilynwyr-yn-unig.",
|
||||
"compose_form.lock_disclaimer.lock": "wedi ei gloi",
|
||||
"compose_form.placeholder": "Beth sydd ar eich meddwl?",
|
||||
"compose_form.poll.add_option": "Ychwanegu dewis",
|
||||
"compose_form.poll.duration": "Cyfnod pleidlais",
|
||||
"compose_form.poll.option_placeholder": "Dewis {number}",
|
||||
"compose_form.poll.remove_option": "Tynnu'r dewis",
|
||||
"compose_form.poll.switch_to_multiple": "Newid pleidlais i adael mwy nag un dewis",
|
||||
"compose_form.poll.switch_to_single": "Newid pleidlais i gyfyngu i un dewis",
|
||||
"compose_form.publish": "Cyhoeddi",
|
||||
"compose_form.publish_form": "Cyhoeddi",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Cadw newidiadau",
|
||||
"compose_form.sensitive.hide": "Marcio cyfryngau fel eu bod yn sensitif",
|
||||
"compose_form.sensitive.marked": "Cyfryngau wedi'u marcio'n sensitif",
|
||||
"compose_form.sensitive.unmarked": "Nid yw'r cyfryngau wedi'u marcio'n sensitif",
|
||||
"compose_form.spoiler.marked": "Dileu rhybudd cynnwys",
|
||||
"compose_form.spoiler.unmarked": "Ychwanegu rhybudd cynnwys",
|
||||
"compose_form.spoiler_placeholder": "Ysgrifenwch eich rhybudd yma",
|
||||
"confirmation_modal.cancel": "Diddymu",
|
||||
"confirmations.block.block_and_report": "Rhwystro ac Adrodd",
|
||||
"confirmations.block.confirm": "Blocio",
|
||||
|
@ -408,7 +397,6 @@
|
|||
"navigation_bar.direct": "Crybwylliadau preifat",
|
||||
"navigation_bar.discover": "Darganfod",
|
||||
"navigation_bar.domain_blocks": "Parthau wedi'u blocio",
|
||||
"navigation_bar.edit_profile": "Golygu proffil",
|
||||
"navigation_bar.explore": "Darganfod",
|
||||
"navigation_bar.favourites": "Ffefrynnau",
|
||||
"navigation_bar.filters": "Geiriau wedi'u tewi",
|
||||
|
@ -526,14 +514,7 @@
|
|||
"poll_button.add_poll": "Ychwanegu pleidlais",
|
||||
"poll_button.remove_poll": "Tynnu pleidlais",
|
||||
"privacy.change": "Addasu preifatrwdd y post",
|
||||
"privacy.direct.long": "Dim ond yn weladwy i ddefnyddwyr a grybwyllwyd",
|
||||
"privacy.direct.short": "Dim ond pobl sy wedi'u crybwyll",
|
||||
"privacy.private.long": "Dim ond pobl sy'n ddilynwyr",
|
||||
"privacy.private.short": "Dilynwyr yn unig",
|
||||
"privacy.public.long": "Gweladwy i bawb",
|
||||
"privacy.public.short": "Cyhoeddus",
|
||||
"privacy.unlisted.long": "Gweladwy i bawb, ond wedi optio allan o nodweddion darganfod",
|
||||
"privacy.unlisted.short": "Heb ei restru",
|
||||
"privacy_policy.last_updated": "Diweddarwyd ddiwethaf ar {date}",
|
||||
"privacy_policy.title": "Polisi Preifatrwydd",
|
||||
"recommended": "Argymhellwyd",
|
||||
|
@ -715,10 +696,8 @@
|
|||
"upload_error.poll": "Nid oes modd llwytho ffeiliau â phleidleisiau.",
|
||||
"upload_form.audio_description": "Disgrifio ar gyfer pobl sydd â cholled clyw",
|
||||
"upload_form.description": "Disgrifio i'r rheini a nam ar ei golwg",
|
||||
"upload_form.description_missing": "Dim disgrifiad wedi'i ychwanegu",
|
||||
"upload_form.edit": "Golygu",
|
||||
"upload_form.thumbnail": "Newid llun bach",
|
||||
"upload_form.undo": "Dileu",
|
||||
"upload_form.video_description": "Disgrifio ar gyfer pobl sydd â cholled clyw neu amhariad golwg",
|
||||
"upload_modal.analyzing_picture": "Yn dadansoddi llun…",
|
||||
"upload_modal.apply": "Gosod",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Bekendtgørelse",
|
||||
"attachments_list.unprocessed": "(ubehandlet)",
|
||||
"audio.hide": "Skjul lyd",
|
||||
"autosuggest_hashtag.per_week": "{count} pr. uge",
|
||||
"boost_modal.combo": "Du kan trykke {combo} for at springe dette over næste gang",
|
||||
"bundle_column_error.copy_stacktrace": "Kopiér fejlrapport",
|
||||
"bundle_column_error.error.body": "Den anmodede side kunne ikke gengives. Dette kan skyldes flere typer fejl.",
|
||||
|
@ -146,22 +145,22 @@
|
|||
"compose_form.lock_disclaimer": "Din konto er ikke {locked}. Enhver kan følge dig og se indlæg kun beregnet for følgere.",
|
||||
"compose_form.lock_disclaimer.lock": "låst",
|
||||
"compose_form.placeholder": "Hvad tænker du på?",
|
||||
"compose_form.poll.add_option": "Tilføj valgmulighed",
|
||||
"compose_form.poll.add_option": "Tilføj mulighed",
|
||||
"compose_form.poll.duration": "Afstemningens varighed",
|
||||
"compose_form.poll.multiple": "Multivalg",
|
||||
"compose_form.poll.option_placeholder": "Valgmulighed {number}",
|
||||
"compose_form.poll.remove_option": "Fjern denne valgmulighed",
|
||||
"compose_form.poll.single": "Vælg én",
|
||||
"compose_form.poll.switch_to_multiple": "Ændr afstemning til flervalgstype",
|
||||
"compose_form.poll.switch_to_single": "Ændr afstemning til enkeltvalgstype",
|
||||
"compose_form.publish": "Publicér",
|
||||
"compose_form.poll.type": "Stil",
|
||||
"compose_form.publish": "Indsend",
|
||||
"compose_form.publish_form": "Publicér",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Gem ændringer",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Markér medie som følsomt} other {Markér medier som følsomme}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {Medie er markeret som sensitivt} other {Medier er markerede som sensitive}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Medie er ikke market som sensitivt} other {Medier er ikke markerede som sensitive}}",
|
||||
"compose_form.reply": "Svar",
|
||||
"compose_form.save_changes": "Opdatér",
|
||||
"compose_form.spoiler.marked": "Fjern indholdsadvarsel",
|
||||
"compose_form.spoiler.unmarked": "Tilføj indholdsadvarsel",
|
||||
"compose_form.spoiler_placeholder": "Skriv din advarsel hér",
|
||||
"compose_form.spoiler_placeholder": "Indholdsadvarsel (valgfri)",
|
||||
"confirmation_modal.cancel": "Afbryd",
|
||||
"confirmations.block.block_and_report": "Blokér og Anmeld",
|
||||
"confirmations.block.confirm": "Blokér",
|
||||
|
@ -408,7 +407,6 @@
|
|||
"navigation_bar.direct": "Private omtaler",
|
||||
"navigation_bar.discover": "Opdag",
|
||||
"navigation_bar.domain_blocks": "Blokerede domæner",
|
||||
"navigation_bar.edit_profile": "Redigér profil",
|
||||
"navigation_bar.explore": "Udforsk",
|
||||
"navigation_bar.favourites": "Favoritter",
|
||||
"navigation_bar.filters": "Skjulte ord (mutede)",
|
||||
|
@ -526,14 +524,15 @@
|
|||
"poll_button.add_poll": "Tilføj en afstemning",
|
||||
"poll_button.remove_poll": "Fjern afstemning",
|
||||
"privacy.change": "Tilpas indlægsfortrolighed",
|
||||
"privacy.direct.long": "Kun synlig for nævnte brugere",
|
||||
"privacy.direct.short": "Kun omtalte personer",
|
||||
"privacy.private.long": "Kun synlig for følgere",
|
||||
"privacy.private.short": "Kun følgere",
|
||||
"privacy.public.long": "Synlig for alle",
|
||||
"privacy.direct.long": "Alle nævnt i indlægget",
|
||||
"privacy.direct.short": "Bestemte personer",
|
||||
"privacy.private.long": "Kun dine følgere",
|
||||
"privacy.private.short": "Følgere",
|
||||
"privacy.public.long": "Alle på og udenfor Mastodon",
|
||||
"privacy.public.short": "Offentlig",
|
||||
"privacy.unlisted.long": "Synlig for alle, men med fravalgt visning i opdagelsesfunktioner",
|
||||
"privacy.unlisted.short": "Diskret",
|
||||
"privacy.unlisted.additional": "Dette er præcis som offentlig adfærd, dog vises indlægget ikke i live feeds/hashtags, udforsk eller Mastodon-søgning, selv hvis valget gælder hele kontoen.",
|
||||
"privacy.unlisted.long": "Færre algoritmiske fanfarer",
|
||||
"privacy.unlisted.short": "Tavsgøre offentligt",
|
||||
"privacy_policy.last_updated": "Senest opdateret {date}",
|
||||
"privacy_policy.title": "Privatlivspolitik",
|
||||
"recommended": "Anbefalet",
|
||||
|
@ -551,7 +550,9 @@
|
|||
"relative_time.minutes": "{number}m",
|
||||
"relative_time.seconds": "{number}s",
|
||||
"relative_time.today": "i dag",
|
||||
"reply_indicator.attachments": "{count, plural, one {# vedhæftning} other {# vedhæftninger}}",
|
||||
"reply_indicator.cancel": "Afbryd",
|
||||
"reply_indicator.poll": "Afstemning",
|
||||
"report.block": "Blokér",
|
||||
"report.block_explanation": "Du vil ikke se vedkommendes indlæg. Vedkommende vil ikke kunne se dine indlæg eller følge dig. Vedkommende vil kunne se, at de er blokeret.",
|
||||
"report.categories.legal": "Juridisk",
|
||||
|
@ -715,10 +716,8 @@
|
|||
"upload_error.poll": "Filupload ikke tilladt for afstemninger.",
|
||||
"upload_form.audio_description": "Beskrivelse til hørehæmmede",
|
||||
"upload_form.description": "Beskrivelse til svagtseende",
|
||||
"upload_form.description_missing": "Ingen beskrivelse tilføjet",
|
||||
"upload_form.edit": "Redigér",
|
||||
"upload_form.thumbnail": "Skift miniature",
|
||||
"upload_form.undo": "Slet",
|
||||
"upload_form.video_description": "Beskrivelse for hørehæmmede eller synshandicappede personer",
|
||||
"upload_modal.analyzing_picture": "Analyserer billede…",
|
||||
"upload_modal.apply": "Anvend",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Ankündigung",
|
||||
"attachments_list.unprocessed": "(ausstehend)",
|
||||
"audio.hide": "Audio ausblenden",
|
||||
"autosuggest_hashtag.per_week": "{count} pro Woche",
|
||||
"boost_modal.combo": "Mit {combo} wird dieses Fenster beim nächsten Mal nicht mehr angezeigt",
|
||||
"bundle_column_error.copy_stacktrace": "Fehlerbericht kopieren",
|
||||
"bundle_column_error.error.body": "Die angeforderte Seite konnte nicht dargestellt werden. Dies könnte auf einen Fehler in unserem Code oder auf ein Browser-Kompatibilitätsproblem zurückzuführen sein.",
|
||||
|
@ -146,22 +145,22 @@
|
|||
"compose_form.lock_disclaimer": "Dein Profil ist nicht {locked}. Andere können dir folgen und deine Beiträge sehen, die nur für Follower bestimmt sind.",
|
||||
"compose_form.lock_disclaimer.lock": "geschützt",
|
||||
"compose_form.placeholder": "Was gibt’s Neues?",
|
||||
"compose_form.poll.add_option": "Auswahl",
|
||||
"compose_form.poll.add_option": "Auswahl hinzufügen",
|
||||
"compose_form.poll.duration": "Umfragedauer",
|
||||
"compose_form.poll.option_placeholder": "{number}. Auswahl",
|
||||
"compose_form.poll.remove_option": "Auswahlfeld entfernen",
|
||||
"compose_form.poll.multiple": "Mehrfachauswahl",
|
||||
"compose_form.poll.option_placeholder": "{number}. Auswahlmöglichkeit",
|
||||
"compose_form.poll.remove_option": "Dieses Auswahlfeld entfernen",
|
||||
"compose_form.poll.single": "Einfachauswahl",
|
||||
"compose_form.poll.switch_to_multiple": "Mehrfachauswahl erlauben",
|
||||
"compose_form.poll.switch_to_single": "Nur Einzelauswahl erlauben",
|
||||
"compose_form.poll.switch_to_single": "Nur Einfachauswahl erlauben",
|
||||
"compose_form.poll.type": "Art",
|
||||
"compose_form.publish": "Veröffentlichen",
|
||||
"compose_form.publish_form": "Neuer Beitrag",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Änderungen speichern",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Mit einer Inhaltswarnung versehen} other {Mit einer Inhaltswarnung versehen}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {Medien-Datei ist mit einer Inhaltswarnung versehen} other {Medien-Dateien sind mit einer Inhaltswarnung versehen}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Medien-Datei ist nicht mit einer Inhaltswarnung versehen} other {Medien-Dateien sind nicht mit einer Inhaltswarnung versehen}}",
|
||||
"compose_form.reply": "Antworten",
|
||||
"compose_form.save_changes": "Aktualisieren",
|
||||
"compose_form.spoiler.marked": "Inhaltswarnung entfernen",
|
||||
"compose_form.spoiler.unmarked": "Inhaltswarnung hinzufügen",
|
||||
"compose_form.spoiler_placeholder": "Inhaltswarnung",
|
||||
"compose_form.spoiler_placeholder": "Inhaltswarnung (optional)",
|
||||
"confirmation_modal.cancel": "Abbrechen",
|
||||
"confirmations.block.block_and_report": "Blockieren und melden",
|
||||
"confirmations.block.confirm": "Blockieren",
|
||||
|
@ -408,7 +407,6 @@
|
|||
"navigation_bar.direct": "Private Erwähnungen",
|
||||
"navigation_bar.discover": "Entdecken",
|
||||
"navigation_bar.domain_blocks": "Blockierte Domains",
|
||||
"navigation_bar.edit_profile": "Profil bearbeiten",
|
||||
"navigation_bar.explore": "Entdecken",
|
||||
"navigation_bar.favourites": "Favoriten",
|
||||
"navigation_bar.filters": "Stummgeschaltete Wörter",
|
||||
|
@ -526,14 +524,15 @@
|
|||
"poll_button.add_poll": "Umfrage erstellen",
|
||||
"poll_button.remove_poll": "Umfrage entfernen",
|
||||
"privacy.change": "Sichtbarkeit anpassen",
|
||||
"privacy.direct.long": "Nur für die erwähnten Profile sichtbar",
|
||||
"privacy.direct.short": "Nur erwähnte Profile",
|
||||
"privacy.private.long": "Nur für deine Follower sichtbar",
|
||||
"privacy.private.short": "Nur Follower",
|
||||
"privacy.public.long": "Für alle sichtbar",
|
||||
"privacy.direct.long": "Alle in diesem Beitrag erwähnten Profile",
|
||||
"privacy.direct.short": "Bestimmte Profile",
|
||||
"privacy.private.long": "Nur deine Follower",
|
||||
"privacy.private.short": "Follower",
|
||||
"privacy.public.long": "Alle auf und außerhalb von Mastodon",
|
||||
"privacy.public.short": "Öffentlich",
|
||||
"privacy.unlisted.long": "Für alle sichtbar, aber nicht über die Suche zu finden",
|
||||
"privacy.unlisted.short": "Nicht gelistet",
|
||||
"privacy.unlisted.additional": "Das Verhalten ist wie bei „Öffentlich“, jedoch erscheint dieser Beitrag nicht in „Live-Feeds“, „Erkunden“, Hashtags oder über die Mastodon-Suchfunktion – selbst wenn du das in den Einstellungen aktiviert hast.",
|
||||
"privacy.unlisted.long": "Weniger im Algorithmus berücksichtigt",
|
||||
"privacy.unlisted.short": "Öffentlich (eingeschränkt)",
|
||||
"privacy_policy.last_updated": "Stand: {date}",
|
||||
"privacy_policy.title": "Datenschutzerklärung",
|
||||
"recommended": "Empfohlen",
|
||||
|
@ -551,7 +550,9 @@
|
|||
"relative_time.minutes": "{number} Min.",
|
||||
"relative_time.seconds": "{number} Sek.",
|
||||
"relative_time.today": "heute",
|
||||
"reply_indicator.attachments": "{count, plural, one {# Anhang} other {# Anhänge}}",
|
||||
"reply_indicator.cancel": "Abbrechen",
|
||||
"reply_indicator.poll": "Umfrage",
|
||||
"report.block": "Blockieren",
|
||||
"report.block_explanation": "Du wirst keine Beiträge mehr von diesem Konto sehen. Das blockierte Konto wird deine Beiträge nicht mehr sehen oder dir folgen können. Die Person könnte mitbekommen, dass du sie blockiert hast.",
|
||||
"report.categories.legal": "Rechtlich",
|
||||
|
@ -715,10 +716,8 @@
|
|||
"upload_error.poll": "Medien-Anhänge sind zusammen mit Umfragen nicht erlaubt.",
|
||||
"upload_form.audio_description": "Beschreibe für Menschen mit Hörbehinderung",
|
||||
"upload_form.description": "Beschreibe für Menschen mit Sehbehinderung",
|
||||
"upload_form.description_missing": "Keine Beschreibung hinzugefügt",
|
||||
"upload_form.edit": "Bearbeiten",
|
||||
"upload_form.thumbnail": "Vorschaubild ändern",
|
||||
"upload_form.undo": "Löschen",
|
||||
"upload_form.video_description": "Beschreibe für Menschen mit einer Hör- oder Sehbehinderung",
|
||||
"upload_modal.analyzing_picture": "Bild wird analysiert …",
|
||||
"upload_modal.apply": "Übernehmen",
|
||||
|
|
|
@ -85,7 +85,6 @@
|
|||
"announcement.announcement": "Ανακοίνωση",
|
||||
"attachments_list.unprocessed": "(μη επεξεργασμένο)",
|
||||
"audio.hide": "Απόκρυψη αρχείου ήχου",
|
||||
"autosuggest_hashtag.per_week": "{count} ανά εβδομάδα",
|
||||
"boost_modal.combo": "Μπορείς να πατήσεις {combo} για να το προσπεράσεις την επόμενη φορά",
|
||||
"bundle_column_error.copy_stacktrace": "Αντιγραφή αναφοράς σφάλματος",
|
||||
"bundle_column_error.error.body": "Δεν ήταν δυνατή η απόδοση της σελίδας που ζήτησες. Μπορεί να οφείλεται σε σφάλμα στον κώδικά μας ή σε πρόβλημα συμβατότητας του προγράμματος περιήγησης.",
|
||||
|
@ -111,6 +110,7 @@
|
|||
"column.direct": "Ιδιωτικές αναφορές",
|
||||
"column.directory": "Περιήγηση στα προφίλ",
|
||||
"column.domain_blocks": "Αποκλεισμένοι τομείς",
|
||||
"column.favourites": "Αγαπημένα",
|
||||
"column.follow_requests": "Αιτήματα ακολούθησης",
|
||||
"column.home": "Αρχική",
|
||||
"column.lists": "Λίστες",
|
||||
|
@ -131,6 +131,9 @@
|
|||
"community.column_settings.remote_only": "Απομακρυσμένα μόνο",
|
||||
"compose.language.change": "Αλλαγή γλώσσας",
|
||||
"compose.language.search": "Αναζήτηση γλωσσών...",
|
||||
"compose.published.body": "Η ανάρτηση δημοσιεύτηκε.",
|
||||
"compose.published.open": "Άνοιγμα",
|
||||
"compose.saved.body": "Η ανάρτηση αποθηκεύτηκε.",
|
||||
"compose_form.direct_message_warning_learn_more": "Μάθε περισσότερα",
|
||||
"compose_form.encryption_warning": "Οι δημοσιεύσεις στο Mastodon δεν είναι κρυπτογραφημένες από άκρο σε άκρο. Μη μοιράζεσαι ευαίσθητες πληροφορίες μέσω του Mastodon.",
|
||||
"compose_form.hashtag_warning": "Αυτή η δημοσίευση δεν θα εμφανίζεται κάτω από οποιαδήποτε ετικέτα καθώς δεν είναι δημόσια. Μόνο οι δημόσιες δημοσιεύσεις μπορούν να αναζητηθούν με ετικέτα.",
|
||||
|
@ -139,20 +142,19 @@
|
|||
"compose_form.placeholder": "Τι σκέφτεσαι;",
|
||||
"compose_form.poll.add_option": "Προσθήκη επιλογής",
|
||||
"compose_form.poll.duration": "Διάρκεια δημοσκόπησης",
|
||||
"compose_form.poll.multiple": "Πολλαπλή επιλογή",
|
||||
"compose_form.poll.option_placeholder": "Επιλογή {number}",
|
||||
"compose_form.poll.remove_option": "Αφαίρεση επιλογής",
|
||||
"compose_form.poll.switch_to_multiple": "Ενημέρωση δημοσκόπησης με πολλαπλές επιλογές",
|
||||
"compose_form.poll.switch_to_single": "Ενημέρωση δημοσκόπησης με μοναδική επιλογή",
|
||||
"compose_form.publish": "Δημοσίευση",
|
||||
"compose_form.poll.type": "Στυλ",
|
||||
"compose_form.publish": "Ανάρτηση",
|
||||
"compose_form.publish_form": "Δημοσίευση",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Αποθήκευση αλλαγών",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Επισήμανση πολυμέσου ως ευαίσθητο} other {Επισήμανση πολυμέσων ως ευαίσθητα}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {Το πολυμέσο έχει σημειωθεί ως ευαίσθητο} other {Τα πολυμέσα έχουν σημειωθεί ως ευαίσθητα}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Το πολυμέσο δεν έχει σημειωθεί ως ευαίσθητο} other {Τα πολυμέσα δεν έχουν σημειωθεί ως ευαίσθητα}}",
|
||||
"compose_form.reply": "Απάντηση",
|
||||
"compose_form.save_changes": "Ενημέρωση",
|
||||
"compose_form.spoiler.marked": "Αφαίρεση προειδοποίηση περιεχομένου",
|
||||
"compose_form.spoiler.unmarked": "Προσθήκη προειδοποίησης περιεχομένου",
|
||||
"compose_form.spoiler_placeholder": "Γράψε την προειδοποίησή σου εδώ",
|
||||
"compose_form.spoiler_placeholder": "Προειδοποίηση περιεχομένου (προαιρετική)",
|
||||
"confirmation_modal.cancel": "Άκυρο",
|
||||
"confirmations.block.block_and_report": "Αποκλεισμός & Αναφορά",
|
||||
"confirmations.block.confirm": "Αποκλεισμός",
|
||||
|
@ -184,6 +186,7 @@
|
|||
"conversation.mark_as_read": "Σήμανση ως αναγνωσμένο",
|
||||
"conversation.open": "Προβολή συνομιλίας",
|
||||
"conversation.with": "Με {names}",
|
||||
"copy_icon_button.copied": "Αντιγράφηκε στο πρόχειρο",
|
||||
"copypaste.copied": "Αντιγράφηκε",
|
||||
"copypaste.copy_to_clipboard": "Αντιγραφή στο πρόχειρο",
|
||||
"directory.federated": "Από το γνωστό fediverse",
|
||||
|
@ -380,7 +383,6 @@
|
|||
"navigation_bar.direct": "Ιδιωτικές επισημάνσεις",
|
||||
"navigation_bar.discover": "Ανακάλυψη",
|
||||
"navigation_bar.domain_blocks": "Αποκλεισμένοι τομείς",
|
||||
"navigation_bar.edit_profile": "Επεξεργασία προφίλ",
|
||||
"navigation_bar.explore": "Εξερεύνηση",
|
||||
"navigation_bar.filters": "Αποσιωπημένες λέξεις",
|
||||
"navigation_bar.follow_requests": "Αιτήματα ακολούθησης",
|
||||
|
@ -450,6 +452,12 @@
|
|||
"onboarding.actions.go_to_home": "Πηγαίνετε στην αρχική σας ροή",
|
||||
"onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!",
|
||||
"onboarding.follows.title": "Δημοφιλή στο Mastodon",
|
||||
"onboarding.profile.note": "Βιογραφικό",
|
||||
"onboarding.profile.note_hint": "Μπορείτε να @αναφέρετε άλλα άτομα ή #hashtags…",
|
||||
"onboarding.profile.save_and_continue": "Αποθήκευση και συνέχεια",
|
||||
"onboarding.profile.title": "Ρύθμιση προφίλ",
|
||||
"onboarding.profile.upload_avatar": "Μεταφόρτωση εικόνας προφίλ",
|
||||
"onboarding.profile.upload_header": "Μεταφόρτωση κεφαλίδας προφίλ",
|
||||
"onboarding.share.lead": "Let people know how they can find you on Mastodon!\nΕνημερώστε άλλα άτομα πώς μπορούν να σας βρουν στο Mastodon!",
|
||||
"onboarding.share.next_steps": "Πιθανά επόμενα βήματα:",
|
||||
"onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:",
|
||||
|
@ -458,6 +466,7 @@
|
|||
"onboarding.steps.follow_people.body": "You curate your own feed. Lets fill it with interesting people.",
|
||||
"onboarding.steps.follow_people.title": "Follow {count, plural, one {one person} other {# people}}",
|
||||
"onboarding.steps.publish_status.body": "Say hello to the world.",
|
||||
"onboarding.steps.publish_status.title": "Κάντε την πρώτη σας δημοσίευση",
|
||||
"onboarding.steps.setup_profile.body": "Others are more likely to interact with you with a filled out profile.",
|
||||
"onboarding.steps.setup_profile.title": "Customize your profile",
|
||||
"onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
|
||||
|
@ -467,6 +476,7 @@
|
|||
"picture_in_picture.restore": "Βάλε το πίσω",
|
||||
"poll.closed": "Κλειστή",
|
||||
"poll.refresh": "Ανανέωση",
|
||||
"poll.reveal": "Δείτε τα αποτελέσματα",
|
||||
"poll.total_people": "{count, plural, one {# άτομο} other {# άτομα}}",
|
||||
"poll.total_votes": "{count, plural, one {# ψήφος} other {# ψήφοι}}",
|
||||
"poll.vote": "Ψήφισε",
|
||||
|
@ -475,16 +485,14 @@
|
|||
"poll_button.add_poll": "Προσθήκη δημοσκόπησης",
|
||||
"poll_button.remove_poll": "Αφαίρεση δημοσκόπησης",
|
||||
"privacy.change": "Προσαρμογή ιδιωτικότητας ανάρτησης",
|
||||
"privacy.direct.long": "Δημοσίευση μόνο σε όσους επισημαίνονται",
|
||||
"privacy.direct.short": "Αναφερόμενα άτομα μόνο",
|
||||
"privacy.private.long": "Ορατό μόνο για τους ακολούθους",
|
||||
"privacy.private.short": "Μόνο ακόλουθοι",
|
||||
"privacy.public.long": "Ορατό σε όλους",
|
||||
"privacy.direct.long": "Όλοι όσοι αναφέρθηκαν στη δημοσίευση",
|
||||
"privacy.direct.short": "Συγκεκριμένα άτομα",
|
||||
"privacy.private.long": "Μόνο οι ακόλουθοί σας",
|
||||
"privacy.private.short": "Ακόλουθοι",
|
||||
"privacy.public.short": "Δημόσιο",
|
||||
"privacy.unlisted.long": "Ορατό για όλους, εκτός αυτών που δεν συμμετέχουν σε δυνατότητες ανακάλυψης",
|
||||
"privacy.unlisted.short": "Μη καταχωρημένα",
|
||||
"privacy_policy.last_updated": "Τελευταία ενημέρωση {date}",
|
||||
"privacy_policy.title": "Πολιτική Απορρήτου",
|
||||
"recommended": "Προτεινόμενα",
|
||||
"refresh": "Ανανέωση",
|
||||
"regeneration_indicator.label": "Φορτώνει…",
|
||||
"regeneration_indicator.sublabel": "Η αρχική σου ροή ετοιμάζεται!",
|
||||
|
@ -500,8 +508,10 @@
|
|||
"relative_time.seconds": "{number}δ",
|
||||
"relative_time.today": "σήμερα",
|
||||
"reply_indicator.cancel": "Άκυρο",
|
||||
"reply_indicator.poll": "Δημοσκόπηση",
|
||||
"report.block": "Αποκλεισμός",
|
||||
"report.block_explanation": "Δεν θα βλέπεις τις αναρτήσεις του. Δεν θα μπορεί να δει τις αναρτήσεις σου ή να σε ακολουθήσει. Θα μπορεί να δει ότι έχει αποκλειστεί.",
|
||||
"report.categories.legal": "Νομικό περιεχόμενο",
|
||||
"report.categories.other": "Άλλες",
|
||||
"report.categories.spam": "Ανεπιθύμητα",
|
||||
"report.categories.violation": "Το περιεχόμενο παραβιάζει έναν ή περισσότερους κανόνες διακομιστή",
|
||||
|
@ -519,6 +529,8 @@
|
|||
"report.placeholder": "Επιπλέον σχόλια",
|
||||
"report.reasons.dislike": "Δεν μου αρέσει",
|
||||
"report.reasons.dislike_description": "Δεν είναι κάτι που θα ήθελες να δεις",
|
||||
"report.reasons.legal": "Είναι παράνομο",
|
||||
"report.reasons.legal_description": "Πιστεύετε ότι παραβιάζει το νόμο της χώρας σας ή της χώρας του διακομιστή",
|
||||
"report.reasons.other": "Είναι κάτι άλλο",
|
||||
"report.reasons.other_description": "Το ζήτημα δεν ταιριάζει σε άλλες κατηγορίες",
|
||||
"report.reasons.spam": "Είναι σπαμ",
|
||||
|
@ -552,10 +564,12 @@
|
|||
"search.search_or_paste": "Αναζήτηση ή εισαγωγή URL",
|
||||
"search_popout.quick_actions": "Γρήγορες ενέργειες",
|
||||
"search_popout.recent": "Πρόσφατες αναζητήσεις",
|
||||
"search_popout.user": "χρήστης",
|
||||
"search_results.accounts": "Προφίλ",
|
||||
"search_results.all": "Όλα",
|
||||
"search_results.hashtags": "Ετικέτες",
|
||||
"search_results.nothing_found": "Δεν βρέθηκε τίποτα με αυτούς τους όρους αναζήτησης",
|
||||
"search_results.see_all": "Δες τα όλα",
|
||||
"search_results.statuses": "Αναρτήσεις",
|
||||
"search_results.title": "Αναζήτηση για {q}",
|
||||
"server_banner.about_active_users": "Άτομα που χρησιμοποιούν αυτόν τον διακομιστή κατά τις τελευταίες 30 ημέρες (Μηνιαία Ενεργοί Χρήστες)",
|
||||
|
@ -566,6 +580,8 @@
|
|||
"server_banner.server_stats": "Στατιστικά διακομιστή:",
|
||||
"sign_in_banner.create_account": "Δημιουργία λογαριασμού",
|
||||
"sign_in_banner.sign_in": "Σύνδεση",
|
||||
"sign_in_banner.sso_redirect": "Συνδεθείτε ή Εγγραφείτε",
|
||||
"sign_in_banner.text": "Συνδεθείτε για να ακολουθήσετε προφίλ ή ετικέτες, αγαπήστε, μοιραστείτε και απαντήστε σε δημοσιεύσεις. Μπορείτε επίσης να αλληλεπιδράσετε από τον λογαριασμό σας σε διαφορετικό διακομιστή.",
|
||||
"status.admin_account": "Άνοιγμα διεπαφής συντονισμού για τον/την @{name}",
|
||||
"status.admin_domain": "Άνοιγμα λειτουργίας διαμεσολάβησης για {domain}",
|
||||
"status.admin_status": "Άνοιγμα αυτής της ανάρτησης σε διεπαφή συντονισμού",
|
||||
|
@ -582,6 +598,7 @@
|
|||
"status.edited": "Επεξεργάστηκε στις {date}",
|
||||
"status.edited_x_times": "Επεξεργάστηκε {count, plural, one {{count} φορά} other {{count} φορές}}",
|
||||
"status.embed": "Ενσωμάτωσε",
|
||||
"status.favourite": "Αγαπημένα",
|
||||
"status.filter": "Φιλτράρισμα αυτής της ανάρτησης",
|
||||
"status.filtered": "Φιλτραρισμένα",
|
||||
"status.hide": "Απόκρυψη ανάρτησης",
|
||||
|
@ -646,10 +663,8 @@
|
|||
"upload_error.poll": "Στις δημοσκοπήσεις δεν επιτρέπεται η μεταφόρτωση αρχείου.",
|
||||
"upload_form.audio_description": "Περιγραφή για άτομα με προβλήματα ακοής",
|
||||
"upload_form.description": "Περιγραφή για άτομα με προβλήματα όρασης",
|
||||
"upload_form.description_missing": "Δεν προστέθηκε περιγραφή",
|
||||
"upload_form.edit": "Επεξεργασία",
|
||||
"upload_form.thumbnail": "Αλλαγή μικρογραφίας",
|
||||
"upload_form.undo": "Διαγραφή",
|
||||
"upload_form.video_description": "Περιγραφή για άτομα με προβλήματα ακοής ή όρασης",
|
||||
"upload_modal.analyzing_picture": "Ανάλυση εικόνας…",
|
||||
"upload_modal.apply": "Εφαρμογή",
|
||||
|
|
|
@ -87,7 +87,6 @@
|
|||
"announcement.announcement": "Announcement",
|
||||
"attachments_list.unprocessed": "(unprocessed)",
|
||||
"audio.hide": "Hide audio",
|
||||
"autosuggest_hashtag.per_week": "{count} per week",
|
||||
"boost_modal.combo": "You can press {combo} to skip this next time",
|
||||
"bundle_column_error.copy_stacktrace": "Copy error report",
|
||||
"bundle_column_error.error.body": "The requested page could not be rendered. It could be due to a bug in our code, or a browser compatibility issue.",
|
||||
|
@ -144,22 +143,12 @@
|
|||
"compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
|
||||
"compose_form.lock_disclaimer.lock": "locked",
|
||||
"compose_form.placeholder": "What's on your mind?",
|
||||
"compose_form.poll.add_option": "Add a choice",
|
||||
"compose_form.poll.duration": "Poll duration",
|
||||
"compose_form.poll.option_placeholder": "Choice {number}",
|
||||
"compose_form.poll.remove_option": "Remove this choice",
|
||||
"compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
|
||||
"compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
|
||||
"compose_form.publish": "Publish",
|
||||
"compose_form.publish_form": "New post",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Save changes",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Mark media as sensitive} other {Mark media as sensitive}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {Media is marked as sensitive} other {Media is marked as sensitive}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Media is not marked as sensitive} other {Media is not marked as sensitive}}",
|
||||
"compose_form.spoiler.marked": "Remove content warning",
|
||||
"compose_form.spoiler.unmarked": "Add content warning",
|
||||
"compose_form.spoiler_placeholder": "Write your warning here",
|
||||
"confirmation_modal.cancel": "Cancel",
|
||||
"confirmations.block.block_and_report": "Block & Report",
|
||||
"confirmations.block.confirm": "Block",
|
||||
|
@ -406,7 +395,6 @@
|
|||
"navigation_bar.direct": "Private mentions",
|
||||
"navigation_bar.discover": "Discover",
|
||||
"navigation_bar.domain_blocks": "Blocked domains",
|
||||
"navigation_bar.edit_profile": "Edit profile",
|
||||
"navigation_bar.explore": "Explore",
|
||||
"navigation_bar.favourites": "Favourites",
|
||||
"navigation_bar.filters": "Muted words",
|
||||
|
@ -524,14 +512,7 @@
|
|||
"poll_button.add_poll": "Add a poll",
|
||||
"poll_button.remove_poll": "Remove poll",
|
||||
"privacy.change": "Change post privacy",
|
||||
"privacy.direct.long": "Visible for mentioned users only",
|
||||
"privacy.direct.short": "Mentioned people only",
|
||||
"privacy.private.long": "Visible for followers only",
|
||||
"privacy.private.short": "Followers-only",
|
||||
"privacy.public.long": "Visible for all",
|
||||
"privacy.public.short": "Public",
|
||||
"privacy.unlisted.long": "Visible for all, but opted-out of discovery features",
|
||||
"privacy.unlisted.short": "Unlisted",
|
||||
"privacy_policy.last_updated": "Last updated {date}",
|
||||
"privacy_policy.title": "Privacy Policy",
|
||||
"recommended": "Recommended",
|
||||
|
@ -713,10 +694,8 @@
|
|||
"upload_error.poll": "File upload not allowed with polls.",
|
||||
"upload_form.audio_description": "Describe for people who are deaf or hard of hearing",
|
||||
"upload_form.description": "Describe for people who are blind or have low vision",
|
||||
"upload_form.description_missing": "No description added",
|
||||
"upload_form.edit": "Edit",
|
||||
"upload_form.thumbnail": "Change thumbnail",
|
||||
"upload_form.undo": "Delete",
|
||||
"upload_form.video_description": "Describe for people who are deaf, hard of hearing, blind or have low vision",
|
||||
"upload_modal.analyzing_picture": "Analysing picture…",
|
||||
"upload_modal.apply": "Apply",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Announcement",
|
||||
"attachments_list.unprocessed": "(unprocessed)",
|
||||
"audio.hide": "Hide audio",
|
||||
"autosuggest_hashtag.per_week": "{count} per week",
|
||||
"boost_modal.combo": "You can press {combo} to skip this next time",
|
||||
"bundle_column_error.copy_stacktrace": "Copy error report",
|
||||
"bundle_column_error.error.body": "The requested page could not be rendered. It could be due to a bug in our code, or a browser compatibility issue.",
|
||||
|
@ -146,22 +145,20 @@
|
|||
"compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
|
||||
"compose_form.lock_disclaimer.lock": "locked",
|
||||
"compose_form.placeholder": "What's on your mind?",
|
||||
"compose_form.poll.add_option": "Add a choice",
|
||||
"compose_form.poll.duration": "Poll duration",
|
||||
"compose_form.poll.option_placeholder": "Choice {number}",
|
||||
"compose_form.poll.remove_option": "Remove this choice",
|
||||
"compose_form.poll.multiple": "Multiple choice",
|
||||
"compose_form.poll.option_placeholder": "Option {number}",
|
||||
"compose_form.poll.single": "Pick one",
|
||||
"compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
|
||||
"compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
|
||||
"compose_form.publish": "Publish",
|
||||
"compose_form.poll.type": "Style",
|
||||
"compose_form.publish": "Post",
|
||||
"compose_form.publish_form": "New post",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Save changes",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Mark media as sensitive} other {Mark media as sensitive}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {Media is marked as sensitive} other {Media is marked as sensitive}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Media is not marked as sensitive} other {Media is not marked as sensitive}}",
|
||||
"compose_form.reply": "Reply",
|
||||
"compose_form.save_changes": "Update",
|
||||
"compose_form.spoiler.marked": "Remove content warning",
|
||||
"compose_form.spoiler.unmarked": "Add content warning",
|
||||
"compose_form.spoiler_placeholder": "Write your warning here",
|
||||
"compose_form.spoiler_placeholder": "Content warning (optional)",
|
||||
"confirmation_modal.cancel": "Cancel",
|
||||
"confirmations.block.block_and_report": "Block & Report",
|
||||
"confirmations.block.confirm": "Block",
|
||||
|
@ -408,7 +405,6 @@
|
|||
"navigation_bar.direct": "Private mentions",
|
||||
"navigation_bar.discover": "Discover",
|
||||
"navigation_bar.domain_blocks": "Blocked domains",
|
||||
"navigation_bar.edit_profile": "Edit profile",
|
||||
"navigation_bar.explore": "Explore",
|
||||
"navigation_bar.favourites": "Favorites",
|
||||
"navigation_bar.filters": "Muted words",
|
||||
|
@ -526,14 +522,15 @@
|
|||
"poll_button.add_poll": "Add a poll",
|
||||
"poll_button.remove_poll": "Remove poll",
|
||||
"privacy.change": "Change post privacy",
|
||||
"privacy.direct.long": "Visible for mentioned users only",
|
||||
"privacy.direct.short": "Mentioned people only",
|
||||
"privacy.private.long": "Visible for followers only",
|
||||
"privacy.private.short": "Followers only",
|
||||
"privacy.public.long": "Visible for all",
|
||||
"privacy.direct.long": "Everyone mentioned in the post",
|
||||
"privacy.direct.short": "Specific people",
|
||||
"privacy.private.long": "Only your followers",
|
||||
"privacy.private.short": "Followers",
|
||||
"privacy.public.long": "Anyone on and off Mastodon",
|
||||
"privacy.public.short": "Public",
|
||||
"privacy.unlisted.long": "Visible for all, but opted-out of discovery features",
|
||||
"privacy.unlisted.short": "Unlisted",
|
||||
"privacy.unlisted.additional": "This behaves exactly like public, except the post will not appear in live feeds or hashtags, explore, or Mastodon search, even if you are opted-in account-wide.",
|
||||
"privacy.unlisted.long": "Fewer algorithmic fanfares",
|
||||
"privacy.unlisted.short": "Quiet public",
|
||||
"privacy_policy.last_updated": "Last updated {date}",
|
||||
"privacy_policy.title": "Privacy Policy",
|
||||
"recommended": "Recommended",
|
||||
|
@ -551,7 +548,9 @@
|
|||
"relative_time.minutes": "{number}m",
|
||||
"relative_time.seconds": "{number}s",
|
||||
"relative_time.today": "today",
|
||||
"reply_indicator.attachments": "{count, plural, one {# attachment} other {# attachments}}",
|
||||
"reply_indicator.cancel": "Cancel",
|
||||
"reply_indicator.poll": "Poll",
|
||||
"report.block": "Block",
|
||||
"report.block_explanation": "You will not see their posts. They will not be able to see your posts or follow you. They will be able to tell that they are blocked.",
|
||||
"report.categories.legal": "Legal",
|
||||
|
@ -715,10 +714,8 @@
|
|||
"upload_error.poll": "File upload not allowed with polls.",
|
||||
"upload_form.audio_description": "Describe for people who are deaf or hard of hearing",
|
||||
"upload_form.description": "Describe for people who are blind or have low vision",
|
||||
"upload_form.description_missing": "No description added",
|
||||
"upload_form.edit": "Edit",
|
||||
"upload_form.thumbnail": "Change thumbnail",
|
||||
"upload_form.undo": "Delete",
|
||||
"upload_form.video_description": "Describe for people who are deaf, hard of hearing, blind or have low vision",
|
||||
"upload_modal.analyzing_picture": "Analyzing picture…",
|
||||
"upload_modal.apply": "Apply",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Anoncoj",
|
||||
"attachments_list.unprocessed": "(neprilaborita)",
|
||||
"audio.hide": "Kaŝi aŭdion",
|
||||
"autosuggest_hashtag.per_week": "po {count} por semajno",
|
||||
"boost_modal.combo": "Vi povas premi {combo} por preterpasi sekvafoje",
|
||||
"bundle_column_error.copy_stacktrace": "Kopii la eraran raporton",
|
||||
"bundle_column_error.error.body": "La petita paĝo ne povas redonitis. Eble estas eraro.",
|
||||
|
@ -146,22 +145,12 @@
|
|||
"compose_form.lock_disclaimer": "Via konto ne estas {locked}. Iu ajn povas sekvi vin por vidi viajn afiŝojn nur al la sekvantoj.",
|
||||
"compose_form.lock_disclaimer.lock": "ŝlosita",
|
||||
"compose_form.placeholder": "Kion vi pensas?",
|
||||
"compose_form.poll.add_option": "Aldoni elekteblon",
|
||||
"compose_form.poll.duration": "Daŭro de la balotenketo",
|
||||
"compose_form.poll.option_placeholder": "Elekteblo {number}",
|
||||
"compose_form.poll.remove_option": "Forigi ĉi tiu elekteblon",
|
||||
"compose_form.poll.switch_to_multiple": "Ŝanĝi la balotenketon por permesi multajn elektojn",
|
||||
"compose_form.poll.switch_to_single": "Ŝanĝi la balotenketon por permesi unu solan elekton",
|
||||
"compose_form.publish": "Afiŝi",
|
||||
"compose_form.publish_form": "Afiŝi",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Konservi ŝanĝojn",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Marki la plurmedio kiel tikla} other {Marki la plurmedioj kiel tiklaj}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {La plurmedio estas markita kiel tikla} other {La plurmedioj estas markitaj kiel tiklaj}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {La plurmedio ne estas markita kiel tikla} other {La plurmedioj ne estas markitaj kiel tiklaj}}",
|
||||
"compose_form.spoiler.marked": "Forigi la averton de enhavo",
|
||||
"compose_form.spoiler.unmarked": "Aldoni averton de enhavo",
|
||||
"compose_form.spoiler_placeholder": "Skribu vian averton ĉi tie",
|
||||
"confirmation_modal.cancel": "Nuligi",
|
||||
"confirmations.block.block_and_report": "Bloki kaj raporti",
|
||||
"confirmations.block.confirm": "Bloki",
|
||||
|
@ -408,7 +397,6 @@
|
|||
"navigation_bar.direct": "Privataj mencioj",
|
||||
"navigation_bar.discover": "Esplori",
|
||||
"navigation_bar.domain_blocks": "Blokitaj domajnoj",
|
||||
"navigation_bar.edit_profile": "Redakti profilon",
|
||||
"navigation_bar.explore": "Esplori",
|
||||
"navigation_bar.favourites": "Stelumoj",
|
||||
"navigation_bar.filters": "Silentigitaj vortoj",
|
||||
|
@ -526,14 +514,7 @@
|
|||
"poll_button.add_poll": "Aldoni balotenketon",
|
||||
"poll_button.remove_poll": "Forigi balotenketon",
|
||||
"privacy.change": "Agordi mesaĝan privatecon",
|
||||
"privacy.direct.long": "Videbla nur al menciitaj uzantoj",
|
||||
"privacy.direct.short": "Nur menciitaj personoj",
|
||||
"privacy.private.long": "Videbla nur al viaj sekvantoj",
|
||||
"privacy.private.short": "Nur abonantoj",
|
||||
"privacy.public.long": "Videbla por ĉiuj",
|
||||
"privacy.public.short": "Publika",
|
||||
"privacy.unlisted.long": "Videbla por ĉiuj, sed ekskluzive el la funkcio de esploro",
|
||||
"privacy.unlisted.short": "Nelistigita",
|
||||
"privacy_policy.last_updated": "Laste ĝisdatigita en {date}",
|
||||
"privacy_policy.title": "Politiko de privateco",
|
||||
"recommended": "Rekomendita",
|
||||
|
@ -715,10 +696,8 @@
|
|||
"upload_error.poll": "Alŝuto de dosiero ne permesita kun balotenketo.",
|
||||
"upload_form.audio_description": "Priskribi por homoj kiuj malfacile aŭdi",
|
||||
"upload_form.description": "Priskribi por personoj, kiuj estas blindaj aŭ havas vidmalsufiĉon",
|
||||
"upload_form.description_missing": "Neniu priskribo aldonita",
|
||||
"upload_form.edit": "Redakti",
|
||||
"upload_form.thumbnail": "Ŝanĝi etigita bildo",
|
||||
"upload_form.undo": "Forigi",
|
||||
"upload_form.video_description": "Priskribi por homoj kiuj malfacile aŭdi aŭ vidi",
|
||||
"upload_modal.analyzing_picture": "Bilda analizado…",
|
||||
"upload_modal.apply": "Apliki",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Anuncio",
|
||||
"attachments_list.unprocessed": "[sin procesar]",
|
||||
"audio.hide": "Ocultar audio",
|
||||
"autosuggest_hashtag.per_week": "{count} por semana",
|
||||
"boost_modal.combo": "Podés hacer clic en {combo} para saltar esto la próxima vez",
|
||||
"bundle_column_error.copy_stacktrace": "Copiar informe de error",
|
||||
"bundle_column_error.error.body": "La página solicitada no pudo ser cargada. Podría deberse a un error de programación en nuestro código o a un problema de compatibilidad con el navegador web.",
|
||||
|
@ -146,22 +145,21 @@
|
|||
"compose_form.lock_disclaimer": "Tu cuenta no es {locked}. Todos pueden seguirte para ver tus mensajes marcados como \"Sólo para seguidores\".",
|
||||
"compose_form.lock_disclaimer.lock": "privada",
|
||||
"compose_form.placeholder": "¿Qué onda?",
|
||||
"compose_form.poll.add_option": "Agregá una opción",
|
||||
"compose_form.poll.duration": "Duración de la encuesta",
|
||||
"compose_form.poll.multiple": "Selección múltiple",
|
||||
"compose_form.poll.option_placeholder": "Opción {number}",
|
||||
"compose_form.poll.remove_option": "Quitar esta opción",
|
||||
"compose_form.poll.single": "Elige uno",
|
||||
"compose_form.poll.switch_to_multiple": "Cambiar encuesta para permitir opciones múltiples",
|
||||
"compose_form.poll.switch_to_single": "Cambiar encuesta para permitir una sola opción",
|
||||
"compose_form.poll.type": "Estilo",
|
||||
"compose_form.publish": "Publicar",
|
||||
"compose_form.publish_form": "Nuevo mensaje",
|
||||
"compose_form.publish_loud": "¡{publish}!",
|
||||
"compose_form.save_changes": "Guardar cambios",
|
||||
"compose_form.sensitive.hide": "Marcar medio como sensible",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {El medio está marcado como sensible} other {Los medios están marcados como sensibles}}",
|
||||
"compose_form.sensitive.unmarked": "El medio no está marcado como sensible",
|
||||
"compose_form.reply": "Responder",
|
||||
"compose_form.save_changes": "Actualizar",
|
||||
"compose_form.spoiler.marked": "Quitar advertencia de contenido",
|
||||
"compose_form.spoiler.unmarked": "Agregar advertencia de contenido",
|
||||
"compose_form.spoiler_placeholder": "Escribí tu advertencia acá",
|
||||
"compose_form.spoiler_placeholder": "Advertencia de contenido (opcional)",
|
||||
"confirmation_modal.cancel": "Cancelar",
|
||||
"confirmations.block.block_and_report": "Bloquear y denunciar",
|
||||
"confirmations.block.confirm": "Bloquear",
|
||||
|
@ -408,7 +406,6 @@
|
|||
"navigation_bar.direct": "Menciones privadas",
|
||||
"navigation_bar.discover": "Descubrir",
|
||||
"navigation_bar.domain_blocks": "Dominios bloqueados",
|
||||
"navigation_bar.edit_profile": "Editar perfil",
|
||||
"navigation_bar.explore": "Explorá",
|
||||
"navigation_bar.favourites": "Favoritos",
|
||||
"navigation_bar.filters": "Palabras silenciadas",
|
||||
|
@ -526,14 +523,10 @@
|
|||
"poll_button.add_poll": "Agregar encuesta",
|
||||
"poll_button.remove_poll": "Quitar encuesta",
|
||||
"privacy.change": "Configurar privacidad del mensaje",
|
||||
"privacy.direct.long": "Visible sólo para los usuarios mencionados",
|
||||
"privacy.direct.short": "Sólo cuentas mencionadas",
|
||||
"privacy.private.long": "Visible sólo para los seguidores",
|
||||
"privacy.private.short": "Sólo para seguidores",
|
||||
"privacy.public.long": "Visible para todos",
|
||||
"privacy.direct.short": "Personas específicas",
|
||||
"privacy.private.long": "Solo tus seguidores",
|
||||
"privacy.private.short": "Seguidores",
|
||||
"privacy.public.short": "Público",
|
||||
"privacy.unlisted.long": "Visible para todos, pero excluido de las características de descubrimiento",
|
||||
"privacy.unlisted.short": "No listado",
|
||||
"privacy_policy.last_updated": "Última actualización: {date}",
|
||||
"privacy_policy.title": "Política de privacidad",
|
||||
"recommended": "Opción recomendada",
|
||||
|
@ -715,10 +708,8 @@
|
|||
"upload_error.poll": "No se permite la subida de archivos en encuestas.",
|
||||
"upload_form.audio_description": "Agregá una descripción para personas con dificultades auditivas",
|
||||
"upload_form.description": "Agregá una descripción para personas con dificultades visuales",
|
||||
"upload_form.description_missing": "No se agregó descripción",
|
||||
"upload_form.edit": "Editar",
|
||||
"upload_form.thumbnail": "Cambiar miniatura",
|
||||
"upload_form.undo": "Eliminar",
|
||||
"upload_form.video_description": "Agregá una descripción para personas con dificultades auditivas o visuales",
|
||||
"upload_modal.analyzing_picture": "Analizando imagen…",
|
||||
"upload_modal.apply": "Aplicar",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Anuncio",
|
||||
"attachments_list.unprocessed": "(sin procesar)",
|
||||
"audio.hide": "Ocultar audio",
|
||||
"autosuggest_hashtag.per_week": "{count} por semana",
|
||||
"boost_modal.combo": "Puedes hacer clic en {combo} para saltar este aviso la próxima vez",
|
||||
"bundle_column_error.copy_stacktrace": "Copiar informe de error",
|
||||
"bundle_column_error.error.body": "La página solicitada no pudo ser renderizada. Podría deberse a un error en nuestro código o a un problema de compatibilidad con el navegador.",
|
||||
|
@ -146,22 +145,22 @@
|
|||
"compose_form.lock_disclaimer": "Tu cuenta no está bloqueada. Todos pueden seguirte para ver tus toots solo para seguidores.",
|
||||
"compose_form.lock_disclaimer.lock": "bloqueado",
|
||||
"compose_form.placeholder": "¿En qué estás pensando?",
|
||||
"compose_form.poll.add_option": "Añadir una opción",
|
||||
"compose_form.poll.add_option": "Agregar opción",
|
||||
"compose_form.poll.duration": "Duración de la encuesta",
|
||||
"compose_form.poll.option_placeholder": "Elección {number}",
|
||||
"compose_form.poll.multiple": "Selección múltiple",
|
||||
"compose_form.poll.option_placeholder": "Opción {number}",
|
||||
"compose_form.poll.remove_option": "Eliminar esta opción",
|
||||
"compose_form.poll.single": "Seleccione uno",
|
||||
"compose_form.poll.switch_to_multiple": "Modificar encuesta para permitir múltiples opciones",
|
||||
"compose_form.poll.switch_to_single": "Modificar encuesta para permitir una única opción",
|
||||
"compose_form.publish": "Publicar",
|
||||
"compose_form.poll.type": "Estilo",
|
||||
"compose_form.publish": "Publicación",
|
||||
"compose_form.publish_form": "Publicar",
|
||||
"compose_form.publish_loud": "¡{publish}!",
|
||||
"compose_form.save_changes": "Guardar cambios",
|
||||
"compose_form.sensitive.hide": "Marcar multimedia como sensible",
|
||||
"compose_form.sensitive.marked": "Material marcado como sensible",
|
||||
"compose_form.sensitive.unmarked": "Material no marcado como sensible",
|
||||
"compose_form.reply": "Respuesta",
|
||||
"compose_form.save_changes": "Actualización",
|
||||
"compose_form.spoiler.marked": "Texto oculto tras la advertencia",
|
||||
"compose_form.spoiler.unmarked": "Texto no oculto",
|
||||
"compose_form.spoiler_placeholder": "Advertencia de contenido",
|
||||
"compose_form.spoiler_placeholder": "Advertencia de contenido (opcional)",
|
||||
"confirmation_modal.cancel": "Cancelar",
|
||||
"confirmations.block.block_and_report": "Bloquear y Denunciar",
|
||||
"confirmations.block.confirm": "Bloquear",
|
||||
|
@ -408,7 +407,6 @@
|
|||
"navigation_bar.direct": "Menciones privadas",
|
||||
"navigation_bar.discover": "Descubrir",
|
||||
"navigation_bar.domain_blocks": "Dominios ocultos",
|
||||
"navigation_bar.edit_profile": "Editar perfil",
|
||||
"navigation_bar.explore": "Explorar",
|
||||
"navigation_bar.favourites": "Favoritos",
|
||||
"navigation_bar.filters": "Palabras silenciadas",
|
||||
|
@ -526,14 +524,15 @@
|
|||
"poll_button.add_poll": "Añadir una encuesta",
|
||||
"poll_button.remove_poll": "Eliminar encuesta",
|
||||
"privacy.change": "Ajustar privacidad",
|
||||
"privacy.direct.long": "Sólo mostrar a los usuarios mencionados",
|
||||
"privacy.direct.short": "Solo personas mencionadas",
|
||||
"privacy.private.long": "Sólo mostrar a seguidores",
|
||||
"privacy.private.short": "Solo seguidores",
|
||||
"privacy.public.long": "Visible para todos",
|
||||
"privacy.direct.long": "Todos los mencionados en la publicación",
|
||||
"privacy.direct.short": "Personas específicas",
|
||||
"privacy.private.long": "Sólo tus seguidores",
|
||||
"privacy.private.short": "Seguidores",
|
||||
"privacy.public.long": "Cualquiera dentro y fuera de Mastodon",
|
||||
"privacy.public.short": "Público",
|
||||
"privacy.unlisted.long": "Visible para todos, pero excluido de las funciones de descubrimiento",
|
||||
"privacy.unlisted.short": "No listado",
|
||||
"privacy.unlisted.additional": "Esto se comporta exactamente igual que el público, excepto que el post no aparecerá en las cronologías en directo o en los hashtags, la exploración o busquedas en Mastodon, incluso si está optado por activar la cuenta de usuario.",
|
||||
"privacy.unlisted.long": "Menos fanfares algorítmicos",
|
||||
"privacy.unlisted.short": "Público silencioso",
|
||||
"privacy_policy.last_updated": "Actualizado por última vez {date}",
|
||||
"privacy_policy.title": "Política de Privacidad",
|
||||
"recommended": "Recomendado",
|
||||
|
@ -551,7 +550,9 @@
|
|||
"relative_time.minutes": "{number} m",
|
||||
"relative_time.seconds": "{number} s",
|
||||
"relative_time.today": "hoy",
|
||||
"reply_indicator.attachments": "{count, plural, one {# adjunto} other {# adjuntos}}",
|
||||
"reply_indicator.cancel": "Cancelar",
|
||||
"reply_indicator.poll": "Encuesta",
|
||||
"report.block": "Bloquear",
|
||||
"report.block_explanation": "No veras sus publicaciones. No podrán ver tus publicaciones ni seguirte. Podrán saber que están bloqueados.",
|
||||
"report.categories.legal": "Legal",
|
||||
|
@ -715,10 +716,8 @@
|
|||
"upload_error.poll": "Subida de archivos no permitida con encuestas.",
|
||||
"upload_form.audio_description": "Describir para personas con problemas auditivos",
|
||||
"upload_form.description": "Describir para los usuarios con dificultad visual",
|
||||
"upload_form.description_missing": "Sin descripción añadida",
|
||||
"upload_form.edit": "Editar",
|
||||
"upload_form.thumbnail": "Cambiar miniatura",
|
||||
"upload_form.undo": "Borrar",
|
||||
"upload_form.video_description": "Describir para personas con problemas auditivos o visuales",
|
||||
"upload_modal.analyzing_picture": "Analizando imagen…",
|
||||
"upload_modal.apply": "Aplicar",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Anuncio",
|
||||
"attachments_list.unprocessed": "(sin procesar)",
|
||||
"audio.hide": "Ocultar audio",
|
||||
"autosuggest_hashtag.per_week": "{count} por semana",
|
||||
"boost_modal.combo": "Puedes hacer clic en {combo} para saltar este aviso la próxima vez",
|
||||
"bundle_column_error.copy_stacktrace": "Copiar informe de error",
|
||||
"bundle_column_error.error.body": "La página solicitada no pudo ser renderizada. Podría deberse a un error en nuestro código o a un problema de compatibilidad con el navegador.",
|
||||
|
@ -146,22 +145,21 @@
|
|||
"compose_form.lock_disclaimer": "Tu cuenta no está {locked}. Todos pueden seguirte para ver tus publicaciones solo para seguidores.",
|
||||
"compose_form.lock_disclaimer.lock": "bloqueado",
|
||||
"compose_form.placeholder": "¿En qué estás pensando?",
|
||||
"compose_form.poll.add_option": "Añadir una opción",
|
||||
"compose_form.poll.duration": "Duración de la encuesta",
|
||||
"compose_form.poll.option_placeholder": "Elección {number}",
|
||||
"compose_form.poll.remove_option": "Eliminar esta opción",
|
||||
"compose_form.poll.multiple": "Selección múltiple",
|
||||
"compose_form.poll.option_placeholder": "Opción {number}",
|
||||
"compose_form.poll.remove_option": "Quitar esta opción",
|
||||
"compose_form.poll.single": "Elige uno",
|
||||
"compose_form.poll.switch_to_multiple": "Modificar encuesta para permitir múltiples opciones",
|
||||
"compose_form.poll.switch_to_single": "Modificar encuesta para permitir una única opción",
|
||||
"compose_form.poll.type": "Estilo",
|
||||
"compose_form.publish": "Publicar",
|
||||
"compose_form.publish_form": "Publicar",
|
||||
"compose_form.publish_loud": "¡{publish}!",
|
||||
"compose_form.save_changes": "Guardar cambios",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Marcar material como sensible} other {Marcar material como sensible}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {Material marcado como sensible} other {Material marcado como sensible}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Material no marcado como sensible} other {Material no marcado como sensible}}",
|
||||
"compose_form.reply": "Responder",
|
||||
"compose_form.save_changes": "Actualizar",
|
||||
"compose_form.spoiler.marked": "Quitar advertencia de contenido",
|
||||
"compose_form.spoiler.unmarked": "Añadir advertencia de contenido",
|
||||
"compose_form.spoiler_placeholder": "Escribe aquí tu advertencia",
|
||||
"compose_form.spoiler_placeholder": "Advertencia de contenido (opcional)",
|
||||
"confirmation_modal.cancel": "Cancelar",
|
||||
"confirmations.block.block_and_report": "Bloquear y Reportar",
|
||||
"confirmations.block.confirm": "Bloquear",
|
||||
|
@ -408,7 +406,6 @@
|
|||
"navigation_bar.direct": "Menciones privadas",
|
||||
"navigation_bar.discover": "Descubrir",
|
||||
"navigation_bar.domain_blocks": "Dominios ocultos",
|
||||
"navigation_bar.edit_profile": "Editar perfil",
|
||||
"navigation_bar.explore": "Explorar",
|
||||
"navigation_bar.favourites": "Favoritos",
|
||||
"navigation_bar.filters": "Palabras silenciadas",
|
||||
|
@ -526,14 +523,10 @@
|
|||
"poll_button.add_poll": "Añadir una encuesta",
|
||||
"poll_button.remove_poll": "Eliminar encuesta",
|
||||
"privacy.change": "Ajustar privacidad",
|
||||
"privacy.direct.long": "Visible solo para usuarios mencionados",
|
||||
"privacy.direct.short": "Sólo cuentas mencionadas",
|
||||
"privacy.private.long": "Sólo mostrar a seguidores",
|
||||
"privacy.private.short": "Solo seguidores",
|
||||
"privacy.public.long": "Visible para todos",
|
||||
"privacy.direct.short": "Personas específicas",
|
||||
"privacy.private.long": "Solo tus seguidores",
|
||||
"privacy.private.short": "Seguidores",
|
||||
"privacy.public.short": "Público",
|
||||
"privacy.unlisted.long": "Visible para todos, pero excluido de las funciones de descubrimiento",
|
||||
"privacy.unlisted.short": "No listado",
|
||||
"privacy_policy.last_updated": "Actualizado por última vez {date}",
|
||||
"privacy_policy.title": "Política de Privacidad",
|
||||
"recommended": "Recomendado",
|
||||
|
@ -715,10 +708,8 @@
|
|||
"upload_error.poll": "No se permite la subida de archivos con encuestas.",
|
||||
"upload_form.audio_description": "Describir para personas con problemas auditivos",
|
||||
"upload_form.description": "Describir para personas con discapacidad visual",
|
||||
"upload_form.description_missing": "No se ha añadido ninguna descripción",
|
||||
"upload_form.edit": "Editar",
|
||||
"upload_form.thumbnail": "Cambiar miniatura",
|
||||
"upload_form.undo": "Eliminar",
|
||||
"upload_form.video_description": "Describir para personas con problemas auditivos o visuales",
|
||||
"upload_modal.analyzing_picture": "Analizando imagen…",
|
||||
"upload_modal.apply": "Aplicar",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Teadaanne",
|
||||
"attachments_list.unprocessed": "(töötlemata)",
|
||||
"audio.hide": "Peida audio",
|
||||
"autosuggest_hashtag.per_week": "{count} nädalas",
|
||||
"boost_modal.combo": "Vajutades {combo}, saab selle edaspidi vahele jätta",
|
||||
"bundle_column_error.copy_stacktrace": "Kopeeri veateade",
|
||||
"bundle_column_error.error.body": "Soovitud lehte ei õnnestunud esitada. See võib olla meie koodiviga või probleem brauseri ühilduvusega.",
|
||||
|
@ -148,20 +147,20 @@
|
|||
"compose_form.placeholder": "Millest mõtled?",
|
||||
"compose_form.poll.add_option": "Lisa valik",
|
||||
"compose_form.poll.duration": "Küsitluse kestus",
|
||||
"compose_form.poll.multiple": "Valikvastustega",
|
||||
"compose_form.poll.option_placeholder": "Valik {number}",
|
||||
"compose_form.poll.remove_option": "Eemalda see valik",
|
||||
"compose_form.poll.single": "Vali üks",
|
||||
"compose_form.poll.switch_to_multiple": "Muuda küsitlust mitmikvaliku lubamiseks",
|
||||
"compose_form.poll.switch_to_single": "Muuda küsitlust ainult ühe valiku lubamiseks",
|
||||
"compose_form.poll.type": "Stiil",
|
||||
"compose_form.publish": "Postita",
|
||||
"compose_form.publish_form": "Postita",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Salvesta muudatused",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Märgi meedia tundlikuks} other {Märgi meediad tundlikuks}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {Meedia on märgitud tundlikuks} other {Meediad on märgitud tundlikuks}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Meedia ei ole tundlikuks märgitud} other {Meediad ei ole märgitud tundlikuks}}",
|
||||
"compose_form.reply": "Vasta",
|
||||
"compose_form.save_changes": "Uuenda",
|
||||
"compose_form.spoiler.marked": "Tekst on hoiatuse taha peidetud",
|
||||
"compose_form.spoiler.unmarked": "Märgi sisu tundlikuks",
|
||||
"compose_form.spoiler_placeholder": "Kirjuta hoiatus siia",
|
||||
"compose_form.spoiler_placeholder": "Sisuhoiatus (valikuline)",
|
||||
"confirmation_modal.cancel": "Katkesta",
|
||||
"confirmations.block.block_and_report": "Blokeeri ja teata",
|
||||
"confirmations.block.confirm": "Blokeeri",
|
||||
|
@ -408,7 +407,6 @@
|
|||
"navigation_bar.direct": "Privaatsed mainimised",
|
||||
"navigation_bar.discover": "Avasta",
|
||||
"navigation_bar.domain_blocks": "Peidetud domeenid",
|
||||
"navigation_bar.edit_profile": "Muuda profiili",
|
||||
"navigation_bar.explore": "Avasta",
|
||||
"navigation_bar.favourites": "Lemmikud",
|
||||
"navigation_bar.filters": "Vaigistatud sõnad",
|
||||
|
@ -526,14 +524,14 @@
|
|||
"poll_button.add_poll": "Lisa küsitlus",
|
||||
"poll_button.remove_poll": "Eemalda küsitlus",
|
||||
"privacy.change": "Muuda postituse nähtavust",
|
||||
"privacy.direct.long": "Postita ainult mainitud kasutajatele",
|
||||
"privacy.direct.short": "Mainitud inimesed ainult",
|
||||
"privacy.private.long": "Postita ainult jälgijatele",
|
||||
"privacy.private.short": "Jälgijad ainult",
|
||||
"privacy.public.long": "Kõigile nähtav",
|
||||
"privacy.direct.long": "Kõik postituses mainitud",
|
||||
"privacy.direct.short": "Määratud kasutajad",
|
||||
"privacy.private.long": "Ainult jälgijad",
|
||||
"privacy.private.short": "Jälgijad",
|
||||
"privacy.public.long": "Nii kasutajad kui mittekasutajad",
|
||||
"privacy.public.short": "Avalik",
|
||||
"privacy.unlisted.long": "Kõigile nähtav, aga ei ilmu avastamise vaadetes",
|
||||
"privacy.unlisted.short": "Määramata",
|
||||
"privacy.unlisted.long": "Vähem algoritmilisi teavitusi",
|
||||
"privacy.unlisted.short": "Vaikselt avalik",
|
||||
"privacy_policy.last_updated": "Viimati uuendatud {date}",
|
||||
"privacy_policy.title": "Isikuandmete kaitse",
|
||||
"recommended": "Soovitatud",
|
||||
|
@ -551,7 +549,9 @@
|
|||
"relative_time.minutes": "{number}m",
|
||||
"relative_time.seconds": "{number}s",
|
||||
"relative_time.today": "täna",
|
||||
"reply_indicator.attachments": "{count, plural, one {# lisa} other {# lisa}}",
|
||||
"reply_indicator.cancel": "Tühista",
|
||||
"reply_indicator.poll": "Küsitlus",
|
||||
"report.block": "Blokeeri",
|
||||
"report.block_explanation": "Sa ei näe tema postitusi. Tema ei saa näha sinu postitusi ega sind jälgida. Talle on näha, et ta on blokeeritud.",
|
||||
"report.categories.legal": "Juriidiline",
|
||||
|
@ -715,10 +715,8 @@
|
|||
"upload_error.poll": "Küsitlustes pole faili üleslaadimine lubatud.",
|
||||
"upload_form.audio_description": "Kirjelda kuulmispuudega inimeste jaoks",
|
||||
"upload_form.description": "Kirjelda vaegnägijatele",
|
||||
"upload_form.description_missing": "Kirjeldus puudub",
|
||||
"upload_form.edit": "Muuda",
|
||||
"upload_form.thumbnail": "Muuda pisipilti",
|
||||
"upload_form.undo": "Kustuta",
|
||||
"upload_form.video_description": "Kirjelda kuulmis- või nägemispuudega inimeste jaoks",
|
||||
"upload_modal.analyzing_picture": "Analüüsime pilti…",
|
||||
"upload_modal.apply": "Rakenda",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"account.direct": "Aipatu pribatuki @{name}",
|
||||
"account.disable_notifications": "Utzi jakinarazteari @{name} erabiltzaileak argitaratzean",
|
||||
"account.domain_blocked": "Ezkutatutako domeinua",
|
||||
"account.edit_profile": "Aldatu profila",
|
||||
"account.edit_profile": "Editatu profila",
|
||||
"account.enable_notifications": "Jakinarazi @{name} erabiltzaileak argitaratzean",
|
||||
"account.endorse": "Nabarmendu profilean",
|
||||
"account.featured_tags.last_status_at": "Azken bidalketa {date} datan",
|
||||
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Iragarpena",
|
||||
"attachments_list.unprocessed": "(prozesatu gabe)",
|
||||
"audio.hide": "Ezkutatu audioa",
|
||||
"autosuggest_hashtag.per_week": "{count} asteko",
|
||||
"boost_modal.combo": "{combo} sakatu dezakezu hurrengoan hau saltatzeko",
|
||||
"bundle_column_error.copy_stacktrace": "Kopiatu errore-txostena",
|
||||
"bundle_column_error.error.body": "Eskatutako orria ezin izan da bistaratu. Kodeko errore bategatik izan daiteke edo nabigatzailearen bateragarritasun arazo bategatik.",
|
||||
|
@ -146,22 +145,22 @@
|
|||
"compose_form.lock_disclaimer": "Zure kontua ez dago {locked}. Edonork jarraitu zaitzake zure jarraitzaileentzako soilik diren bidalketak ikusteko.",
|
||||
"compose_form.lock_disclaimer.lock": "giltzapetuta",
|
||||
"compose_form.placeholder": "Zer duzu buruan?",
|
||||
"compose_form.poll.add_option": "Gehitu aukera bat",
|
||||
"compose_form.poll.add_option": "Gehitu aukera",
|
||||
"compose_form.poll.duration": "Inkestaren iraupena",
|
||||
"compose_form.poll.multiple": "Aukera aniza",
|
||||
"compose_form.poll.option_placeholder": "{number}. aukera",
|
||||
"compose_form.poll.remove_option": "Kendu aukera hau",
|
||||
"compose_form.poll.single": "Hautatu bat",
|
||||
"compose_form.poll.switch_to_multiple": "Aldatu inkesta hainbat aukera onartzeko",
|
||||
"compose_form.poll.switch_to_single": "Aldatu inkesta aukera bakarra onartzeko",
|
||||
"compose_form.poll.type": "Estiloa",
|
||||
"compose_form.publish": "Argitaratu",
|
||||
"compose_form.publish_form": "Argitaratu",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Gorde aldaketak",
|
||||
"compose_form.sensitive.hide": "Markatu multimedia hunkigarri gisa",
|
||||
"compose_form.sensitive.marked": "Multimedia edukia hunkigarri gisa markatu da",
|
||||
"compose_form.sensitive.unmarked": "Multimedia edukia ez da hunkigarri gisa markatu",
|
||||
"compose_form.reply": "Erantzun",
|
||||
"compose_form.save_changes": "Eguneratu",
|
||||
"compose_form.spoiler.marked": "Testua abisu batek ezkutatzen du",
|
||||
"compose_form.spoiler.unmarked": "Testua ez dago ezkutatuta",
|
||||
"compose_form.spoiler_placeholder": "Idatzi zure abisua hemen",
|
||||
"compose_form.spoiler_placeholder": "Edukiaren abisua (aukerakoa)",
|
||||
"confirmation_modal.cancel": "Utzi",
|
||||
"confirmations.block.block_and_report": "Blokeatu eta salatu",
|
||||
"confirmations.block.confirm": "Blokeatu",
|
||||
|
@ -408,7 +407,6 @@
|
|||
"navigation_bar.direct": "Aipamen pribatuak",
|
||||
"navigation_bar.discover": "Aurkitu",
|
||||
"navigation_bar.domain_blocks": "Ezkutatutako domeinuak",
|
||||
"navigation_bar.edit_profile": "Aldatu profila",
|
||||
"navigation_bar.explore": "Arakatu",
|
||||
"navigation_bar.favourites": "Gogokoak",
|
||||
"navigation_bar.filters": "Mutututako hitzak",
|
||||
|
@ -526,14 +524,15 @@
|
|||
"poll_button.add_poll": "Gehitu inkesta bat",
|
||||
"poll_button.remove_poll": "Kendu inkesta",
|
||||
"privacy.change": "Aldatu bidalketaren pribatutasuna",
|
||||
"privacy.direct.long": "Bidali aipatutako erabiltzaileei besterik ez",
|
||||
"privacy.direct.short": "Aipatutako jendea soilik",
|
||||
"privacy.private.long": "Bidali jarraitzaileei besterik ez",
|
||||
"privacy.private.short": "Jarraitzaileak soilik",
|
||||
"privacy.public.long": "Guztientzat ikusgai",
|
||||
"privacy.direct.long": "Argitalpen honetan aipatutako denak",
|
||||
"privacy.direct.short": "Jende jakina",
|
||||
"privacy.private.long": "Soilik jarraitzaileak",
|
||||
"privacy.private.short": "Jarraitzaileak",
|
||||
"privacy.public.long": "Mastodonen dagoen edo ez dagoen edonor",
|
||||
"privacy.public.short": "Publikoa",
|
||||
"privacy.unlisted.long": "Guztientzat ikusgai, baina ez aurkitzeko ezaugarrietan",
|
||||
"privacy.unlisted.short": "Zerrendatu gabea",
|
||||
"privacy.unlisted.additional": "Aukera honek publiko modua bezala funtzionatzen du, baina argitalpena ez da agertuko zuzeneko jarioetan edo traoletan, \"Arakatu\" atalean edo Mastodonen bilaketan, nahiz eta kontua zabaltzeko onartu duzun.",
|
||||
"privacy.unlisted.long": "Tontakeria algoritmiko gutxiago",
|
||||
"privacy.unlisted.short": "Deiadar urrikoa",
|
||||
"privacy_policy.last_updated": "Azkenengo eguneraketa {date}",
|
||||
"privacy_policy.title": "Pribatutasun politika",
|
||||
"recommended": "Gomendatua",
|
||||
|
@ -551,7 +550,9 @@
|
|||
"relative_time.minutes": "{number}m",
|
||||
"relative_time.seconds": "{number}s",
|
||||
"relative_time.today": "gaur",
|
||||
"reply_indicator.attachments": "{count, plural, one {# eranskin} other {# eranskin}}",
|
||||
"reply_indicator.cancel": "Utzi",
|
||||
"reply_indicator.poll": "Inkesta",
|
||||
"report.block": "Blokeatu",
|
||||
"report.block_explanation": "Ez dituzu bere bidalketak ikusiko. Ezingo dituzte zure bidalketak ikusi eta ez jarraitu. Blokeatu dituzula jakin dezakete.",
|
||||
"report.categories.legal": "Juridikoa",
|
||||
|
@ -715,10 +716,8 @@
|
|||
"upload_error.poll": "Ez da inkestetan fitxategiak igotzea onartzen.",
|
||||
"upload_form.audio_description": "Deskribatu entzumen galera duten pertsonentzat",
|
||||
"upload_form.description": "Deskribatu ikusmen arazoak dituztenentzat",
|
||||
"upload_form.description_missing": "Ez da deskribapenik gehitu",
|
||||
"upload_form.edit": "Editatu",
|
||||
"upload_form.thumbnail": "Aldatu koadro txikia",
|
||||
"upload_form.undo": "Ezabatu",
|
||||
"upload_form.video_description": "Deskribatu entzumen galera edo ikusmen urritasuna duten pertsonentzat",
|
||||
"upload_modal.analyzing_picture": "Irudia aztertzen…",
|
||||
"upload_modal.apply": "Aplikatu",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "اعلامیه",
|
||||
"attachments_list.unprocessed": "(پردازش نشده)",
|
||||
"audio.hide": "نهفتن صدا",
|
||||
"autosuggest_hashtag.per_week": "{count} در هفته",
|
||||
"boost_modal.combo": "دکمهٔ {combo} را بزنید تا دیگر این را نبینید",
|
||||
"bundle_column_error.copy_stacktrace": "رونوشت از گزارش خطا",
|
||||
"bundle_column_error.error.body": "صفحهٔ درخواستی نتوانست پرداخت شود. ممکن است به خاطر اشکالی در کدمان یا مشکل سازگاری مرورگر باشد.",
|
||||
|
@ -148,20 +147,20 @@
|
|||
"compose_form.placeholder": "تازه چه خبر؟",
|
||||
"compose_form.poll.add_option": "افزودن گزینه",
|
||||
"compose_form.poll.duration": "مدت نظرسنجی",
|
||||
"compose_form.poll.multiple": "چند گزینهای",
|
||||
"compose_form.poll.option_placeholder": "گزینهٔ {number}",
|
||||
"compose_form.poll.remove_option": "برداشتن این گزینه",
|
||||
"compose_form.poll.single": "گزینش یکی",
|
||||
"compose_form.poll.switch_to_multiple": "تغییر نظرسنجی برای اجازه به چندین گزینه",
|
||||
"compose_form.poll.switch_to_single": "تبدیل به نظرسنجی تکگزینهای",
|
||||
"compose_form.publish": "انتشار",
|
||||
"compose_form.poll.type": "سبک",
|
||||
"compose_form.publish": "فرستادن",
|
||||
"compose_form.publish_form": "انتشار",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "ذخیرهٔ تغییرات",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {علامتگذاری رسانه به عنوان حساس} other {علامتگذاری رسانهها به عنوان حساس}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {رسانه به عنوان حساس علامتگذاری شد} other {رسانهها به عنوان حساس علامتگذاری شدند}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {رسانه به عنوان حساس علامتگذاری نشد} other {رسانهها به عنوان حساس علامتگذاری نشدند}}",
|
||||
"compose_form.reply": "پاسخ",
|
||||
"compose_form.save_changes": "بهروز رسانی",
|
||||
"compose_form.spoiler.marked": "برداشتن هشدار محتوا",
|
||||
"compose_form.spoiler.unmarked": "افزودن هشدار محتوا",
|
||||
"compose_form.spoiler_placeholder": "هشدارتان را اینجا بنویسید",
|
||||
"compose_form.spoiler_placeholder": "هشدار محتوا (اختیاری)",
|
||||
"confirmation_modal.cancel": "لغو",
|
||||
"confirmations.block.block_and_report": "انسداد و گزارش",
|
||||
"confirmations.block.confirm": "انسداد",
|
||||
|
@ -408,7 +407,6 @@
|
|||
"navigation_bar.direct": "اشارههای خصوصی",
|
||||
"navigation_bar.discover": "گشت و گذار",
|
||||
"navigation_bar.domain_blocks": "دامنههای مسدود شده",
|
||||
"navigation_bar.edit_profile": "ویرایش نمایه",
|
||||
"navigation_bar.explore": "کاوش",
|
||||
"navigation_bar.favourites": "برگزیدهها",
|
||||
"navigation_bar.filters": "واژههای خموش",
|
||||
|
@ -524,14 +522,14 @@
|
|||
"poll_button.add_poll": "افزودن نظرسنجی",
|
||||
"poll_button.remove_poll": "برداشتن نظرسنجی",
|
||||
"privacy.change": "تغییر محرمانگی فرسته",
|
||||
"privacy.direct.long": "نمایان فقط برای کاربران اشاره شده",
|
||||
"privacy.direct.short": "فقط افراد اشاره شده",
|
||||
"privacy.private.long": "نمایان فقط برای پیگیرندگان",
|
||||
"privacy.private.short": "فقط پیگیرندگان",
|
||||
"privacy.public.long": "نمایان برای همه",
|
||||
"privacy.direct.long": "هرکسی که در فرسته نام برده شده",
|
||||
"privacy.direct.short": "افراد مشخّص",
|
||||
"privacy.private.long": "تنها پیگیرندگانتان",
|
||||
"privacy.private.short": "پیگیرندگان",
|
||||
"privacy.public.long": "هرکسی در و بیرون از ماستودون",
|
||||
"privacy.public.short": "عمومی",
|
||||
"privacy.unlisted.long": "نمایان برای همه، ولی خارج از قابلیتهای کشف",
|
||||
"privacy.unlisted.short": "فهرست نشده",
|
||||
"privacy.unlisted.long": "سروصدای الگوریتمی کمتر",
|
||||
"privacy.unlisted.short": "عمومی ساکت",
|
||||
"privacy_policy.last_updated": "آخرین بهروز رسانی در {date}",
|
||||
"privacy_policy.title": "سیاست محرمانگی",
|
||||
"recommended": "پیشنهادشده",
|
||||
|
@ -550,6 +548,7 @@
|
|||
"relative_time.seconds": "{number} ثانیه",
|
||||
"relative_time.today": "امروز",
|
||||
"reply_indicator.cancel": "لغو",
|
||||
"reply_indicator.poll": "نظرسنجی",
|
||||
"report.block": "انسداد",
|
||||
"report.block_explanation": "شما فرستههایشان را نخواهید دید. آنها نمیتوانند فرستههایتان را ببینند یا شما را پیبگیرند. آنها میتوانند بگویند که مسدود شدهاند.",
|
||||
"report.categories.legal": "حقوقی",
|
||||
|
@ -713,10 +712,8 @@
|
|||
"upload_error.poll": "بارگذاری پرونده در نظرسنجیها مجاز نیست.",
|
||||
"upload_form.audio_description": "برای ناشنوایان توصیفش کنید",
|
||||
"upload_form.description": "برای کمبینایان توصیفش کنید",
|
||||
"upload_form.description_missing": "شرحی افزوده نشده",
|
||||
"upload_form.edit": "ویرایش",
|
||||
"upload_form.thumbnail": "تغییر بندانگشتی",
|
||||
"upload_form.undo": "حذف",
|
||||
"upload_form.video_description": "برای کمبینایان یا ناشنوایان توصیفش کنید",
|
||||
"upload_modal.analyzing_picture": "در حال پردازش تصویر…",
|
||||
"upload_modal.apply": "اعمال",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Ilmoitus",
|
||||
"attachments_list.unprocessed": "(käsittelemätön)",
|
||||
"audio.hide": "Piilota ääni",
|
||||
"autosuggest_hashtag.per_week": "{count} viikossa",
|
||||
"boost_modal.combo": "Ensi kerralla voit ohittaa tämän painamalla {combo}",
|
||||
"bundle_column_error.copy_stacktrace": "Kopioi virheraportti",
|
||||
"bundle_column_error.error.body": "Pyydettyä sivua ei voitu hahmontaa. Se voi johtua virheestä koodissamme tai selaimen yhteensopivuudessa.",
|
||||
|
@ -146,22 +145,22 @@
|
|||
"compose_form.lock_disclaimer": "Tilisi ei ole {locked}. Kuka tahansa voi seurata tiliäsi ja nähdä vain seuraajille rajaamasi julkaisut.",
|
||||
"compose_form.lock_disclaimer.lock": "lukittu",
|
||||
"compose_form.placeholder": "Mitä mietit?",
|
||||
"compose_form.poll.add_option": "Lisää valinta",
|
||||
"compose_form.poll.add_option": "Lisää vaihtoehto",
|
||||
"compose_form.poll.duration": "Äänestyksen kesto",
|
||||
"compose_form.poll.option_placeholder": "Valinta {number}",
|
||||
"compose_form.poll.remove_option": "Poista tämä valinta",
|
||||
"compose_form.poll.multiple": "Monivalinta",
|
||||
"compose_form.poll.option_placeholder": "Vaihtoehto {number}",
|
||||
"compose_form.poll.remove_option": "Poista tämä vaihtoehto",
|
||||
"compose_form.poll.single": "Valitse yksi",
|
||||
"compose_form.poll.switch_to_multiple": "Muuta äänestys monivalinnaksi",
|
||||
"compose_form.poll.switch_to_single": "Muuta äänestys sallimaan vain yksi valinta",
|
||||
"compose_form.poll.type": "Tyyli",
|
||||
"compose_form.publish": "Julkaise",
|
||||
"compose_form.publish_form": "Uusi julkaisu",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Tallenna muutokset",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Merkitse media arkaluonteiseksi} other {Merkitse mediat arkaluonteisiksi}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {Media on merkitty arkaluonteiseksi} other {Mediat on merkitty arkaluonteisiksi}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Mediaa ei ole merkitty arkaluonteiseksi} other {Medioita ei ole merkitty arkaluonteisiksi}}",
|
||||
"compose_form.reply": "Vastaa",
|
||||
"compose_form.save_changes": "Päivitä",
|
||||
"compose_form.spoiler.marked": "Poista sisältövaroitus",
|
||||
"compose_form.spoiler.unmarked": "Lisää sisältövaroitus",
|
||||
"compose_form.spoiler_placeholder": "Kirjoita varoituksesi tähän",
|
||||
"compose_form.spoiler_placeholder": "Sisältövaroitus (valinnainen)",
|
||||
"confirmation_modal.cancel": "Peruuta",
|
||||
"confirmations.block.block_and_report": "Estä ja raportoi",
|
||||
"confirmations.block.confirm": "Estä",
|
||||
|
@ -408,7 +407,6 @@
|
|||
"navigation_bar.direct": "Yksityiset maininnat",
|
||||
"navigation_bar.discover": "Löydä uutta",
|
||||
"navigation_bar.domain_blocks": "Estetyt verkkotunnukset",
|
||||
"navigation_bar.edit_profile": "Muokkaa profiilia",
|
||||
"navigation_bar.explore": "Selaa",
|
||||
"navigation_bar.favourites": "Suosikit",
|
||||
"navigation_bar.filters": "Mykistetyt sanat",
|
||||
|
@ -526,14 +524,13 @@
|
|||
"poll_button.add_poll": "Lisää äänestys",
|
||||
"poll_button.remove_poll": "Poista äänestys",
|
||||
"privacy.change": "Muuta julkaisun näkyvyyttä",
|
||||
"privacy.direct.long": "Näkyy vain mainituille käyttäjille",
|
||||
"privacy.direct.short": "Vain mainitut käyttäjät",
|
||||
"privacy.private.long": "Näkyy vain seuraajille",
|
||||
"privacy.private.short": "Vain seuraajat",
|
||||
"privacy.public.long": "Näkyy kaikille",
|
||||
"privacy.direct.long": "Kaikki tässä julkaisussa mainitut",
|
||||
"privacy.direct.short": "Tietyt henkilöt",
|
||||
"privacy.private.long": "Vain seuraajasi",
|
||||
"privacy.private.short": "Seuraajat",
|
||||
"privacy.public.long": "Kuka tahansa Mastodonissa ja sen ulkopuolella",
|
||||
"privacy.public.short": "Julkinen",
|
||||
"privacy.unlisted.long": "Näkyy kaikille mutta jää pois löytämisominaisuuksista",
|
||||
"privacy.unlisted.short": "Listaamaton",
|
||||
"privacy.unlisted.additional": "Tämä toimii kuten julkinen, paitsi että julkaisu ei näy livesyötteissä, aihetunnisteissa, selaa-näkymässä tai Mastodon-haussa, vaikka olisit sallinut ne käyttäjätilin laajuisesti.",
|
||||
"privacy_policy.last_updated": "Viimeksi päivitetty {date}",
|
||||
"privacy_policy.title": "Tietosuojakäytäntö",
|
||||
"recommended": "Suositeltu",
|
||||
|
@ -551,7 +548,9 @@
|
|||
"relative_time.minutes": "{number} min",
|
||||
"relative_time.seconds": "{number} s",
|
||||
"relative_time.today": "tänään",
|
||||
"reply_indicator.attachments": "{count, plural, one {# liite} other {# liitettä}}",
|
||||
"reply_indicator.cancel": "Peruuta",
|
||||
"reply_indicator.poll": "Kysely",
|
||||
"report.block": "Estä",
|
||||
"report.block_explanation": "Et näe hänen viestejään, eikä hän voi nähdä viestejäsi tai seurata sinua. Hän näkee, että olet estänyt hänet.",
|
||||
"report.categories.legal": "Lakiasiat",
|
||||
|
@ -715,10 +714,8 @@
|
|||
"upload_error.poll": "Tiedoston lataaminen ei ole sallittua äänestyksissä.",
|
||||
"upload_form.audio_description": "Kuvaile sisältöä kuuroille ja kuulorajoitteisille",
|
||||
"upload_form.description": "Kuvaile sisältöä sokeille ja näkörajoitteisille",
|
||||
"upload_form.description_missing": "Kuvausta ei ole lisätty",
|
||||
"upload_form.edit": "Muokkaa",
|
||||
"upload_form.thumbnail": "Vaihda pikkukuva",
|
||||
"upload_form.undo": "Poista",
|
||||
"upload_form.video_description": "Kuvaile sisältöä kuuroille, kuulorajoitteisille, sokeille tai näkörajoitteisille",
|
||||
"upload_modal.analyzing_picture": "Analysoidaan kuvaa…",
|
||||
"upload_modal.apply": "Käytä",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Kunngerð",
|
||||
"attachments_list.unprocessed": "(óviðgjørt)",
|
||||
"audio.hide": "Fjal ljóð",
|
||||
"autosuggest_hashtag.per_week": "{count} um vikuna",
|
||||
"boost_modal.combo": "Tú kanst trýsta á {combo} fyri at loypa uppum hetta næstu ferð",
|
||||
"bundle_column_error.copy_stacktrace": "Avrita feilfráboðan",
|
||||
"bundle_column_error.error.body": "Umbidna síðan kann ikki vísast. Tað kann vera orsakað av einum feili í koduni hjá okkum ella tað kann vera orsakað av kaganum, sum tú brúkar.",
|
||||
|
@ -148,20 +147,20 @@
|
|||
"compose_form.placeholder": "Hvat hevur tú í huga?",
|
||||
"compose_form.poll.add_option": "Legg valmøguleika afturat",
|
||||
"compose_form.poll.duration": "Atkvøðugreiðslutíð",
|
||||
"compose_form.poll.multiple": "Fleiri valmøguleikar",
|
||||
"compose_form.poll.option_placeholder": "Valmøguleiki {number}",
|
||||
"compose_form.poll.remove_option": "Strika valmøguleikan",
|
||||
"compose_form.poll.remove_option": "Strika hendan valmøguleikan",
|
||||
"compose_form.poll.single": "Vel ein",
|
||||
"compose_form.poll.switch_to_multiple": "Broyt atkvøðugreiðslu til at loyva fleiri svarum",
|
||||
"compose_form.poll.switch_to_single": "Broyt atkvøðugreiðslu til einstakt svar",
|
||||
"compose_form.publish": "Legg út",
|
||||
"compose_form.poll.type": "Stílur",
|
||||
"compose_form.publish": "Posta",
|
||||
"compose_form.publish_form": "Legg út",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Goym broytingar",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Frámerk tilfar sum viðkvæmt} other {Frámerk tilfar sum viðkvæmt}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {Tilfarið er frámerkt sum viðkvæmt} other {Tilfarið er frámerkt sum viðkvæmt}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Tilfarið er ikki merkt sum viðkvæmt} other {Tilfarið er ikki merkt sum viðkvæmt}}",
|
||||
"compose_form.reply": "Svara",
|
||||
"compose_form.save_changes": "Dagfør",
|
||||
"compose_form.spoiler.marked": "Ávaring um at strika innihald",
|
||||
"compose_form.spoiler.unmarked": "Skriva ávaring um innihald",
|
||||
"compose_form.spoiler_placeholder": "Skriva tína ávaring her",
|
||||
"compose_form.spoiler_placeholder": "Innihaldsávaring (valfrí)",
|
||||
"confirmation_modal.cancel": "Strika",
|
||||
"confirmations.block.block_and_report": "Banna og melda",
|
||||
"confirmations.block.confirm": "Banna",
|
||||
|
@ -408,7 +407,6 @@
|
|||
"navigation_bar.direct": "Privatar umrøður",
|
||||
"navigation_bar.discover": "Uppdaga",
|
||||
"navigation_bar.domain_blocks": "Bannað økisnøvn",
|
||||
"navigation_bar.edit_profile": "Broyt vanga",
|
||||
"navigation_bar.explore": "Rannsaka",
|
||||
"navigation_bar.favourites": "Dámdir postar",
|
||||
"navigation_bar.filters": "Doyvd orð",
|
||||
|
@ -526,14 +524,15 @@
|
|||
"poll_button.add_poll": "Legg atkvøðugreiðslu afturat",
|
||||
"poll_button.remove_poll": "Strika atkvøðugreiðslu",
|
||||
"privacy.change": "Broyt privatverju av posti",
|
||||
"privacy.direct.long": "Bert sjónligt hjá nevndum brúkarum",
|
||||
"privacy.direct.short": "Bert nevnd fólk",
|
||||
"privacy.private.long": "Bert sjónligt hjá fylgjarum",
|
||||
"privacy.private.short": "Einans fylgjarar",
|
||||
"privacy.public.long": "Sjónligt hjá øllum",
|
||||
"privacy.direct.long": "Øll, sum eru nevnd í postinum",
|
||||
"privacy.direct.short": "Ávís fólk",
|
||||
"privacy.private.long": "Einans tey, ið fylgja tær",
|
||||
"privacy.private.short": "Fylgjarar",
|
||||
"privacy.public.long": "Øll í og uttanfyri Mastodon",
|
||||
"privacy.public.short": "Alment",
|
||||
"privacy.unlisted.long": "Sjónligur fyri øll, men ikki gjøgnum uppdagingarhentleikarnar",
|
||||
"privacy.unlisted.short": "Ikki listað",
|
||||
"privacy.unlisted.additional": "Hetta er júst sum almenn, tó verður posturin ikki vístur í samtíðarrásum ella frámerkjum, rannsakan ella Mastodon leitingum, sjálvt um valið er galdandi fyri alla kontuna.",
|
||||
"privacy.unlisted.long": "Færri algoritmiskar fanfarur",
|
||||
"privacy.unlisted.short": "Stillur almenningur",
|
||||
"privacy_policy.last_updated": "Seinast dagført {date}",
|
||||
"privacy_policy.title": "Privatlívspolitikkur",
|
||||
"recommended": "Viðmælt",
|
||||
|
@ -551,7 +550,9 @@
|
|||
"relative_time.minutes": "{number}m",
|
||||
"relative_time.seconds": "{number}s",
|
||||
"relative_time.today": "í dag",
|
||||
"reply_indicator.attachments": "{count, plural, one {# viðfesti} other {# viðfesti}}",
|
||||
"reply_indicator.cancel": "Ógilda",
|
||||
"reply_indicator.poll": "Atkvøðugreiðsla",
|
||||
"report.block": "Blokera",
|
||||
"report.block_explanation": "Tú fer ikki at síggja postarnar hjá teimum. Tey kunnu ikki síggja tínar postar ella fylgja tær. Tey síggja, at tey eru blokeraði.",
|
||||
"report.categories.legal": "Løgfrøðisligt",
|
||||
|
@ -715,10 +716,8 @@
|
|||
"upload_error.poll": "Ikki loyvt at leggja fílur upp í spurnarkanningum.",
|
||||
"upload_form.audio_description": "Lýs fyri teimum, sum eru deyv ella hava ringa hoyrn",
|
||||
"upload_form.description": "Lýs fyri teimum, sum eru blind ella eru sjónveik",
|
||||
"upload_form.description_missing": "Lýsing vantar",
|
||||
"upload_form.edit": "Rætta",
|
||||
"upload_form.thumbnail": "Broyt smámynd",
|
||||
"upload_form.undo": "Strika",
|
||||
"upload_form.video_description": "Lýs fyri teimum, sum eru deyv, hava ringa hoyrn, eru blind ella eru sjónveik",
|
||||
"upload_modal.analyzing_picture": "Greini mynd…",
|
||||
"upload_modal.apply": "Ger virkið",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Annonce",
|
||||
"attachments_list.unprocessed": "(non traité)",
|
||||
"audio.hide": "Masquer l'audio",
|
||||
"autosuggest_hashtag.per_week": "{count} par semaine",
|
||||
"boost_modal.combo": "Vous pouvez appuyer sur {combo} pour sauter ceci la prochaine fois",
|
||||
"bundle_column_error.copy_stacktrace": "Copier le rapport d'erreur",
|
||||
"bundle_column_error.error.body": "La page demandée n'a pas pu être affichée. Cela pourrait être dû à un bogue dans notre code, ou à un problème de compatibilité avec le navigateur.",
|
||||
|
@ -146,22 +145,12 @@
|
|||
"compose_form.lock_disclaimer": "Votre compte n’est pas {locked}. Tout le monde peut vous suivre et voir vos publications privés.",
|
||||
"compose_form.lock_disclaimer.lock": "verrouillé",
|
||||
"compose_form.placeholder": "À quoi pensez-vous?",
|
||||
"compose_form.poll.add_option": "Ajouter un choix",
|
||||
"compose_form.poll.duration": "Durée du sondage",
|
||||
"compose_form.poll.option_placeholder": "Choix {number}",
|
||||
"compose_form.poll.remove_option": "Supprimer ce choix",
|
||||
"compose_form.poll.switch_to_multiple": "Changer le sondage pour autoriser plusieurs choix",
|
||||
"compose_form.poll.switch_to_single": "Changer le sondage pour n'autoriser qu'un seul choix",
|
||||
"compose_form.publish": "Publier",
|
||||
"compose_form.publish_form": "Publier",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Enregistrer les modifications",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Marquer média comme sensible} other {Marquer médias comme sensibles}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {Le média est marqué comme sensible} other {Les médias sont marqués comme sensibles}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Le média n’est pas marqué comme sensible} other {Les médias ne sont pas marqués comme sensibles}}",
|
||||
"compose_form.spoiler.marked": "Enlever l'avertissement de contenu",
|
||||
"compose_form.spoiler.unmarked": "Ajouter un avertissement de contenu",
|
||||
"compose_form.spoiler_placeholder": "Écrivez votre avertissement ici",
|
||||
"confirmation_modal.cancel": "Annuler",
|
||||
"confirmations.block.block_and_report": "Bloquer et signaler",
|
||||
"confirmations.block.confirm": "Bloquer",
|
||||
|
@ -408,7 +397,6 @@
|
|||
"navigation_bar.direct": "Mention privée",
|
||||
"navigation_bar.discover": "Découvrir",
|
||||
"navigation_bar.domain_blocks": "Domaines bloqués",
|
||||
"navigation_bar.edit_profile": "Modifier le profil",
|
||||
"navigation_bar.explore": "Explorer",
|
||||
"navigation_bar.favourites": "Favoris",
|
||||
"navigation_bar.filters": "Mots masqués",
|
||||
|
@ -526,14 +514,7 @@
|
|||
"poll_button.add_poll": "Ajouter un sondage",
|
||||
"poll_button.remove_poll": "Supprimer le sondage",
|
||||
"privacy.change": "Changer la confidentialité des messages",
|
||||
"privacy.direct.long": "Visible uniquement par les comptes mentionnés",
|
||||
"privacy.direct.short": "Personnes mentionnées uniquement",
|
||||
"privacy.private.long": "Visible uniquement pour vos abonné·e·s",
|
||||
"privacy.private.short": "Abonné·e·s seulement",
|
||||
"privacy.public.long": "Visible pour tous",
|
||||
"privacy.public.short": "Public",
|
||||
"privacy.unlisted.long": "Visible pour tous, mais sans fonctionnalités de découverte",
|
||||
"privacy.unlisted.short": "Non listé",
|
||||
"privacy_policy.last_updated": "Dernière mise à jour {date}",
|
||||
"privacy_policy.title": "Politique de confidentialité",
|
||||
"recommended": "Recommandé",
|
||||
|
@ -715,10 +696,8 @@
|
|||
"upload_error.poll": "L’envoi de fichiers n’est pas autorisé avec les sondages.",
|
||||
"upload_form.audio_description": "Décrire pour les personnes ayant des difficultés d’audition",
|
||||
"upload_form.description": "Décrire pour les malvoyants",
|
||||
"upload_form.description_missing": "Description manquante",
|
||||
"upload_form.edit": "Modifier",
|
||||
"upload_form.thumbnail": "Changer la vignette",
|
||||
"upload_form.undo": "Supprimer",
|
||||
"upload_form.video_description": "Décrire pour les personnes ayant des problèmes de vue ou d'audition",
|
||||
"upload_modal.analyzing_picture": "Analyse de l’image en cours…",
|
||||
"upload_modal.apply": "Appliquer",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Annonce",
|
||||
"attachments_list.unprocessed": "(non traité)",
|
||||
"audio.hide": "Masquer l'audio",
|
||||
"autosuggest_hashtag.per_week": "{count} par semaine",
|
||||
"boost_modal.combo": "Vous pouvez appuyer sur {combo} pour passer ceci la prochaine fois",
|
||||
"bundle_column_error.copy_stacktrace": "Copier le rapport d'erreur",
|
||||
"bundle_column_error.error.body": "La page demandée n'a pas pu être affichée. Cela peut être dû à un bogue dans notre code, ou à un problème de compatibilité avec le navigateur.",
|
||||
|
@ -146,22 +145,12 @@
|
|||
"compose_form.lock_disclaimer": "Votre compte n’est pas {locked}. Tout le monde peut vous suivre pour voir vos messages réservés à vos abonné⋅e⋅s.",
|
||||
"compose_form.lock_disclaimer.lock": "verrouillé",
|
||||
"compose_form.placeholder": "Qu’avez-vous en tête ?",
|
||||
"compose_form.poll.add_option": "Ajouter un choix",
|
||||
"compose_form.poll.duration": "Durée du sondage",
|
||||
"compose_form.poll.option_placeholder": "Choix {number}",
|
||||
"compose_form.poll.remove_option": "Supprimer ce choix",
|
||||
"compose_form.poll.switch_to_multiple": "Changer le sondage pour autoriser plusieurs choix",
|
||||
"compose_form.poll.switch_to_single": "Modifier le sondage pour autoriser qu'un seul choix",
|
||||
"compose_form.publish": "Publier",
|
||||
"compose_form.publish_form": "Nouvelle publication",
|
||||
"compose_form.publish_loud": "{publish} !",
|
||||
"compose_form.save_changes": "Enregistrer les modifications",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Marquer le média comme sensible} other {Marquer les médias comme sensibles}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {Le média est marqué comme sensible} other {Les médias sont marqués comme sensibles}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Le média n’est pas marqué comme sensible} other {Les médias ne sont pas marqués comme sensibles}}",
|
||||
"compose_form.spoiler.marked": "Enlever l’avertissement de contenu",
|
||||
"compose_form.spoiler.unmarked": "Ajouter un avertissement de contenu",
|
||||
"compose_form.spoiler_placeholder": "Écrivez votre avertissement ici",
|
||||
"confirmation_modal.cancel": "Annuler",
|
||||
"confirmations.block.block_and_report": "Bloquer et signaler",
|
||||
"confirmations.block.confirm": "Bloquer",
|
||||
|
@ -408,7 +397,6 @@
|
|||
"navigation_bar.direct": "Mention privée",
|
||||
"navigation_bar.discover": "Découvrir",
|
||||
"navigation_bar.domain_blocks": "Domaines bloqués",
|
||||
"navigation_bar.edit_profile": "Modifier le profil",
|
||||
"navigation_bar.explore": "Explorer",
|
||||
"navigation_bar.favourites": "Favoris",
|
||||
"navigation_bar.filters": "Mots masqués",
|
||||
|
@ -526,14 +514,7 @@
|
|||
"poll_button.add_poll": "Ajouter un sondage",
|
||||
"poll_button.remove_poll": "Supprimer le sondage",
|
||||
"privacy.change": "Ajuster la confidentialité du message",
|
||||
"privacy.direct.long": "Visible uniquement par les comptes mentionnés",
|
||||
"privacy.direct.short": "Personnes mentionnées uniquement",
|
||||
"privacy.private.long": "Visible uniquement par vos abonnés",
|
||||
"privacy.private.short": "Abonnés uniquement",
|
||||
"privacy.public.long": "Visible pour tous",
|
||||
"privacy.public.short": "Public",
|
||||
"privacy.unlisted.long": "Visible pour tous, mais sans fonctionnalités de découverte",
|
||||
"privacy.unlisted.short": "Non listé",
|
||||
"privacy_policy.last_updated": "Dernière mise à jour {date}",
|
||||
"privacy_policy.title": "Politique de confidentialité",
|
||||
"recommended": "Recommandé",
|
||||
|
@ -715,10 +696,8 @@
|
|||
"upload_error.poll": "L’envoi de fichiers n’est pas autorisé avec les sondages.",
|
||||
"upload_form.audio_description": "Décrire pour les personnes ayant des difficultés d’audition",
|
||||
"upload_form.description": "Décrire pour les malvoyant·e·s",
|
||||
"upload_form.description_missing": "Description manquante",
|
||||
"upload_form.edit": "Modifier",
|
||||
"upload_form.thumbnail": "Changer la vignette",
|
||||
"upload_form.undo": "Supprimer",
|
||||
"upload_form.video_description": "Décrire pour les personnes ayant des problèmes de vue ou d'audition",
|
||||
"upload_modal.analyzing_picture": "Analyse de l’image en cours…",
|
||||
"upload_modal.apply": "Appliquer",
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
"announcement.announcement": "Oankundiging",
|
||||
"attachments_list.unprocessed": "(net ferwurke)",
|
||||
"audio.hide": "Audio ferstopje",
|
||||
"autosuggest_hashtag.per_week": "{count} yn ’e wike",
|
||||
"boost_modal.combo": "Jo kinne op {combo} drukke om dit de folgjende kear oer te slaan",
|
||||
"bundle_column_error.copy_stacktrace": "Flaterrapport kopiearje",
|
||||
"bundle_column_error.error.body": "De opfrege side koe net werjûn wurde. It kin wêze troch in flater yn ús koade, of in probleem mei browserkompatibiliteit.",
|
||||
|
@ -146,22 +145,12 @@
|
|||
"compose_form.lock_disclaimer": "Jo account is net {locked}. Elkenien kin jo folgje en kin de berjochten sjen dy’t jo allinnich oan jo folgers rjochte hawwe.",
|
||||
"compose_form.lock_disclaimer.lock": "beskoattele",
|
||||
"compose_form.placeholder": "Wat wolle jo kwyt?",
|
||||
"compose_form.poll.add_option": "Kar tafoegje",
|
||||
"compose_form.poll.duration": "Doer fan de enkête",
|
||||
"compose_form.poll.option_placeholder": "Kar {number}",
|
||||
"compose_form.poll.remove_option": "Dizze kar fuortsmite",
|
||||
"compose_form.poll.switch_to_multiple": "Enkête wizigje om meardere karren ta te stean",
|
||||
"compose_form.poll.switch_to_single": "Enkête wizigje om in inkelde kar ta te stean",
|
||||
"compose_form.publish": "Publisearje",
|
||||
"compose_form.publish_form": "Publisearje",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.save_changes": "Wizigingen bewarje",
|
||||
"compose_form.sensitive.hide": "{count, plural, one {Media as gefoelich markearje} other {Media as gefoelich markearje}}",
|
||||
"compose_form.sensitive.marked": "{count, plural, one {Media as gefoelich markearre} other {Media as gefoelich markearre}}",
|
||||
"compose_form.sensitive.unmarked": "{count, plural, one {Media is net as gefoelich markearre} other {Media is net as gefoelich markearre}}",
|
||||
"compose_form.spoiler.marked": "Ynhâldswarskôging fuortsmite",
|
||||
"compose_form.spoiler.unmarked": "Ynhâldswarskôging tafoegje",
|
||||
"compose_form.spoiler_placeholder": "Warskôgingstekst",
|
||||
"confirmation_modal.cancel": "Annulearje",
|
||||
"confirmations.block.block_and_report": "Blokkearje en rapportearje",
|
||||
"confirmations.block.confirm": "Blokkearje",
|
||||
|
@ -408,7 +397,6 @@
|
|||
"navigation_bar.direct": "Priveefermeldingen",
|
||||
"navigation_bar.discover": "Untdekke",
|
||||
"navigation_bar.domain_blocks": "Blokkearre domeinen",
|
||||
"navigation_bar.edit_profile": "Profyl bewurkje",
|
||||
"navigation_bar.explore": "Ferkenne",
|
||||
"navigation_bar.favourites": "Favoriten",
|
||||
"navigation_bar.filters": "Negearre wurden",
|
||||
|
@ -526,14 +514,7 @@
|
|||
"poll_button.add_poll": "Poll tafoegje",
|
||||
"poll_button.remove_poll": "Enkête fuortsmite",
|
||||
"privacy.change": "Sichtberheid fan berjocht oanpasse",
|
||||
"privacy.direct.long": "Allinnich sichtber foar fermelde brûkers",
|
||||
"privacy.direct.short": "Allinnich fermelde minsken",
|
||||
"privacy.private.long": "Allinnich sichtber foar folgers",
|
||||
"privacy.private.short": "Allinnich folgers",
|
||||
"privacy.public.long": "Sichtber foar elkenien",
|
||||
"privacy.public.short": "Iepenbier",
|
||||
"privacy.unlisted.long": "Foar elkenien sichtber, mar net ûnder trends, hashtags en op iepenbiere tiidlinen",
|
||||
"privacy.unlisted.short": "Minder iepenbier",
|
||||
"privacy_policy.last_updated": "Lêst bywurke op {date}",
|
||||
"privacy_policy.title": "Privacybelied",
|
||||
"recommended": "Oanrekommandearre",
|
||||
|
@ -715,10 +696,8 @@
|
|||
"upload_error.poll": "It opladen fan bestannen is yn enkêten net tastien.",
|
||||
"upload_form.audio_description": "Describe for people with hearing loss",
|
||||
"upload_form.description": "Describe for the visually impaired",
|
||||
"upload_form.description_missing": "Gjin omskriuwing tafoege",
|
||||
"upload_form.edit": "Bewurkje",
|
||||
"upload_form.thumbnail": "Miniatuerôfbylding wizigje",
|
||||
"upload_form.undo": "Fuortsmite",
|
||||
"upload_form.video_description": "Describe for people with hearing loss or visual impairment",
|
||||
"upload_modal.analyzing_picture": "Ofbylding analysearje…",
|
||||
"upload_modal.apply": "Tapasse",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue