diff --git a/.bundler-audit.yml b/.bundler-audit.yml deleted file mode 100644 index a457fc41e8..0000000000 --- a/.bundler-audit.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -ignore: - # Sidekiq security issue, fixes in the latest Sidekiq 7 but we can not upgrade. Will be fixed in Sidekiq 6.5.10 - - CVE-2023-26141 diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 20aecd71d6..0369521963 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -70,7 +70,7 @@ services: hard: -1 libretranslate: - image: libretranslate/libretranslate:v1.3.11 + image: libretranslate/libretranslate:v1.3.12 restart: unless-stopped volumes: - lt-data:/home/libretranslate/.local diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml index 12b991f762..bcf63bb59e 100644 --- a/.github/workflows/test-ruby.yml +++ b/.github/workflows/test-ruby.yml @@ -284,8 +284,8 @@ jobs: ports: - 6379:6379 - elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:7.17.13 + search: + image: ${{ matrix.search-image }} env: discovery.type: single-node xpack.security.enabled: false @@ -315,6 +315,11 @@ jobs: - '3.0' - '3.1' - '.ruby-version' + search-image: + - docker.elastic.co/elasticsearch/elasticsearch:7.17.13 + include: + - ruby-version: '.ruby-version' + search-image: docker.elastic.co/elasticsearch/elasticsearch:8.10.2 steps: - uses: actions/checkout@v4 diff --git a/.gitignore b/.gitignore index 2bc8b18c8f..cb442609a1 100644 --- a/.gitignore +++ b/.gitignore @@ -31,9 +31,6 @@ # Ignore Vagrant files .vagrant/ -# Ignore Capistrano customizations -/config/deploy/* - # Ignore IDE files .vscode/ .idea/ diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml index a839e3789f..bace35f112 100644 --- a/.haml-lint_todo.yml +++ b/.haml-lint_todo.yml @@ -1,13 +1,13 @@ # This configuration was generated by # `haml-lint --auto-gen-config` -# on 2023-07-20 09:47:50 -0400 using Haml-Lint version 0.48.0. +# on 2023-10-03 08:32:28 -0400 using Haml-Lint version 0.51.0. # The point is for the user to remove these configuration records # one by one as the lints are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of Haml-Lint, may require this file to be generated again. linters: - # Offense count: 951 + # Offense count: 944 LineLength: enabled: false @@ -15,7 +15,7 @@ linters: UnnecessaryStringOutput: enabled: false - # Offense count: 57 + # Offense count: 44 RuboCop: enabled: false @@ -27,23 +27,17 @@ linters: - 'app/views/admin/reports/show.html.haml' - 'app/views/disputes/strikes/show.html.haml' - # Offense count: 32 + # Offense count: 15 InstanceVariables: exclude: - 'app/views/admin/reports/_actions.html.haml' - - 'app/views/admin/roles/_form.html.haml' - - 'app/views/admin/webhooks/_form.html.haml' - 'app/views/auth/registrations/_status.html.haml' - 'app/views/auth/sessions/two_factor/_otp_authentication_form.html.haml' - - 'app/views/authorize_interactions/_post_follow_actions.html.haml' - - 'app/views/invites/_form.html.haml' - 'app/views/relationships/_account.html.haml' - - 'app/views/shared/_og.html.haml' - 'app/views/application/_sidebar.html.haml' - # Offense count: 3 + # Offense count: 2 IdNames: exclude: - - 'app/views/authorize_interactions/error.html.haml' - 'app/views/oauth/authorizations/error.html.haml' - 'app/views/shared/_error_messages.html.haml' diff --git a/.nvmrc b/.nvmrc index b1b396bcfa..fa69d015bd 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20.7 +20.8 diff --git a/.prettierignore b/.prettierignore index 91029f665d..305f0fd753 100644 --- a/.prettierignore +++ b/.prettierignore @@ -31,9 +31,6 @@ # Ignore Vagrant files .vagrant/ -# Ignore Capistrano customizations -/config/deploy/* - # Ignore IDE files .vscode/ .idea/ diff --git a/.rubocop.yml b/.rubocop.yml index ef40e95a2a..786a724f0c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -28,6 +28,7 @@ AllCops: - 'Vagrantfile' - 'vendor/**/*' - 'lib/json_ld/*' # Generated files + - 'lib/mastodon/migration_helpers.rb' # Vendored from GitLab - 'lib/templates/**/*' # Reason: Prefer Hashes without extreme indentation @@ -76,12 +77,6 @@ Metrics/AbcSize: - 'lib/mastodon/cli/*.rb' - db/*migrate/**/* -# Reason: -# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocknesting -Metrics/BlockNesting: - Exclude: - - 'lib/mastodon/cli/*.rb' - # Reason: Currently disabled in .rubocop_todo.yml # https://docs.rubocop.org/rubocop/cops_metrics.html#metricscyclomaticcomplexity Metrics/CyclomaticComplexity: diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 7f879c9bb8..a31e00d540 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -13,32 +13,6 @@ Bundler/OrderedGems: Exclude: - 'Gemfile' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, IndentationWidth. -# SupportedStyles: with_first_argument, with_fixed_indentation -Layout/ArgumentAlignment: - Exclude: - - 'config/initializers/cors.rb' - - 'config/initializers/session_store.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. -# SupportedHashRocketStyles: key, separator, table -# SupportedColonStyles: key, separator, table -# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit -Layout/HashAlignment: - Exclude: - - 'config/environments/production.rb' - - 'config/initializers/rack_attack.rb' - - 'config/routes.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowDoxygenCommentStyle, AllowGemfileRubyComment. -Layout/LeadingCommentSpace: - Exclude: - - 'config/application.rb' - - 'config/initializers/3_omniauth.rb' - # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns. # URISchemes: http, https @@ -46,14 +20,6 @@ Layout/LineLength: Exclude: - 'app/models/account.rb' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: require_no_space, require_space -Layout/SpaceInLambdaLiteral: - Exclude: - - 'config/environments/production.rb' - - 'config/initializers/content_security_policy.rb' - # Configuration parameters: AllowComments, AllowEmptyLambdas. Lint/EmptyBlock: Exclude: @@ -289,10 +255,6 @@ RSpec/MultipleMemoizedHelpers: RSpec/NestedGroups: Max: 6 -RSpec/PendingWithoutReason: - Exclude: - - 'spec/models/account_spec.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). Rails/ApplicationController: Exclude: @@ -848,6 +810,5 @@ Style/TrailingCommaInHashLiteral: Style/WordArray: Exclude: - 'app/helpers/languages_helper.rb' - - 'config/initializers/cors.rb' - 'spec/controllers/settings/imports_controller_spec.rb' - 'spec/models/form/import_spec.rb' diff --git a/CHANGELOG.md b/CHANGELOG.md index b9834dcaef..6eebe5c85d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. -## [4.2.1] - UNRELEASED +## [4.2.1] - 2023-10-10 ### Added @@ -16,6 +16,11 @@ All notable changes to this project will be documented in this file. ### Fixed +- Fix clicking on already-opened thread post scrolling to the top of the thread ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27331), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27338), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27350)) +- Fix some remote posts getting truncated ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27307)) +- Fix some cases of infinite scroll code trying to fetch inaccessible posts in a loop ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27286)) +- Fix `Vary` headers not being set on some redirects ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27272)) +- Fix mentions being matched in some URL query strings ([mjankowski](https://github.com/mastodon/mastodon/pull/25656)) - Fix unexpected linebreak in version string in the Web UI ([vmstan](https://github.com/mastodon/mastodon/pull/26986)) - Fix double scroll bars in some columns in advanced interface ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27187)) - Fix boosts of local users being filtered in account timelines ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27204)) @@ -32,7 +37,7 @@ All notable changes to this project will be documented in this file. - Fix retention dashboard not displaying correct month ([vmstan](https://github.com/mastodon/mastodon/pull/27180)) - Fix tIME chunk not being properly removed from PNG uploads ([TheEssem](https://github.com/mastodon/mastodon/pull/27111)) - Fix division by zero in video in bitrate computation code ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27129)) -- Fix inefficient queries in “Follows and followers” as well as several admin pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27116)) +- Fix inefficient queries in “Follows and followers” as well as several admin pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27116), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27306)) - Fix ActiveRecord using two connection pools when no replica is defined ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27061)) - Fix the search documentation URL in system checks ([renchap](https://github.com/mastodon/mastodon/pull/27036)) diff --git a/Capfile b/Capfile deleted file mode 100644 index 86efa5bacf..0000000000 --- a/Capfile +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -require 'capistrano/setup' -require 'capistrano/deploy' -require 'capistrano/scm/git' - -install_plugin Capistrano::SCM::Git - -require 'capistrano/rbenv' -require 'capistrano/bundler' -require 'capistrano/yarn' -require 'capistrano/rails/assets' -require 'capistrano/rails/migrations' - -Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r } diff --git a/Dockerfile b/Dockerfile index 4d397e3bdf..8e40091add 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.4 # This needs to be bookworm-slim because the Ruby image is built on bookworm-slim -ARG NODE_VERSION="20.7-bookworm-slim" +ARG NODE_VERSION="20.8-bookworm-slim" FROM ghcr.io/moritzheiber/ruby-jemalloc:3.2.2-slim as ruby FROM node:${NODE_VERSION} as build diff --git a/Gemfile b/Gemfile index 18452b1ce9..cb02c03a74 100644 --- a/Gemfile +++ b/Gemfile @@ -172,12 +172,6 @@ group :development do # Linter CLI for HAML files gem 'haml_lint', require: false - # Deployment automation - gem 'capistrano', '~> 3.17' - gem 'capistrano-rails', '~> 1.6' - gem 'capistrano-rbenv', '~> 2.2' - gem 'capistrano-yarn', '~> 2.0' - # Validate missing i18n keys gem 'i18n-tasks', '~> 1.0', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index e7e4348960..ec52cb2c4b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -84,9 +84,9 @@ GEM erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - active_model_serializers (0.10.13) - actionpack (>= 4.1, < 7.1) - activemodel (>= 4.1, < 7.1) + active_model_serializers (0.10.14) + actionpack (>= 4.1) + activemodel (>= 4.1) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) activejob (7.0.8) @@ -112,8 +112,6 @@ GEM addressable (2.8.5) public_suffix (>= 2.0.2, < 6.0) aes_key_wrap (1.1.0) - airbrussh (1.4.1) - sshkit (>= 1.6.1, != 1.7.0) android_key_attestation (0.3.0) annotate (3.2.0) activerecord (>= 3.2, < 8.0) @@ -175,21 +173,6 @@ GEM bundler-audit (0.9.1) bundler (>= 1.2.0, < 3) thor (~> 1.0) - capistrano (3.17.3) - airbrussh (>= 1.0.0) - i18n - rake (>= 10.0.0) - sshkit (>= 1.9.0) - capistrano-bundler (2.1.0) - capistrano (~> 3.1) - capistrano-rails (1.6.3) - capistrano (~> 3.1) - capistrano-bundler (>= 1.1, < 3) - capistrano-rbenv (2.2.0) - capistrano (~> 3.1) - sshkit (~> 1.3) - capistrano-yarn (2.0.2) - capistrano (~> 3.0) capybara (3.39.2) addressable matrix @@ -324,7 +307,7 @@ GEM ruby-progressbar (~> 1.4) globalid (1.1.0) activesupport (>= 5.0) - haml (6.1.2) + haml (6.2.0) temple (>= 0.8.2) thor tilt @@ -333,8 +316,8 @@ GEM activesupport (>= 5.1) haml (>= 4.0.6) railties (>= 5.1) - haml_lint (0.50.0) - haml (>= 4.0, < 6.2) + haml_lint (0.51.0) + haml (>= 4.0) parallel (~> 1.10) rainbow rubocop (>= 1.0) @@ -473,11 +456,8 @@ GEM net-protocol net-protocol (0.2.1) timeout - net-scp (4.0.0) - net-ssh (>= 2.6.5, < 8.0.0) net-smtp (0.3.3) net-protocol - net-ssh (7.1.0) nio4r (2.5.9) nokogiri (1.15.4) mini_portile2 (~> 2.8.2) @@ -642,7 +622,7 @@ GEM sidekiq (>= 5, < 8) rspec-support (3.12.1) rspec_chunked (0.6) - rubocop (1.56.3) + rubocop (1.56.4) base64 (~> 0.1.1) json (~> 2.3) language_server-protocol (>= 3.17.0) @@ -693,7 +673,7 @@ GEM rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) semantic_range (3.0.0) - sidekiq (6.5.9) + sidekiq (6.5.10) connection_pool (>= 2.2.5, < 3) rack (~> 2.0) redis (>= 4.5.0, < 5) @@ -728,9 +708,6 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) - sshkit (1.21.5) - net-scp (>= 1.1.2) - net-ssh (>= 2.8.0) stackprof (0.2.25) statsd-ruby (1.5.0) stoplight (3.0.2) @@ -749,7 +726,7 @@ GEM climate_control (>= 0.0.3, < 1.0) test-prof (1.2.3) thor (1.2.2) - tilt (2.2.0) + tilt (2.3.0) timeout (0.4.0) tpm-key_attestation (0.12.0) bindata (~> 2.4) @@ -775,7 +752,7 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.8.2) - unicode-display_width (2.4.2) + unicode-display_width (2.5.0) uri (0.12.2) validate_email (0.1.6) activemodel (>= 3.0) @@ -831,10 +808,6 @@ DEPENDENCIES brakeman (~> 6.0) browser bundler-audit (~> 0.9) - capistrano (~> 3.17) - capistrano-rails (~> 1.6) - capistrano-rbenv (~> 2.2) - capistrano-yarn (~> 2.0) capybara (~> 3.39) charlock_holmes (~> 0.7.7) chewy (~> 7.3) diff --git a/SECURITY.md b/SECURITY.md index 9a08c4e251..3e13377db6 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -15,6 +15,7 @@ A "vulnerability in Mastodon" is a vulnerability in the code distributed through | Version | Supported | | ------- | ---------------- | +| 4.2.x | Yes | | 4.1.x | Yes | | 4.0.x | Until 2023-10-31 | | 3.5.x | Until 2023-12-31 | diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb index c4b7e9c9d2..ffccf7a28e 100644 --- a/app/controllers/about_controller.rb +++ b/app/controllers/about_controller.rb @@ -5,15 +5,7 @@ class AboutController < ApplicationController skip_before_action :require_functional! - before_action :set_instance_presenter - def show expires_in(15.seconds, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day) unless user_signed_in? end - - private - - def set_instance_presenter - @instance_presenter = InstancePresenter.new - end end diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb index c91b9b7163..edacbd5adc 100644 --- a/app/controllers/admin/domain_blocks_controller.rb +++ b/app/controllers/admin/domain_blocks_controller.rb @@ -89,17 +89,17 @@ module Admin def update_params params.require(:domain_block).permit(:severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, :reject_send_sensitive, :reject_hashtag, - :reject_straight_follow, :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous) + :reject_straight_follow, :reject_new_follow, :reject_friend, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous) end def resource_params params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, :reject_send_sensitive, :reject_hashtag, - :reject_straight_follow, :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous) + :reject_straight_follow, :reject_new_follow, :reject_friend, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous) end def form_domain_block_batch_params params.require(:form_domain_block_batch).permit(domain_blocks_attributes: [:enabled, :domain, :severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, - :reject_send_sensitive, :reject_hashtag, :reject_straight_follow, :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous]) + :reject_send_sensitive, :reject_hashtag, :reject_straight_follow, :reject_new_follow, :reject_friend, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous]) end def action_from_button diff --git a/app/controllers/admin/friend_servers_controller.rb b/app/controllers/admin/friend_servers_controller.rb new file mode 100644 index 0000000000..729d3b3912 --- /dev/null +++ b/app/controllers/admin/friend_servers_controller.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +module Admin + class FriendServersController < BaseController + before_action :set_friend, except: [:index, :new, :create] + before_action :warn_signatures_not_enabled!, only: [:new, :edit, :create, :follow, :unfollow, :accept, :reject] + + def index + authorize :friend_server, :update? + @friends = FriendDomain.all + end + + def new + authorize :friend_server, :update? + @friend = FriendDomain.new + end + + def edit + authorize :friend_server, :update? + end + + def create + authorize :friend_server, :update? + + @friend = FriendDomain.new(resource_params) + + if @friend.save + @friend.follow! + redirect_to admin_friend_servers_path + else + render action: :new + end + end + + def update + authorize :friend_server, :update? + + if @friend.update(update_resource_params) + redirect_to admin_friend_servers_path + else + render action: :edit + end + end + + def destroy + authorize :friend_server, :update? + @friend.destroy + redirect_to admin_friend_servers_path + end + + def follow + authorize :friend_server, :update? + @friend.follow! + render action: :edit + end + + def unfollow + authorize :friend_server, :update? + @friend.unfollow! + render action: :edit + end + + def accept + authorize :friend_server, :update? + @friend.accept! + render action: :edit + end + + def reject + authorize :friend_server, :update? + @friend.reject! + render action: :edit + end + + private + + def set_friend + @friend = FriendDomain.find(params[:id]) + end + + def resource_params + params.require(:friend_domain).permit(:domain, :inbox_url, :available, :pseudo_relay, :delivery_local, :unlocked, :allow_all_posts) + end + + def update_resource_params + params.require(:friend_domain).permit(:inbox_url, :available, :pseudo_relay, :delivery_local, :unlocked, :allow_all_posts) + end + + def warn_signatures_not_enabled! + flash.now[:error] = I18n.t('admin.relays.signatures_not_enabled') if authorized_fetch_mode? + end + end +end diff --git a/app/controllers/api/v1/admin/domain_blocks_controller.rb b/app/controllers/api/v1/admin/domain_blocks_controller.rb index bd0660dbaa..e157ed1e1f 100644 --- a/app/controllers/api/v1/admin/domain_blocks_controller.rb +++ b/app/controllers/api/v1/admin/domain_blocks_controller.rb @@ -70,7 +70,7 @@ class Api::V1::Admin::DomainBlocksController < Api::BaseController def domain_block_params params.permit(:severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_reports, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, :reject_send_sensitive, :reject_hashtag, :reject_straight_follow, - :reject_new_follow, :detect_invalid_subscription, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous) + :reject_new_follow, :reject_friend, :detect_invalid_subscription, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous) end def insert_pagination_headers @@ -103,6 +103,6 @@ class Api::V1::Admin::DomainBlocksController < Api::BaseController def resource_params params.permit(:domain, :severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, :reject_send_sensitive, :reject_hashtag, :reject_straight_follow, - :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous) + :reject_new_follow, :reject_friend, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous) end end diff --git a/app/controllers/api/v1/filters_controller.rb b/app/controllers/api/v1/filters_controller.rb index 3b097a3478..4345b61ac7 100644 --- a/app/controllers/api/v1/filters_controller.rb +++ b/app/controllers/api/v1/filters_controller.rb @@ -52,11 +52,11 @@ class Api::V1::FiltersController < Api::BaseController end def resource_params - params.permit(:phrase, :expires_in, :irreversible, :exclude_follows, :exclude_localusers, :whole_word, context: []) + params.permit(:phrase, :expires_in, :irreversible, :exclude_follows, :exclude_localusers, :with_quote, :whole_word, context: []) end def filter_params - resource_params.slice(:phrase, :expires_in, :irreversible, :exclude_follows, :exclude_localusers, :context) + resource_params.slice(:phrase, :expires_in, :irreversible, :exclude_follows, :exclude_localusers, :with_quote, :context) end def keyword_params diff --git a/app/controllers/api/v1/statuses/emoji_reactions_controller.rb b/app/controllers/api/v1/statuses/emoji_reactions_controller.rb index 4dc4bd92c8..f437576d1b 100644 --- a/app/controllers/api/v1/statuses/emoji_reactions_controller.rb +++ b/app/controllers/api/v1/statuses/emoji_reactions_controller.rb @@ -31,7 +31,7 @@ class Api::V1::Statuses::EmojiReactionsController < Api::BaseController end render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new( - [@status], current_account.id, emoji_reactions_map: { @status.id => false } + [@status], current_account.id ) rescue Mastodon::NotPermittedError not_found diff --git a/app/controllers/api/v2/filters_controller.rb b/app/controllers/api/v2/filters_controller.rb index f3e9938d8c..5e39d77416 100644 --- a/app/controllers/api/v2/filters_controller.rb +++ b/app/controllers/api/v2/filters_controller.rb @@ -43,6 +43,6 @@ class Api::V2::FiltersController < Api::BaseController end def resource_params - params.permit(:title, :expires_in, :filter_action, :exclude_follows, :exclude_localusers, context: [], keywords_attributes: [:id, :keyword, :whole_word, :_destroy]) + params.permit(:title, :expires_in, :filter_action, :exclude_follows, :exclude_localusers, :with_quote, context: [], keywords_attributes: [:id, :keyword, :whole_word, :_destroy]) end end diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index e70ae5b1b8..b0f2a02aa3 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -10,7 +10,6 @@ class Auth::RegistrationsController < Devise::RegistrationsController before_action :configure_sign_up_params, only: [:create] before_action :set_sessions, only: [:edit, :update] before_action :set_strikes, only: [:edit, :update] - before_action :set_instance_presenter, only: [:new, :create, :update] before_action :set_body_classes, only: [:new, :create, :edit, :update] before_action :require_not_suspended!, only: [:update] before_action :set_cache_headers, only: [:edit, :update] @@ -107,10 +106,6 @@ class Auth::RegistrationsController < Devise::RegistrationsController private - def set_instance_presenter - @instance_presenter = InstancePresenter.new - end - def set_body_classes @body_classes = %w(edit update).include?(action_name) ? 'admin' : 'lighter' end diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index 06a3deee2b..5327192b81 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -11,7 +11,6 @@ class Auth::SessionsController < Devise::SessionsController include TwoFactorAuthenticationConcern - before_action :set_instance_presenter, only: [:new] before_action :set_body_classes content_security_policy only: :new do |p| @@ -99,10 +98,6 @@ class Auth::SessionsController < Devise::SessionsController private - def set_instance_presenter - @instance_presenter = InstancePresenter.new - end - def set_body_classes @body_classes = 'lighter' end diff --git a/app/controllers/concerns/account_controller_concern.rb b/app/controllers/concerns/account_controller_concern.rb index e9cff22ca8..d63bcc85c9 100644 --- a/app/controllers/concerns/account_controller_concern.rb +++ b/app/controllers/concerns/account_controller_concern.rb @@ -9,17 +9,11 @@ module AccountControllerConcern FOLLOW_PER_PAGE = 12 included do - before_action :set_instance_presenter - after_action :set_link_headers, if: -> { request.format.nil? || request.format == :html } end private - def set_instance_presenter - @instance_presenter = InstancePresenter.new - end - def set_link_headers response.headers['Link'] = LinkHeader.new( [ diff --git a/app/controllers/concerns/web_app_controller_concern.rb b/app/controllers/concerns/web_app_controller_concern.rb index 273d7344ca..5687d6e5b6 100644 --- a/app/controllers/concerns/web_app_controller_concern.rb +++ b/app/controllers/concerns/web_app_controller_concern.rb @@ -4,10 +4,10 @@ module WebAppControllerConcern extend ActiveSupport::Concern included do - prepend_before_action :redirect_unauthenticated_to_permalinks! - before_action :set_app_body_class - vary_by 'Accept, Accept-Language, Cookie' + + before_action :redirect_unauthenticated_to_permalinks! + before_action :set_app_body_class end def skip_csrf_meta_tags? @@ -22,7 +22,9 @@ module WebAppControllerConcern 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? - redirect_to(redirect_path) if redirect_path.present? + expires_in(15.seconds, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day) unless user_signed_in? + redirect_to(redirect_path) end end diff --git a/app/controllers/filters_controller.rb b/app/controllers/filters_controller.rb index b0b2168884..9549ae3500 100644 --- a/app/controllers/filters_controller.rb +++ b/app/controllers/filters_controller.rb @@ -49,7 +49,7 @@ class FiltersController < ApplicationController end def resource_params - params.require(:custom_filter).permit(:title, :expires_in, :filter_action, :exclude_follows, :exclude_localusers, context: [], keywords_attributes: [:id, :keyword, :whole_word, :_destroy]) + params.require(:custom_filter).permit(:title, :expires_in, :filter_action, :exclude_follows, :exclude_localusers, :with_quote, context: [], keywords_attributes: [:id, :keyword, :whole_word, :_destroy]) end def set_body_classes diff --git a/app/controllers/follower_accounts_controller.rb b/app/controllers/follower_accounts_controller.rb index d29fe17de1..44d90ec671 100644 --- a/app/controllers/follower_accounts_controller.rb +++ b/app/controllers/follower_accounts_controller.rb @@ -3,7 +3,6 @@ class FollowerAccountsController < ApplicationController include AccountControllerConcern include SignatureVerification - include WebAppControllerConcern vary_by -> { public_fetch_mode? ? 'Accept, Accept-Language, Cookie' : 'Accept, Accept-Language, Cookie, Signature' } diff --git a/app/controllers/following_accounts_controller.rb b/app/controllers/following_accounts_controller.rb index f732324174..87a702f711 100644 --- a/app/controllers/following_accounts_controller.rb +++ b/app/controllers/following_accounts_controller.rb @@ -3,7 +3,6 @@ class FollowingAccountsController < ApplicationController include AccountControllerConcern include SignatureVerification - include WebAppControllerConcern vary_by -> { public_fetch_mode? ? 'Accept, Accept-Language, Cookie' : 'Accept, Accept-Language, Cookie, Signature' } diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index ee940e6707..03aa3eb52a 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -3,15 +3,7 @@ class HomeController < ApplicationController include WebAppControllerConcern - before_action :set_instance_presenter - def index expires_in(15.seconds, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day) unless user_signed_in? end - - private - - def set_instance_presenter - @instance_presenter = InstancePresenter.new - end end diff --git a/app/controllers/privacy_controller.rb b/app/controllers/privacy_controller.rb index 070ee8a06a..860e7c77a0 100644 --- a/app/controllers/privacy_controller.rb +++ b/app/controllers/privacy_controller.rb @@ -5,15 +5,7 @@ class PrivacyController < ApplicationController skip_before_action :require_functional! - before_action :set_instance_presenter - def show expires_in(15.seconds, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day) unless user_signed_in? end - - private - - def set_instance_presenter - @instance_presenter = InstancePresenter.new - end end diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb index 50a8763b72..7d18aef132 100644 --- a/app/controllers/statuses_controller.rb +++ b/app/controllers/statuses_controller.rb @@ -10,7 +10,6 @@ class StatusesController < ApplicationController before_action :require_account_signature!, only: [:show, :activity], if: -> { request.format == :json && authorized_fetch_mode? } before_action :set_status - before_action :set_instance_presenter before_action :redirect_to_original, only: :show before_action :set_body_classes, only: :embed @@ -72,10 +71,6 @@ class StatusesController < ApplicationController not_found end - def set_instance_presenter - @instance_presenter = InstancePresenter.new - end - def redirect_to_original redirect_to(ActivityPub::TagManager.instance.url_for(@status.reblog), allow_other_host: true) if @status.reblog? end diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index 2007fe8462..b0bdbde956 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@ -14,7 +14,6 @@ class TagsController < ApplicationController before_action :set_local before_action :set_tag before_action :set_statuses, if: -> { request.format == :rss } - before_action :set_instance_presenter skip_before_action :require_functional!, unless: :limited_federation_mode? @@ -49,10 +48,6 @@ class TagsController < ApplicationController @statuses = cache_collection(TagFeed.new(@tag, nil, local: @local).get(limit_param), Status) end - def set_instance_presenter - @instance_presenter = InstancePresenter.new - end - def limit_param params[:limit].present? ? [params[:limit].to_i, PAGE_SIZE_MAX].min : PAGE_SIZE end diff --git a/app/helpers/admin/announcements_helper.rb b/app/helpers/admin/announcements_helper.rb new file mode 100644 index 0000000000..97abe8e011 --- /dev/null +++ b/app/helpers/admin/announcements_helper.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Admin::AnnouncementsHelper + def datetime_pattern + '[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}(:[0-9]{2}){1,2}' + end + + def datetime_placeholder + Time.zone.now.strftime('%FT%R') + end +end diff --git a/app/helpers/invites_helper.rb b/app/helpers/invites_helper.rb new file mode 100644 index 0000000000..c189061db0 --- /dev/null +++ b/app/helpers/invites_helper.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module InvitesHelper + def invites_max_uses_options + [1, 5, 10, 25, 50, 100] + end + + def invites_expires_options + [30.minutes, 1.hour, 6.hours, 12.hours, 1.day, 1.week] + end +end diff --git a/app/helpers/kmyblue_capabilities_helper.rb b/app/helpers/kmyblue_capabilities_helper.rb index c1a67b2627..8653a3f9da 100644 --- a/app/helpers/kmyblue_capabilities_helper.rb +++ b/app/helpers/kmyblue_capabilities_helper.rb @@ -17,7 +17,8 @@ module KmyblueCapabilitiesHelper :kmyblue_bookmark_category, :kmyblue_quote, :kmyblue_searchability_limited, - :kmyblue_visibility_public_unlisted, + :kmyblue_searchability_public_unlisted, + :kmyblue_circle_history, ] capabilities << :profile_search unless Chewy.enabled? @@ -25,6 +26,7 @@ module KmyblueCapabilitiesHelper capabilities << :emoji_reaction capabilities << :enable_wide_emoji_reaction end + capabilities << :kmyblue_visibility_public_unlisted if Setting.enable_public_unlisted_visibility capabilities end diff --git a/app/helpers/languages_helper.rb b/app/helpers/languages_helper.rb index a8c66552cf..ddb10aa25f 100644 --- a/app/helpers/languages_helper.rb +++ b/app/helpers/languages_helper.rb @@ -230,6 +230,24 @@ module LanguagesHelper 'sr-Latn': 'Srpski (latinica)', }.freeze + # Helper for self.sorted_locale_keys + private_class_method def self.locale_name_for_sorting(locale) + if locale.blank? || locale == 'und' + '000' + elsif (supported_locale = SUPPORTED_LOCALES[locale.to_sym]) + ASCIIFolding.new.fold(supported_locale[1]).downcase + elsif (regional_locale = REGIONAL_LOCALE_NAMES[locale.to_sym]) + ASCIIFolding.new.fold(regional_locale).downcase + else + locale + end + end + + # Sort locales by native name for dropdown menus + def self.sorted_locale_keys(locale_keys) + locale_keys.sort_by { |key, _| locale_name_for_sorting(key) } + end + def native_locale_name(locale) if locale.blank? || locale == 'und' I18n.t('generic.none') @@ -254,6 +272,7 @@ module LanguagesHelper def valid_locale_or_nil(str) return if str.blank? + return str if valid_locale?(str) code, = str.to_s.split(/[_-]/) # Strip out the region from e.g. en_US or ja-JP diff --git a/app/helpers/mascot_helper.rb b/app/helpers/mascot_helper.rb index 0124c74f19..8ee04383ec 100644 --- a/app/helpers/mascot_helper.rb +++ b/app/helpers/mascot_helper.rb @@ -5,8 +5,6 @@ module MascotHelper full_asset_url(instance_presenter.mascot&.file&.url || asset_pack_path('media/images/elephant_ui_plane.svg')) end - private - def instance_presenter @instance_presenter ||= InstancePresenter.new end diff --git a/app/helpers/routing_helper.rb b/app/helpers/routing_helper.rb index 0d5a8505a2..2fb9ce72cb 100644 --- a/app/helpers/routing_helper.rb +++ b/app/helpers/routing_helper.rb @@ -3,11 +3,12 @@ module RoutingHelper extend ActiveSupport::Concern - include Rails.application.routes.url_helpers include ActionView::Helpers::AssetTagHelper include Webpacker::Helper included do + include Rails.application.routes.url_helpers + def default_url_options ActionMailer::Base.default_url_options end diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb index 889ca7f402..fce36bf43e 100644 --- a/app/helpers/settings_helper.rb +++ b/app/helpers/settings_helper.rb @@ -2,7 +2,11 @@ module SettingsHelper def filterable_languages - LanguagesHelper::SUPPORTED_LOCALES.keys + LanguagesHelper.sorted_locale_keys(LanguagesHelper::SUPPORTED_LOCALES.keys) + end + + def ui_languages + LanguagesHelper.sorted_locale_keys(I18n.available_locales) end def session_device_icon(session) diff --git a/app/javascript/mastodon/actions/importer/index.js b/app/javascript/mastodon/actions/importer/index.js index 369be6b8fb..50b90ef655 100644 --- a/app/javascript/mastodon/actions/importer/index.js +++ b/app/javascript/mastodon/actions/importer/index.js @@ -80,6 +80,10 @@ export function importFetchedStatuses(statuses) { processStatus(status.reblog); } + if (status.quote && status.quote.id) { + processStatus(status.quote); + } + if (status.poll && status.poll.id) { pushUnique(polls, normalizePoll(status.poll, getState().getIn(['polls', status.poll.id]))); } diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index 3220118f3d..a376992b7e 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -85,6 +85,11 @@ export function normalizeStatus(status, normalOldStatus) { normalStatus.spoiler_text = normalOldStatus.get('spoiler_text'); normalStatus.hidden = normalOldStatus.get('hidden'); + // for quoted post + if (!normalStatus.filtered && normalOldStatus.get('filtered')) { + normalStatus.filtered = normalOldStatus.get('filtered'); + } + if (normalOldStatus.get('translation')) { normalStatus.translation = normalOldStatus.get('translation'); } diff --git a/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx b/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx index 1856b7109e..b7225fc92e 100644 --- a/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx +++ b/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx @@ -45,6 +45,21 @@ describe('computeHashtagBarForStatus', () => { ); }); + it('does not truncate the contents when the last child is a text node', () => { + const status = createStatus( + 'this is a #test. Some more text', + ['test'], + ); + + const { hashtagsInBar, statusContentProps } = + computeHashtagBarForStatus(status); + + expect(hashtagsInBar).toEqual([]); + expect(statusContentProps.statusContent).toMatchInlineSnapshot( + `"this is a #test. Some more text"`, + ); + }); + it('extract tags from the last line', () => { const status = createStatus( '
Simple text
', diff --git a/app/javascript/mastodon/components/compacted_status.jsx b/app/javascript/mastodon/components/compacted_status.jsx new file mode 100644 index 0000000000..7ab5ee2025 --- /dev/null +++ b/app/javascript/mastodon/components/compacted_status.jsx @@ -0,0 +1,503 @@ +import PropTypes from 'prop-types'; + +import { injectIntl, defineMessages, FormattedMessage } from 'react-intl'; + +import classNames from 'classnames'; + +import ImmutablePropTypes from 'react-immutable-proptypes'; +import ImmutablePureComponent from 'react-immutable-pure-component'; + +import { HotKeys } from 'react-hotkeys'; + +import AttachmentList from 'mastodon/components/attachment_list'; +import { Icon } from 'mastodon/components/icon'; + +import Card from '../features/status/components/card'; +// We use the component (and not the container) since we do not want +// to use the progress bar to show download progress +import Bundle from '../features/ui/components/bundle'; +import { MediaGallery, Video, Audio } from '../features/ui/util/async-components'; +import { displayMedia } from '../initial_state'; + +import { Avatar } from './avatar'; +import { DisplayName } from './display_name'; +import { getHashtagBarForStatus } from './hashtag_bar'; +import { RelativeTimestamp } from './relative_timestamp'; +import StatusContent from './status_content'; + +const domParser = new DOMParser(); + +export const textForScreenReader = (intl, status, rebloggedByText = false) => { + const displayName = status.getIn(['account', 'display_name']); + + const spoilerText = status.getIn(['translation', 'spoiler_text']) || status.get('spoiler_text'); + const contentHtml = status.getIn(['translation', 'contentHtml']) || status.get('contentHtml'); + const contentText = domParser.parseFromString(contentHtml, 'text/html').documentElement.textContent; + + const values = [ + displayName.length === 0 ? status.getIn(['account', 'acct']).split('@')[0] : displayName, + spoilerText && status.get('hidden') ? spoilerText : contentText, + intl.formatDate(status.get('created_at'), { hour: '2-digit', minute: '2-digit', month: 'short', day: 'numeric' }), + status.getIn(['account', 'acct']), + ]; + + if (rebloggedByText) { + values.push(rebloggedByText); + } + + return values.join(', '); +}; + +export const defaultMediaVisibility = (status) => { + if (!status) { + return undefined; + } + + if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') { + status = status.get('reblog'); + } + + return (displayMedia !== 'hide_all' && !status.get('sensitive') || displayMedia === 'show_all'); +}; + +const messages = defineMessages({ + public_short: { id: 'privacy.public.short', defaultMessage: 'Public' }, + unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' }, + public_unlisted_short: { id: 'privacy.public_unlisted.short', defaultMessage: 'Public unlisted' }, + login_short: { id: 'privacy.login.short', defaultMessage: 'Login only' }, + private_short: { id: 'privacy.private.short', defaultMessage: 'Followers only' }, + limited_short: { id: 'privacy.limited.short', defaultMessage: 'Limited menbers only' }, + mutual_short: { id: 'privacy.mutual.short', defaultMessage: 'Mutual followers only' }, + circle_short: { id: 'privacy.circle.short', defaultMessage: 'Circle members only' }, + personal_short: { id: 'privacy.personal.short', defaultMessage: 'Yourself only' }, + direct_short: { id: 'privacy.direct.short', defaultMessage: 'Mentioned people only' }, + edited: { id: 'status.edited', defaultMessage: 'Edited {date}' }, +}); + +class CompactedStatus extends ImmutablePureComponent { + + static contextTypes = { + router: PropTypes.object, + }; + + static propTypes = { + status: ImmutablePropTypes.map, + previousId: PropTypes.string, + nextInReplyToId: PropTypes.string, + rootId: PropTypes.string, + onClick: PropTypes.func, + onOpenMedia: PropTypes.func, + onOpenVideo: PropTypes.func, + onHeightChange: PropTypes.func, + onToggleHidden: PropTypes.func, + onToggleCollapsed: PropTypes.func, + onTranslate: PropTypes.func, + onInteractionModal: PropTypes.func, + muted: PropTypes.bool, + hidden: PropTypes.bool, + unread: PropTypes.bool, + onMoveUp: PropTypes.func, + onMoveDown: PropTypes.func, + showThread: PropTypes.bool, + getScrollPosition: PropTypes.func, + updateScrollBottom: PropTypes.func, + cacheMediaWidth: PropTypes.func, + cachedMediaWidth: PropTypes.number, + }; + + // Avoid checking props that are functions (and whose equality will always + // evaluate to false. See react-immutable-pure-component for usage. + updateOnProps = [ + 'status', + 'muted', + 'hidden', + 'unread', + ]; + + state = { + showMedia: defaultMediaVisibility(this.props.status), + statusId: undefined, + forceFilter: undefined, + }; + + static getDerivedStateFromProps(nextProps, prevState) { + if (nextProps.status && nextProps.status.get('id') !== prevState.statusId) { + return { + showMedia: defaultMediaVisibility(nextProps.status), + statusId: nextProps.status.get('id'), + }; + } else { + return null; + } + } + + handleToggleMediaVisibility = () => { + this.setState({ showMedia: !this.state.showMedia }); + }; + + handleClick = e => { + if (e && (e.button !== 0 || e.ctrlKey || e.metaKey)) { + return; + } + + if (e) { + e.preventDefault(); + } + + this.handleHotkeyOpen(); + }; + + handlePrependAccountClick = e => { + this.handleAccountClick(e, false); + }; + + handleAccountClick = (e, proper = true) => { + if (e && (e.button !== 0 || e.ctrlKey || e.metaKey)) { + return; + } + + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + + this._openProfile(proper); + }; + + handleExpandedToggle = () => { + this.props.onToggleHidden(this._properStatus()); + }; + + handleCollapsedToggle = isCollapsed => { + this.props.onToggleCollapsed(this._properStatus(), isCollapsed); + }; + + handleTranslate = () => { + this.props.onTranslate(this._properStatus()); + }; + + getAttachmentAspectRatio () { + const attachments = this._properStatus().get('media_attachments'); + + if (attachments.getIn([0, 'type']) === 'video') { + return `${attachments.getIn([0, 'meta', 'original', 'width'])} / ${attachments.getIn([0, 'meta', 'original', 'height'])}`; + } else if (attachments.getIn([0, 'type']) === 'audio') { + return '16 / 9'; + } else { + return (attachments.size === 1 && attachments.getIn([0, 'meta', 'small', 'aspect'])) ? attachments.getIn([0, 'meta', 'small', 'aspect']) : '3 / 2' + } + } + + renderLoadingMediaGallery = () => { + return ( + + ); + }; + + renderLoadingVideoPlayer = () => { + return ( + + ); + }; + + renderLoadingAudioPlayer = () => { + return ( + + ); + }; + + handleOpenVideo = (options) => { + const status = this._properStatus(); + const lang = status.getIn(['translation', 'language']) || status.get('language'); + this.props.onOpenVideo(status.get('id'), status.getIn(['media_attachments', 0]), lang, options); + }; + + handleOpenMedia = (media, index) => { + const status = this._properStatus(); + const lang = status.getIn(['translation', 'language']) || status.get('language'); + this.props.onOpenMedia(status.get('id'), media, index, lang); + }; + + handleHotkeyOpenMedia = e => { + const { onOpenMedia, onOpenVideo } = this.props; + const status = this._properStatus(); + + e.preventDefault(); + + if (status.get('media_attachments').size > 0) { + const lang = status.getIn(['translation', 'language']) || status.get('language'); + if (status.getIn(['media_attachments', 0, 'type']) === 'video') { + onOpenVideo(status.get('id'), status.getIn(['media_attachments', 0]), lang, { startTime: 0 }); + } else { + onOpenMedia(status.get('id'), status.get('media_attachments'), 0, lang); + } + } + }; + + handleHotkeyOpen = () => { + if (this.props.onClick) { + this.props.onClick(); + return; + } + + const { router } = this.context; + const status = this._properStatus(); + + if (!router) { + return; + } + + router.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`); + }; + + handleHotkeyOpenProfile = () => { + this._openProfile(); + }; + + _openProfile = (proper = true) => { + const { router } = this.context; + const status = proper ? this._properStatus() : this.props.status; + + if (!router) { + return; + } + + router.history.push(`/@${status.getIn(['account', 'acct'])}`); + }; + + handleHotkeyMoveUp = e => { + this.props.onMoveUp(this.props.status.get('id'), e.target.getAttribute('data-featured')); + }; + + handleHotkeyMoveDown = e => { + this.props.onMoveDown(this.props.status.get('id'), e.target.getAttribute('data-featured')); + }; + + handleHotkeyToggleHidden = () => { + this.props.onToggleHidden(this._properStatus()); + }; + + handleHotkeyToggleSensitive = () => { + this.handleToggleMediaVisibility(); + }; + + _properStatus () { + const { status } = this.props; + + if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') { + return status.get('reblog'); + } else { + return status; + } + } + + handleRef = c => { + this.node = c; + }; + + render () { + const { intl, hidden, featured, unread, showThread, previousId, nextInReplyToId, rootId } = this.props; + + let { status } = this.props; + + if (status === null) { + return null; + } + + const handlers = this.props.muted ? {} : { + open: this.handleHotkeyOpen, + openProfile: this.handleHotkeyOpenProfile, + moveUp: this.handleHotkeyMoveUp, + moveDown: this.handleHotkeyMoveDown, + toggleHidden: this.handleHotkeyToggleHidden, + toggleSensitive: this.handleHotkeyToggleSensitive, + openMedia: this.handleHotkeyOpenMedia, + }; + + let media, isCardMediaWithSensitive, prepend, rebloggedByText; + + if (hidden) { + return ( +%{comment}' - private_comment_template: Importēts no %{source} %{date} + private_comment_template: Importēt no %{source} %{date} title: Importēt bloķētos domēnus invalid_domain_block: 'Viens vai vairāki domēna bloķi tika izlaisti šādas kļūdas(-u) dēļ: %{error}' new: @@ -1106,7 +1106,7 @@ lv: new_confirmation_instructions_sent: Pēc dažām minūtēm saņemsi jaunu e-pastu ar apstiprinājuma saiti! title: Pārbaudi savu iesūtni sign_in: - preamble_html: Pierakstieties ar saviem %{domain} akreditācijas datiem. Ja jūsu konts ir mitināts citā serverī, jūs nevarēsit pieteikties šeit. + preamble_html: Piesakies ar saviem %{domain} akreditācijas datiem. Ja tavs konts ir mitināts citā serverī, tu nevarēsi pieteikties šeit. title: Pierakstīties %{domain} sign_up: manual_review: Reģistrācijas domēnā %{domain} manuāli pārbauda mūsu moderatori. Lai palīdzētu mums apstrādāt tavu reģistrāciju, uzraksti mazliet par sevi un to, kāpēc vēlies kontu %{domain}. diff --git a/config/locales/si.yml b/config/locales/si.yml index 9349852df4..e9393e5c9a 100644 --- a/config/locales/si.yml +++ b/config/locales/si.yml @@ -1,75 +1,72 @@ si: about: - about_mastodon_html: 'අනාගත සමාජ ජාලය: දැන්වීම් නැත, ආයතනික නිරීක්ෂණ නැත, සදාචාරාත්මක සැලසුම් සහ විමධ්යගත කිරීම! Mastodon සමඟ ඔබේ දත්ත අයිති කරගන්න!' - contact_missing: සකස් කර නැත + about_mastodon_html: 'අනාගත සමාජ ජාලය: දැන්වීම් නැත, සංස්ථානික අවේක්ෂණ නැත, යහපතට නිර්මිතයි සහ විමධ්යගතයි! මාස්ටඩන් සමඟ ඔබගේ දත්ත අයිතිව තබාගන්න!' + contact_missing: සකසා නැත contact_unavailable: අ/නොවේ - hosted_on: Mastodon %{domain}හි සත්කාරකත්වය දරයි + hosted_on: "%{domain} හරහා සත්කාරකත්වය ලබයි" + title: පිළිබඳව accounts: follow: අනුගමනය followers: one: අනුගාමිකයා other: අනුගාමිකයින් - following: අනුගමනය + following: අනුගමන instance_actor_flash: මෙම ගිණුම සේවාදායකයම නියෝජනය කිරීමට භාවිතා කරන අතථ්ය නළුවෙකු වන අතර කිසිදු තනි පරිශීලකයෙකු නොවේ. එය ෆෙඩරේෂන් අරමුණු සඳහා භාවිතා කරන අතර අත්හිටුවිය යුතු නොවේ. last_active: අවසාන ක්රියාකාරී link_verified_on: මෙම සබැඳියේ හිමිකාරිත්වය %{date}හි පරීක්ෂා කරන ලදී - nothing_here: මෙහි කිසිත් නැත! - pin_errors: - following: ඔබට අනුමත කිරීමට අවශ්ය පුද්ගලයා ඔබ දැනටමත් අනුගමනය කරමින් සිටිය යුතුය + nothing_here: මෙහි කිසිවක් නැත! posts: one: ලිපිය other: ලිපි posts_tab_heading: ලිපි admin: account_actions: - action: ක්රියාව සිදු කරන්න - title: '%{acct}මත මධ්යස්ථ ක්රියාව සිදු කරන්න' + action: ක්රියාමාර්ගයක් ගන්න account_moderation_notes: - create: සටහන හැරයන්න - created_msg: මධ්යස්ථ සටහන සාර්ථකව සාදන ලදී! - destroyed_msg: මධ්යස්ථ සටහන සාර්ථකව විනාශ විය! + create: සටහනක් තබන්න accounts: add_email_domain_block: වි-තැපැල් වසම අවහිර කරන්න - approve: අනුමත කරන්න - approved_msg: '%{username}හි ලියාපදිංචි වීමේ යෙදුම සාර්ථකව අනුමත කරන ලදී' + approve: අනුමැතිය + approved_msg: "%{username}හි ලියාපදිංචි වීමේ යෙදුම සාර්ථකව අනුමත කරන ලදී" are_you_sure: ඔබට විශ්වාසද? - avatar: අවතාරය + avatar: ප්රතිරූපය by_domain: වසම change_email: + changed_msg: වි-තැපෑල සාර්ථකව වෙනස් විය! current_email: වත්මන් වි-තැපෑල label: වි-තැපෑල වෙනස් කරන්න new_email: නව විද්යුත් තැපෑල submit: වි-තැපෑල වෙනස් කරන්න - title: '%{username} සඳහා වි-තැපෑල වෙනස් කරන්න' + title: "%{username} සඳහා වි-තැපෑල වෙනස් කරන්න" + change_role: + changed_msg: භූමිකාව සාර්ථකව වෙනස් විය! + label: භූමිකාව වෙනස් කරන්න + no_role: නව භූමිකාව + title: "%{username} සඳහා භූමිකාව වෙනස් කරන්න" confirm: සනාථ කරන්න - confirmed: තහවුරු කර ඇත + confirmed: සනාථ කර ඇත confirming: සනාථ කරමින් custom: අභිරුචි delete: දත්ත මකන්න - deleted: මකා දමන ලදී - demote: පහත් කරන්න + deleted: මකා ඇත destroyed_msg: "%{username}හි දත්ත ඉක්මනින් මකා දැමීමට පෝලිම් කර ඇත" - disable: කැටි කරන්න disable_sign_in_token_auth: ඊමේල් ටෝකන් සත්යාපනය අක්රීය කරන්න disable_two_factor_authentication: 2FA අබල කරන්න - disabled: ශීත කළ display_name: ප්රදර්ශන නාමය domain: වසම edit: සංස්කරණය email: විද්යුත් තැපෑල email_status: වි-තැපෑලෙහි තත්වය - enable: කැටි කිරීම ඉවත් කරන්න enable_sign_in_token_auth: විද්යුත් තැපෑල ටෝකන් සත්යාපනය සබල කරන්න enabled: සබල කර ඇත enabled_msg: '%{username}ගේ ගිණුම සාර්ථකව අත්හිටුවා ඇත' followers: අනුගාමිකයින් - follows: පහත සඳහන් header: ශීර්ෂය inbox_url: එන ලිපි URL - invite_request_text: එක්වීම සඳහා + invite_request_text: එක්වීමට හේතුව invited_by: විසින් ආරාධනා කරන ලදී ip: අ.ජා. කෙ. (IP) - joined: එක් වී ඇත + joined: එක් වූ දිනය location: all: සියල්ල local: ස්ථානීය @@ -83,12 +80,13 @@ si: moderation: active: සක්රීයයි all: සියල්ල - pending: පොරොත්තුවෙන් + disabled: අබලයි + pending: පොරොත්තු suspended: අත්හිටුවන ලදි - title: මධ්යස්ථභාවය - moderation_notes: මධ්යස්ථ සටහන් + title: මැදිහත්කරණය + moderation_notes: මැදිහත්කරණ සටහන් most_recent_activity: වඩාත්ම මෑත ක්රියාකාරිත්වය - most_recent_ip: ඊට වඩා අ.ජා.කේ.(IP) + most_recent_ip: මෑත අ.ජා.කෙ. (IP) no_account_selected: කිසිවක් තෝරා නොගත් බැවින් ගිණුම් කිසිවක් වෙනස් කර නැත no_limits_imposed: සීමාවන් පනවා නැත not_subscribed: දායක වී නැත @@ -98,37 +96,36 @@ si: previous_strikes_description_html: one: මෙම ගිණුමට එක වර්ජනයක් ඇත. other: මෙම ගිණුමේ වර්ජන %{count} ඇත. - promote: ප්රවර්ධනය කරන්න protocol: කෙටුම්පත - public: ප්රසිද්ධ + public: ප්රසිද්ධ push_subscription_expires: පුෂ් දායකත්වය කල් ඉකුත් වේ - redownload: පැතිකඩ නැවුම්කරන්න + redownload: පැතිකඩ නැවුම් කරන්න redownloaded_msg: මූලාරම්භයේ සිට %{username}හි පැතිකඩ සාර්ථකව නැවුම් කරන ලදී reject: ප්රතික්ෂේප - rejected_msg: '%{username}හි ලියාපදිංචි වීමේ අයදුම්පත සාර්ථකව ප්රතික්ෂේප විය' - remove_avatar: අවතාරය ඉවත් කරන්න + rejected_msg: "%{username}හි ලියාපදිංචි වීමේ අයදුම්පත සාර්ථකව ප්රතික්ෂේප විය" + remove_avatar: ප්රතිරූපය ඉවත් කරන්න remove_header: ශීර්ෂය ඉවත්කරන්න removed_avatar_msg: '%{username}ගේ අවතාර රූපය සාර්ථකව ඉවත් කරන ලදී' removed_header_msg: '%{username}හි ශීර්ෂ රූපය සාර්ථකව ඉවත් කරන ලදී' resend_confirmation: already_confirmed: මෙම පරිශීලකයා දැනටමත් තහවුරු කර ඇත reset: නැවත සකසන්න - reset_password: මුරපදය නැවතසකසන්න + reset_password: මුරපදය යළි සකසන්න resubscribe: නැවත දායක වන්න + role: භූමිකාව search: සොයන්න search_same_email_domain: එකම විද්යුත් තැපැල් වසම සහිත වෙනත් පරිශීලකයන් search_same_ip: එකම IP සහිත වෙනත් පරිශීලකයන් + security: ආරක්ෂාව security_measures: only_password: මුරපදය පමණි password_and_2fa: මුරපදය සහ 2FA - sensitive: සංවේදී - sensitized: සංවේදී ලෙස සලකුණු කර ඇත + sensitized: සංවේදී බව සලකුණු කර ඇත shared_inbox_url: බෙදාගත් එන ලිපි URL show: created_reports: වාර්තා හැදුවා targeted_reports: වෙනත් අය විසින් වාර්තා කරන ලදී - silence: සීමාව - silenced: සීමාසහිත + silenced: සීමා සහිතයි statuses: ලිපි strikes: පෙර වැඩ වර්ජන subscribe: දායක වන්න @@ -145,27 +142,26 @@ si: undo_suspension: අත්හිටුවීම අහෝසි කරන්න unsilenced_msg: '%{username}ගිණුමේ සීමාව සාර්ථකව ඉවත් කරන ලදී' unsubscribe: දායක නොවන්න - unsuspended_msg: '%{username}ගිණුම සාර්ථකව අත්හිටුවන ලදී' - username: පරිශීලක නාමය + unsuspended_msg: "%{username}ගිණුම සාර්ථකව අත්හිටුවන ලදී" + username: පරිශ්රීලක නාමය view_domain: වසම සඳහා සාරාංශය බලන්න warn: අවවාද web: වියමන - whitelisted: ෆෙඩරේෂන් සඳහා අවසර ඇත + whitelisted: ඒකාබද්ධයට ඉඩ දී ඇත action_logs: action_types: approve_appeal: අභියාචනය අනුමත කරන්න approve_user: පරිශීලක අනුමත කරන්න assigned_to_self_report: වාර්තාව පැවරීම - change_email_user: පරිශීලකයින්ට වි-තැපෑල වෙනස් කරන්න confirm_user: පරිශීලක තහවුරු කරන්න create_account_warning: අවවාදයක් සාදන්න create_announcement: නිවේදනය සාදන්න create_custom_emoji: අභිරුචි ඉමොජි සාදන්න - create_domain_allow: වසම් ඉඩදීමක් සාදන්න - create_domain_block: වසම් අවහිරයක් සාදන්න + create_domain_allow: වසමකට ඉඩදීම සාදන්න create_email_domain_block: ඊමේල් ඩොමේන් බ්ලොක් එකක් සාදන්න create_ip_block: අ.ජා. කෙ. (IP) නීතියක් සාදන්න create_unavailable_domain: ලබා ගත නොහැකි වසම සාදන්න + create_user_role: භූමිකාව සාදන්න demote_user: පරිශීලකයා පහත් කරන්න destroy_announcement: නිවේදනය මකන්න destroy_custom_emoji: අභිරුචි ඉමොජි මකන්න @@ -174,26 +170,24 @@ si: destroy_email_domain_block: ඊමේල් ඩොමේන් බ්ලොක් එක මකන්න destroy_instance: වසම පිරිසිදු කරන්න destroy_ip_block: IP රීතිය මකන්න - destroy_status: පළ කිරීම මකන්න + destroy_status: ලිපිය මකන්න destroy_unavailable_domain: ලබා ගත නොහැකි වසම මකන්න disable_2fa_user: 2FA අබල කරන්න - disable_custom_emoji: අභිරුචි ඉමොජි අබල කරන්න + disable_custom_emoji: අභිරුචි ඉමෝජි අබල කරන්න disable_sign_in_token_auth_user: පරිශීලකයා සඳහා ඊමේල් ටෝකන් සත්යාපනය අක්රීය කරන්න - disable_user: පරිශීලනය කරන්න - enable_custom_emoji: අභිරුචි ඉමොජි සබල කරන්න + enable_custom_emoji: අභිරුචි ඉමෝජි සබල කරන්න enable_sign_in_token_auth_user: පරිශීලකයා සඳහා විද්යුත් තැපෑල ටෝකන් සත්යාපනය සක්රීය කරන්න enable_user: පරිශීලක සබල කරන්න memorialize_account: ගිණුම අනුස්මරණ කරන්න - promote_user: පරිශීලක ප්රවර්ධනය කරන්න reject_appeal: අභියාචනය ප්රතික්ෂේප කරන්න reject_user: පරිශීලක ප්රතික්ෂේප කරන්න - remove_avatar_user: Avatar ඉවත් කරන්න - reopen_report: වාර්තාව නැවත විවෘත කරන්න - reset_password_user: මුරපදය නැවතසකසන්න + remove_avatar_user: ප්රතිරූපය ඉවත් කරන්න + reopen_report: වාර්තාව නැවත අරින්න + reset_password_user: මුරපදය යළි සකසන්න resolve_report: වාර්තාව විසඳන්න sensitive_account: බල සංවේදී ගිණුම silence_account: ගිණුම සීමා කරන්න - suspend_account: සැලකිය යුතු + suspend_account: ගිණුම අත්හිටුවන්න unassigned_report: වාර්තාව පැවරීම ඉවත් කරන්න unblock_email_account: ඊමේල් ලිපිනය අවහිර කිරීම ඉවත් කරන්න unsensitive_account: බල සංවේදී ගිණුම අහෝසි කරන්න @@ -202,9 +196,8 @@ si: update_announcement: නිවේදනය යාවත්කාල කරන්න update_custom_emoji: අභිරුචි ඉමොජි යාවත්කාලීන කරන්න update_domain_block: ඩොමේන් බ්ලොක් යාවත්කාලීන කරන්න - update_status: පළ කිරීම යාවත්කාලීන කරන්න + update_status: ලිපිය යාවත්කාල කරන්න actions: - approve_appeal_html: "%{name} අනුමත මධ්යස්ථ තීරණ අභියාචනය %{target}සිට" approve_user_html: "%{name} අනුමත ලියාපදිංචිය %{target}සිට" assigned_to_self_report_html: "%{name} වාර්තාව %{target} තමන්ටම පවරා ඇත" change_email_user_html: "%{name} පරිශීලක %{target}ගේ ඊමේල් ලිපිනය වෙනස් කළේය" @@ -223,8 +216,7 @@ si: destroy_domain_block_html: "%{name} අවහිර නොකළ වසම %{target}" destroy_email_domain_block_html: "%{name} අවහිර නොකළ විද්යුත් තැපැල් වසම %{target}" destroy_instance_html: "%{name} පිරිසිදු කරන ලද වසම %{target}" - destroy_ip_block_html: "IP %{target}සඳහා %{name} මකා දැමූ රීතිය" - destroy_status_html: "%{name} පෝස්ට් %{target}විසින් ඉවත් කරන ලදී" + destroy_ip_block_html: IP %{target}සඳහා %{name} මකා දැමූ රීතිය destroy_unavailable_domain_html: "%{name} වසම %{target}වෙත බෙදා හැරීම නැවත ආරම්භ විය" disable_2fa_user_html: "%{name} පරිශීලක %{target}සඳහා සාධක දෙකක අවශ්යතාවය අක්රීය කර ඇත" disable_custom_emoji_html: "%{name} ආබාධිත ඉමොජි %{target}" @@ -235,27 +227,25 @@ si: enable_user_html: "පරිශීලක %{target}සඳහා %{name} සක්රීය පුරනය වීම" memorialize_account_html: "%{name} %{target}ගේ ගිණුම සිහිවටන පිටුවක් බවට පත් කළේය" promote_user_html: "%{name} උසස් පරිශීලක %{target}" - reject_appeal_html: "%{name} %{target}සිට මධ්යස්ථ තීරණ අභියාචනය ප්රතික්ෂේප කරන ලදී" reject_user_html: "%{name} %{target}සිට ලියාපදිංචි වීම ප්රතික්ෂේප විය" remove_avatar_user_html: "%{name} %{target}ගේ අවතාරය ඉවත් කරන ලදී" reopen_report_html: "%{name} නැවත විවෘත කළ වාර්තාව %{target}" reset_password_user_html: "%{name} පරිශීලක %{target}හි මුරපදය යළි පිහිටුවන්න" resolve_report_html: "%{name} විසඳන ලද වාර්තාව %{target}" - sensitive_account_html: "%{name} %{target}හි මාධ්ය සංවේදී ලෙස සලකුණු කර ඇත" + sensitive_account_html: "%{target}ගේ මාධ්ය සංවේදී බව %{name} සලකුණු කර ඇත" silence_account_html: "%{name} සීමිත %{target}ගිණුමක්" suspend_account_html: "%{name} %{target}ගේ ගිණුම අත්හිටුවා ඇත" unassigned_report_html: "%{name} පවරා නොදුන් වාර්තාව %{target}" unblock_email_account_html: "%{name} %{target}ගේ ඊමේල් ලිපිනය අවහිර කිරීම ඉවත් කරන ලදී" - unsensitive_account_html: "%{name} සලකුණු නොකළ %{target}ගේ මාධ්ය සංවේදී ලෙස" + unsensitive_account_html: "%{target}ගේ මාධ්ය සංවේදී බව %{name} ඉවත් කර ඇත" unsilence_account_html: "%{target}ගිණුමේ %{name} undid සීමාව" unsuspend_account_html: "%{name} අත්හිටුවන ලද %{target}ගිණුම" update_announcement_html: "%{name} යාවත්කාලීන නිවේදනය %{target}" update_custom_emoji_html: "%{name} යාවත්කාලීන කළ ඉමොජි %{target}" update_domain_block_html: "%{target}සඳහා %{name} යාවත්කාලීන කරන ලද වසම් වාරණ" - update_status_html: "%{name} %{target}යාවත්කාලීන කරන ලද පළ කිරීම" empty: ලඝු-සටහන් හමු නොවිණි. - filter_by_action: ක්රියාව අනුව පෙරන්න - filter_by_user: පරිශීලක අනුව පෙරන්න + filter_by_action: ක්රියාමාර්ගය අනුව පෙරන්න + filter_by_user: පරිශ්රීලකයා අනුව පෙරන්න title: විගණන සටහන announcements: destroyed_msg: නිවේදනය සාර්ථකව මකා ඇත! @@ -278,22 +268,22 @@ si: assign_category: කාණ්ඩය පැවරීම by_domain: වසම copied_msg: ඉමොජි වල දේශීය පිටපත සාර්ථකව සාදන ලදී - copy: පිටපත් + copy: පිටපතක් copy_failed_msg: එම ඉමොජියේ දේශීය පිටපතක් සෑදීමට නොහැකි විය create_new_category: නව ප්රවර්ගයක් සාදන්න created_msg: ඉමොජි සාර්ථකව නිර්මාණය කළා! delete: මකන්න destroyed_msg: Emojo සාර්ථකව විනාශ විය! disable: අබල කරන්න - disabled: අබල කර ඇත + disabled: අබලයි disabled_msg: එම ඉමොජිය සාර්ථකව අබල කරන ලදී - emoji: ඉමොජි + emoji: ඉමෝජි enable: සබල කරන්න - enabled: සබල කර ඇත + enabled: සබලයි enabled_msg: එම ඉමොජි සාර්ථකව සබල කරන ලදී image_hint: PNG හෝ GIF %{size}දක්වා list: ලැයිස්තුව - listed: ලැයිස්තුගත කර ඇත + listed: ලැයිස්තුගත new: title: නව අභිරුචි ඉමොජි එක් කරන්න not_permitted: මෙම ක්රියාව සිදු කිරීමට ඔබට අවසර නැත @@ -310,9 +300,9 @@ si: dashboard: active_users: ක්රියාකාරී පරිශීලකයන් interactions: අන්තර්ක්රියා - media_storage: මාධ්ය ගබඩාව - new_users: නව පරිශීලකයන් - opened_reports: වාර්තා විවෘත විය + media_storage: මාධ්ය ආචයනය + new_users: නව පරිශ්රීලකයින් + opened_reports: විවෘත වාර්තා pending_appeals_html: one: "%{count} අභියාචනයක් බලාපොරොත්තු වේ" other: "%{count} අභියාචනා පොරොත්තු" @@ -328,11 +318,11 @@ si: resolved_reports: වාර්තා විසඳා ඇත software: මෘදුකාංගය sources: ලියාපදිංචි මූලාශ්ර - space: අවකාශය භාවිතය + space: ඉඩ භාවිතය title: උපකරණ පුවරුව top_languages: ඉහළම ක්රියාකාරී භාෂා top_servers: ඉහළම ක්රියාකාරී සේවාදායකයන් - website: වෙබ් අඩවිය + website: අඩවිය disputes: appeals: empty: අභියාචනා හමු නොවීය. @@ -352,7 +342,6 @@ si: existing_domain_block_html: ඔබ දැනටමත් %{name}මත දැඩි සීමාවන් පනවා ඇත, ඔබට එය අවහිර කිරීම ඉවත් කිරීමට අවශ්යයි. new: create: බ්ලොක් එකක් සාදන්න - hint: ඩොමේන් බ්ලොක් එක දත්ත සමුදාය තුල ගිණුම් ඇතුලත් කිරීම් නිර්මාණය වීම වලක්වන්නේ නැත, නමුත් එම ගිණුම් වලට ප්රතික්රියාශීලීව සහ ස්වයංක්රීයව විශේෂිත මධ්යස්ථ ක්රම යොදනු ඇත. severity: noop: කිසිවක් නැත suspend: අත්හිටුවන්න @@ -360,7 +349,6 @@ si: obfuscate: අපැහැදිලි වසම් නාමය obfuscate_hint: වසම් සීමාවන් ලැයිස්තුව ප්රචාරණය කිරීම සබල කර ඇත්නම් ලැයිස්තුවේ වසම් නාමය අර්ධ වශයෙන් අපැහැදිලි කරන්න private_comment: පුද්ගලික අදහස - private_comment_hint: පරිපාලකයින් විසින් අභ්යන්තර භාවිතය සඳහා මෙම වසම් සීමාව ගැන අදහස් දක්වන්න. public_comment: ප්රසිද්ධ අදහස public_comment_hint: වසම් සීමාවන් ලැයිස්තුව ප්රචාරණය කිරීම සබල කර ඇත්නම්, සාමාන්ය ජනතාව සඳහා මෙම වසම් සීමාව ගැන අදහස් දක්වන්න. reject_media: මාධ්ය ගොනු ප්රතික්ෂේප කරන්න @@ -394,7 +382,7 @@ si: status: තත්වය suppress: අනුගමනය නිර්දේශය යටපත් කරන්න suppressed: යටපත් කළා - title: නිර්දේශ අනුගමනය කරන්න + title: අනුගමනයට නිර්දේශ unsuppress: නිර්දේශ පිළිපැදීම ප්රතිසාධනය කරන්න instances: availability: @@ -414,16 +402,16 @@ si: by_domain: වසම confirm_purge: ඔබට මෙම වසමෙන් දත්ත ස්ථිරවම මැකීමට අවශ්ය බව විශ්වාසද? content_policies: - comment: අභ්යන්තර සටහන + comment: අභ්යන්තර සටහන description_html: ඔබට මෙම වසම සහ එහි ඕනෑම උප වසමකින් සියලුම ගිණුම් වලට අදාළ වන අන්තර්ගත ප්රතිපත්ති නිර්වචනය කළ හැක. policies: reject_media: මාධ්ය ප්රතික්ෂේප කරන්න reject_reports: වාර්තා ප්රතික්ෂේප කරන්න silence: සීමාව suspend: අත්හිටුවන්න - policy: ප්රතිපත්ති + policy: ප්රතිපත්තිය reason: පොදු හේතුව - title: අන්තර්ගත ප්රතිපත්ති + title: අන්තර්ගත ප්රතිපත්ති dashboard: instance_accounts_dimension: වැඩිපුරම අනුගමනය කරන ගිණුම් instance_accounts_measure: ගබඩා කර ඇති ගිණුම් @@ -442,8 +430,8 @@ si: unavailable: ලබා ගත නොහැක delivery_available: බෙදා හැරීම ලබා ගත හැකිය delivery_error_days: බෙදා හැරීමේ දෝෂ සහිත දින - delivery_error_hint: දින %{count} ක් සඳහා බෙදා හැරීම කළ නොහැකි නම්, එය ස්වයංක්රීයව බෙදා හැරිය නොහැකි ලෙස ලකුණු කරනු ලැබේ. - destroyed_msg: '%{domain} සිට දත්ත දැන් ආසන්න මකාදැමීම සඳහා පෝලිම් කර ඇත.' + delivery_error_hint: දවස් %{count} කින් බාරදීමට නොහැකි වුවහොත්, බාරදීමට නොහැකි බව ස්වයංක්රීයව සලකුණු වේ. + destroyed_msg: "%{domain} සිට දත්ත දැන් ආසන්න මකාදැමීම සඳහා පෝලිම් කර ඇත." empty: වසම් කිසිවක් හමු නොවීය. known_accounts: one: "%{count} දන්නා ගිණුම්" @@ -451,7 +439,7 @@ si: moderation: all: සියල්ල limited: සීමා සහිතයි - title: මධ්යස්ථභාවය + title: මැදිහත්කරණය private_comment: පුද්ගලික අදහස public_comment: ප්රසිද්ධ අදහස purge: පිරිසිදු කරන්න @@ -461,13 +449,12 @@ si: total_followed_by_them: ඔවුන් විසින් අනුගමනය කරන ලදී total_followed_by_us: අප විසින් අනුගමනය කරන ලදී total_reported: ඔවුන් ගැන වාර්තා - total_storage: මාධ්ය ඇමුණුම් + total_storage: මාධ්ය ඇමුණුම් totals_time_period_hint_html: පහත දැක්වෙන එකතුවෙහි සියලු කාලය සඳහා දත්ත ඇතුළත් වේ. invites: deactivate_all: සියල්ල අක්රිය කරන්න filter: all: සියල්ල - available: පවතින expired: ඉකුත් වී ඇත title: පෙරහන title: ඇරයුම් @@ -491,15 +478,13 @@ si: relays: add_new: නව රිලේ එක් කරන්න delete: මකන්න - description_html: ෆෙඩරේෂන් රිලේ යනු එයට දායක වී ප්රකාශයට පත් කරන සේවාදායකයන් අතර විශාල ප්රසිද්ධ පළ කිරීම් හුවමාරු කරන අතරමැදි සේවාදායකයකි. එය කුඩා සහ මධ්යම සේවාදායකයන්ට fediverseවෙතින් අන්තර්ගතය සොයා ගැනීමට උදවු කළ හැකි අතර, එසේ නොමැති නම් දේශීය පරිශීලකයින්ට දුරස්ථ සේවාදායකයන් මත වෙනත් පුද්ගලයින් හස්තීයව අනුගමනය කිරීම අවශ්ය වේ. disable: අබල කරන්න - disabled: අබල කර ඇත + disabled: අබලයි enable: සබල කරන්න - enable_hint: සක්රිය කළ පසු, ඔබේ සේවාදායකය මෙම රිලේ වෙතින් සියලුම පොදු පළ කිරීම් සඳහා දායක වන අතර, මෙම සේවාදායකයේ පොදු පළ කිරීම් එයට යැවීම ආරම්භ කරනු ඇත. enabled: සබල කර ඇත inbox_url: රිලේ URL pending: රිලේ අනුමැතිය සඳහා රැඳී සිටිමින් - save_and_enable: සුරකින්න සහ සක්රිය කරන්න + save_and_enable: සුරකින්න හා සබල කරන්න setup: රිලේ සම්බන්ධතාවයක් සකසන්න signatures_not_enabled: ආරක්ෂිත මාදිලිය හෝ සීමිත ෆෙඩරේෂන් මාදිලිය සබල කර ඇති අතර රිලේ නිවැරදිව ක්රියා නොකරනු ඇත status: තත්වය @@ -515,41 +500,38 @@ si: action_log: විගණන සටහන action_taken_by: විසින් ගන්නා ලද පියවර actions: - delete_description_html: වාර්තා කරන ලද පළ කිරීම් මකා දැමෙනු ඇති අතර එම ගිණුමේම අනාගත උල්ලංඝනයන් තීව්ර කිරීමට ඔබට උදවු කිරීමට වර්ජනයක් වාර්තා කරනු ඇත. - mark_as_sensitive_description_html: වාර්තා කරන ලද පළ කිරීම් වල මාධ්ය සංවේදී ලෙස සලකුණු කරනු ලබන අතර එම ගිණුම මගින් අනාගත උල්ලංඝනයන් උත්සන්න කිරීමට ඔබට උපකාර කිරීමට වර්ජනයක් වාර්තා කරනු ඇත. other_description_html: ගිණුමේ හැසිරීම පාලනය කිරීම සහ වාර්තා කළ ගිණුමට සන්නිවේදනය අභිරුචිකරණය කිරීම සඳහා තවත් විකල්ප බලන්න. resolve_description_html: වාර්තා කරන ලද ගිණුමට එරෙහිව කිසිදු ක්රියාමාර්ගයක් නොගනු ඇත, වැඩ වර්ජනයක් වාර්තා නොකෙරේ, වාර්තාව වසා දමනු ඇත. actions_description_html: මෙම වාර්තාව විසඳීමට ගත යුතු ක්රියාමාර්ගය තීරණය කරන්න. ඔබ වාර්තා කරන ලද ගිණුමට එරෙහිව දණ්ඩනීය ක්රියාමාර්ගයක් ගන්නේ නම්, Spam කාණ්ඩය තෝරාගත් විට හැර, ඔවුන්ට විද්යුත් තැපෑලෙන් දැනුම්දීමක් යවනු ලැබේ. add_to_report: වාර්තා කිරීමට තවත් එක් කරන්න are_you_sure: ඔබට විශ්වාසද? assign_to_self: මට පවරන්න - assigned: පවරා ඇති උපපරිපාලක by_target_domain: වාර්තා කළ ගිණුමෙහි වසම - category: වර්ගය + cancel: අවලංගු + category: ප්රවර්ගය category_description_html: මෙම ගිණුම සහ/හෝ අන්තර්ගතය වාර්තා කළ හේතුව වාර්තා කළ ගිණුම සමඟ සන්නිවේදනයේ සඳහන් කරනු ඇත comment: none: කිසිවක් නැත comment_description_html: 'වැඩි විස්තර සැපයීම සඳහා, %{name} ලිවීය:' created_at: වාර්තා කර ඇත - delete_and_resolve: පළ කිරීම් මකන්න - forwarded: යොමු කළා - forwarded_to: '%{domain}වෙත යොමු කරන ලදී' - mark_as_resolved: විසඳා ඇති ලෙස ලකුණු කරන්න - mark_as_sensitive: සංවේදී ලෙස ලකුණු කරන්න - mark_as_unresolved: නොවිසඳුනු ලෙස ලකුණු කරන්න - no_one_assigned: කිසි කෙනෙක නැහැ + delete_and_resolve: ලිපි මකන්න + forwarded: හරවා යවා ඇත + forwarded_to: "%{domain} වෙත හරවා යැවිණි" + mark_as_resolved: විසඳූ බව යොදන්න + mark_as_sensitive: සංවේදී බව යොදන්න + mark_as_unresolved: නොවිසඳූ බව යොදන්න + no_one_assigned: කිසිවෙක් නැත notes: create: සටහන එකතු කරන්න - create_and_resolve: සටහන සමඟ විසඳන්න - create_and_unresolve: සටහනක් සමඟ නැවත විවෘත කරන්න + create_and_resolve: සටහනක් සමඟ විසඳන්න + create_and_unresolve: සටහනක් සමඟ නැවත අරින්න delete: මකන්න placeholder: ගෙන ඇති ක්රියාමාර්ග, හෝ වෙනත් අදාළ යාවත්කාලීන විස්තර කරන්න... title: සටහන් - notes_description_html: අනෙකුත් උපපරිපාලකයින්ට සහ ඔබේ අනාගතයට සටහන් බලන්න සහ තබන්න quick_actions_description_html: 'වාර්තා කළ අන්තර්ගතය බැලීමට ඉක්මන් ක්රියාමාර්ගයක් ගන්න හෝ පහළට අනුචලනය කරන්න:' - remote_user_placeholder: '%{instance}සිට දුරස්ථ පරිශීලකයා' - reopen: වාර්තාව නැවත විවෘත කරන්න - report: '@%{id} වාර්තා කරන්න' + remote_user_placeholder: "%{instance}සිට දුරස්ථ පරිශීලකයා" + reopen: වාර්තාව නැවත අරින්න + report: "@%{id} වාර්තා කරන්න" reported_account: වාර්තා කළ ගිණුම reported_by: විසින් වාර්තා resolved: විසඳා ඇත @@ -561,17 +543,52 @@ si: target_origin: වාර්තා කළ ගිණුමේ ආරම්භය title: වාර්තා unassign: පැවරීම ඉවත් කරන්න + unknown_action_msg: 'නොදන්නා ක්රියාමාර්ගයකි: %{action}' unresolved: නොවිසඳී ඇත updated_at: යාවත්කාලීන කරන ලදී view_profile: පැතිකඩ බලන්න + roles: + categories: + administration: පරිපාලනය + devops: DevOps + invites: ඇරයුම් + moderation: මැදිහත්කරණය + special: විශේෂ + delete: මකන්න + permissions_count: + one: අවසර %{count} + other: අවසර %{count} + privileges: + administrator: පරිපාලක + delete_user_data: පරිශ්රීලක දත්ත මකන්න + invite_users: ආරාධනා කරන්න + manage_announcements: නිවේදනය කළමනාකරණය + manage_federation: ඒකාබද්ධ කළමනාකරණය + manage_invites: ආරාධනා කළමනාකරණය + manage_reports: වාර්තා කළමනාකරණය + manage_roles: භූමිකා කළමනාකරණය + manage_rules: නීති කළමනාකරණය + manage_settings: සැකසුම් කළමනාකරණය + manage_user_access: ප්රවේශය කළමනාකරණය + manage_users: පරිශ්රීලකයින් කළමනාකරණය + view_dashboard: උපකරණ පුවරුව බලන්න + view_devops: DevOps + title: භූමිකා rules: - add_new: නීතිය එකතු කරන්න delete: මකන්න description_html: බොහෝ දෙනා සේවා කොන්දේසි කියවා එකඟ වූ බව ප්රකාශ කරන අතර, සාමාන්යයෙන් මිනිසුන් ගැටලුවක් පැනනඟින තුරු කියවා නොගනිති. පැතලි බුලට් පොයින්ට් ලිස්ට් එකකින් ඒවා ලබා දීමෙන් බැලූ බැල්මට ඔබේ සේවාදායකයේ නීති බැලීම පහසු කරන්න. තනි නීති කෙටි හා සරලව තබා ගැනීමට උත්සාහ කරන්න, නමුත් ඒවා විවිධ අයිතම වලට බෙදීමට උත්සාහ නොකරන්න. - edit: නීතිය සංස්කරණය කරන්න + edit: නීතිය සංස්කරණය empty: තවමත් සේවාදායක රීති නිර්වචනය කර නොමැත. title: සේවාදායකයේ නීති settings: + about: + manage_rules: සේවාදායකයේ නීති කළමනාකරණය + title: පිළිබඳව + appearance: + title: පෙනුම + discovery: + profile_directory: පැතිකඩ නාමාවලිය + public_timelines: ප්රසිද්ධ කාලරේඛා domain_blocks: all: හැමෝටම disabled: කාටවත් නෑ @@ -581,28 +598,45 @@ si: approved: ලියාපදිංචි වීමට අනුමැතිය අවශ්යයි none: කිසිවෙකුට ලියාපදිංචි විය නොහැක open: ඕනෑම කෙනෙකුට ලියාපදිංචි විය හැක + title: සේවාදායකයේ සැකසුම් site_uploads: delete: උඩුගත කළ ගොනුව මකන්න destroyed_msg: අඩවිය උඩුගත කිරීම සාර්ථකව මකා ඇත! + software_updates: + documentation_link: තව දැනගන්න + release_notes: නිකුතු සටහන් + title: තිබෙන යාවත්කාල + type: වර්ගය + version: අනුවාදය statuses: - back_to_account: ගිණුම් පිටුවට ආපසු යන්න - back_to_report: වාර්තා පිටුවට ආපසු යන්න + account: කර්තෘ + application: යෙදුම + back_to_account: ගිණුමේ පිටුවට ආපසු + back_to_report: වාර්තා පිටුවට ආපසු batch: remove_from_report: වාර්තාවෙන් ඉවත් කරන්න report: වාර්තාව - deleted: මකා දමන ලදී + deleted: මකා ඇත + favourites: ප්රියතමයන් + history: අනුවාද ඉතිහාසය + language: භාෂාව media: - title: මාධ්යය - no_status_selected: කිසිවක් තෝරා නොගත් බැවින් තනතුරු කිසිවක් වෙනස් කර නැත - title: ගිණුම් තනතුරු - with_media: මාධ්ය දායකත්වය + title: මාධ්ය + metadata: පාරදත්ත + no_status_selected: කිසිවක් නොතේරූ බැවින් ලිපි කිසිවක් වෙනස් කර නැත + open: ලිපිය අරින්න + original_status: මුල් ලිපිය + status_changed: ලිපිය සංශෝධිතයි + title: ගිණුමේ ලිපි + trending: නැගී එන + with_media: මාධ්ය සමඟ strikes: actions: - delete_statuses: "%{target}ගේ පළ කිරීම් %{name} මකා දමන ලදී" + delete_statuses: "%{target}ගේ ලිපි %{name} මකා ඇත" disable: "%{name} %{target}ගේ ගිණුම නිශ්චල කළේය" - mark_statuses_as_sensitive: "%{name} %{target}ගේ පළ කිරීම් සංවේදී ලෙස ලකුණු කර ඇත" + mark_statuses_as_sensitive: "%{target}ගේ ලිපි සංවේදී බව %{name} සලකුණු කර ඇත" none: "%{name} %{target}අනතුරු ඇඟවීමක් යවා ඇත" - sensitive: "%{name} %{target}ගේ ගිණුම සංවේදී ලෙස ලකුණු කර ඇත" + sensitive: "%{target}ගේ ගිණුම සංවේදී බව %{name} සලකුණු කර ඇත" silence: "%{name} සීමිත %{target}ගිණුමක්" suspend: "%{name} %{target}ගේ ගිණුම අත්හිටුවා ඇත" appeal_approved: අභියාචනා කළා @@ -610,28 +644,35 @@ si: system_checks: database_schema_check: message_html: පොරොත්තු දත්ත සමුදා සංක්රමණයන් ඇත. යෙදුම අපේක්ෂිත පරිදි ක්රියා කරන බව සහතික කිරීමට කරුණාකර ඒවා ධාවනය කරන්න + elasticsearch_preset: + action: ප්රලේඛනය බලන්න + elasticsearch_preset_single_node: + action: ප්රලේඛනය බලන්න elasticsearch_running_check: message_html: Elasticsearch වෙත සම්බන්ධ වීමට නොහැකි විය. කරුණාකර එය ක්රියාත්මක වන බව පරීක්ෂා කරන්න, නැතහොත් සම්පූර්ණ පෙළ සෙවීම අක්රීය කරන්න elasticsearch_version_check: message_html: 'නොගැලපෙන ඉලාස්ටික් සෙවුම් අනුවාදය: %{value}' version_comparison: Elasticsearch %{running_version} ක්රියාත්මක වන අතර %{required_version} අවශ්ය වේ rules_check: - action: සේවාදායක නීති කළමනාකරණය කරන්න + action: සේවාදායකයේ නීති කළමනාකරණය message_html: ඔබ සේවාදායක රීති කිසිවක් නිර්වචනය කර නැත. sidekiq_process_check: - message_html: '%{value} පෝලිම්(ය) සඳහා Sidekiq ක්රියාවලියක් ක්රියාත්මක නොවේ. කරුණාකර ඔබේ Sidekiq වින්යාසය සමාලෝචනය කරන්න' + message_html: "%{value} පෝලිම්(ය) සඳහා Sidekiq ක්රියාවලියක් ක්රියාත්මක නොවේ. කරුණාකර ඔබේ Sidekiq වින්යාසය සමාලෝචනය කරන්න" + software_version_critical_check: + action: තිබෙන යාවත්කාල බලන්න + software_version_patch_check: + action: තිබෙන යාවත්කාල බලන්න tags: review: තත්වය සමාලෝචනය updated_msg: Hashtag සැකසුම් සාර්ථකව යාවත්කාලීන කරන ලදී title: පරිපාලනය trends: - allow: ඉඩ දෙන්න - approved: අනුමත කළා + allow: ඉඩදෙන්න + approved: අනුමතයි disallow: අවසර නොදෙන්න links: - allow: සබැඳියට ඉඩ දෙන්න + allow: සබැඳියට ඉඩදෙන්න allow_provider: ප්රකාශකයාට ඉඩ දෙන්න - description_html: මේවා ඔබගේ සේවාදායකය විසින් පළ කිරීම් දකින ගිණුම් මගින් දැනට බොහෝ සෙයින් බෙදා ගන්නා සබැඳි වේ. එය ඔබගේ පරිශීලකයින්ට ලෝකයේ සිදුවෙමින් පවතින දේ සොයා ගැනීමට උදවු කළ හැක. ඔබ ප්රකාශකයා අනුමත කරන තුරු සබැඳි කිසිවක් ප්රසිද්ධියේ ප්රදර්ශනය නොවේ. ඔබට තනි සබැඳිවලට ඉඩ දීමට හෝ ප්රතික්ෂේප කිරීමටද හැකිය. disallow: සබැඳියට ඉඩ නොදෙන්න disallow_provider: ප්රකාශකයාට ඉඩ නොදෙන්න shared_by_over_week: @@ -639,7 +680,6 @@ si: other: පසුගිය සතිය පුරා පුද්ගලයින් %{count} දෙනෙකු විසින් බෙදා ගන්නා ලදී title: නැඟී එන සබැඳි usage_comparison: ඊයේ %{yesterday} හා සසඳන විට අද %{today} වරක් බෙදා ගන්නා ලදී - only_allowed: අවසර දී ඇත pending_review: පොරොත්තු සමාලෝචනය preview_card_providers: allowed: මෙම ප්රකාශකයාගේ සබැඳි නැඹුරු විය හැක @@ -648,16 +688,15 @@ si: title: ප්රකාශකයන් rejected: ප්රතික්ෂේප කළා statuses: - allow: පළ කිරීමට ඉඩ දෙන්න + allow: පළ කිරීමට ඉඩදෙන්න allow_account: කතුවරයාට ඉඩ දෙන්න - description_html: මේ වන විට ඔබේ සේවාදායකය දන්නා පෝස්ට් මේ වන විට බොහෝ බෙදාහරින සහ මේ මොහොතේ වැඩි කැමැත්තක් දක්වයි. එය ඔබගේ නව සහ නැවත පැමිණෙන පරිශීලකයින්ට අනුගමනය කිරීමට තවත් පුද්ගලයින් සොයා ගැනීමට උදවු කළ හැක. ඔබ කර්තෘ අනුමත කරන තෙක් පළ කිරීම් කිසිවක් ප්රසිද්ධියේ නොපෙන්වන අතර, කර්තෘ තම ගිණුම අන් අයට යෝජනා කිරීමට ඉඩ දෙයි. ඔබට තනි පළ කිරීම්වලට ඉඩ දීමට හෝ ප්රතික්ෂේප කිරීමටද හැකිය. - disallow: පළ කිරීමට ඉඩ නොදෙන්න + disallow: ප්රකාශනයට ඉඩ නොදෙන්න disallow_account: කතුවරයාට ඉඩ නොදෙන්න not_discoverable: කර්තෘ සොයා ගත හැකි බව තෝරාගෙන නැත shared_by: one: එක් වරක් බෙදාගත් හෝ ප්රිය කරන ලදී - other: '%{friendly_count} වරක් බෙදාගෙන ප්රිය කරන ලදී' - title: ප්රවණතා පළ කිරීම් + other: "%{friendly_count} වරක් බෙදාගෙන ප්රිය කරන ලදී" + title: නැගී එන ලිපි tags: current_score: වත්මන් ලකුණු %{score} dashboard: @@ -666,9 +705,8 @@ si: tag_servers_dimension: ඉහළම සේවාදායකයන් tag_servers_measure: විවිධ සේවාදායකයන් tag_uses_measure: සම්පූර්ණ භාවිතය - description_html: මේවා දැනට ඔබගේ සේවාදායකය දකින බොහෝ පළ කිරීම් වල දිස්වන හැෂ් ටැග් වේ. මේ මොහොතේ මිනිසුන් වැඩිපුරම කතා කරන්නේ කුමක් දැයි සොයා ගැනීමට එය ඔබගේ පරිශීලකයින්ට උදවු කළ හැක. ඔබ ඒවා අනුමත කරන තුරු හෑෂ් ටැග් ප්රසිද්ධියේ නොපෙන්වයි. - listable: යෝජනා කළ හැක - not_listable: යෝජනා නොකරනු ඇත + listable: යෝජනා කළ හැකිය + not_listable: යෝජනා නොවනු ඇත not_trendable: ප්රවණතා යටතේ දිස් නොවනු ඇත not_usable: භාවිතා කළ නොහැක peaked_on_and_decaying: '%{date}හි උච්චතම, දැන් දිරාපත් වෙමින් පවතී' @@ -691,16 +729,15 @@ si: webhooks: add_new: අන්ත ලක්ෂ්යය එක් කරන්න delete: මකන්න - description_html: A webhook Mastodon හට තෝරාගත් සිදුවීම් පිළිබඳ තත්ය කාලීන දැනුම්දීම් ක් ඔබේම යෙදුමට තල්ලු කිරීමට හැකියාව ලබා දෙයි, එම නිසා ඔබේ යෙදුමට ස්වයංක්රීයව ප්රතික්රියා අවුලුවාලීමට හැකිය. - disable: අක්රිය කරන්න - disabled: ආබාධිතයි + disable: අබල කරන්න + disabled: අබලයි edit: අන්ත ලක්ෂ්යය සංස්කරණය කරන්න empty: ඔබට තවම වින්යාස කර ඇති කිසිදු webhook අන්ත ලක්ෂ්යයක් නොමැත. enable: සබල කරන්න - enabled: ක්රියාකාරී + enabled: ක්රියාත්මකයි enabled_events: - one: 1 සබල කළ සිදුවීමක් - other: "%{count} සබල කළ සිදුවීම්" + one: සබල සිදුවීම් 1 + other: සබල සිදුවීම් %{count} events: සිදුවීම් new: නව webhook rotate_secret: රහස කරකවන්න @@ -712,14 +749,10 @@ si: actions: delete_statuses: ඔවුන්ගේ පළ කිරීම් මකා දැමීමට disable: ඔවුන්ගේ ගිණුම කැටි කිරීමට - mark_statuses_as_sensitive: ඔවුන්ගේ තනතුරු සංවේදී ලෙස සලකුණු කිරීමට none: අනතුරු ඇඟවීමක් sensitive: ඔවුන්ගේ ගිණුම සංවේදී ලෙස සලකුණු කිරීමට silence: ඔවුන්ගේ ගිණුම සීමා කිරීමට suspend: ඔවුන්ගේ ගිණුම අත්හිටුවීමට - body: "%{target} යනු %{type}ක් වූ %{date}සිට %{action_taken_by} කින් මධ්යස්ථ තීරණයක් අභියාචනා කරයි. ඔවුන් මෙසේ ලිවීය." - next_steps: ඔබට මධ්යස්ථ තීරණය අවලංගු කිරීමට අභියාචනය අනුමත කළ හැකිය, නැතහොත් එය නොසලකා හරින්න. - subject: "%{username} යනු %{instance}හි මධ්යස්ථ තීරණයකට අභියාචනා කරයි" new_pending_account: body: නව ගිණුමේ විස්තර පහතින්. ඔබට මෙම යෙදුම අනුමත කිරීමට හෝ ප්රතික්ෂේප කිරීමට හැකිය. subject: නව ගිණුම සමාලෝචනය සඳහා %{instance} (%{username}) @@ -732,7 +765,7 @@ si: new_trending_links: title: නැඟී එන සබැඳි new_trending_statuses: - title: ප්රවණතා පළ කිරීම් + title: නැගී එන ලිපි new_trending_tags: no_approved_tags: දැනට අනුමත ප්රවණතා හැෂ් ටැග් නොමැත. requirements: 'මෙම ඕනෑම අපේක්ෂකයෙකුට #%{rank} අනුමත ප්රවණතා හැෂ් ටැගය අභිබවා යා හැකිය, එය දැනට ලකුණු %{lowest_tag_score}ක් සමඟ #%{lowest_tag_name} වේ.' @@ -746,37 +779,38 @@ si: hint_html: ඔබට වෙනත් ගිණුමකින් මෙය වෙත මාරු වීමට අවශ්ය නම්, මෙහිදී ඔබට අන්වර්ථ නාමයක් සෑදිය හැක, එය පැරණි ගිණුමෙන් අනුගාමිකයින් මෙම ගිණුමට ගෙන යාමට පෙර අවශ්ය වේ. මෙම ක්රියාවම හානිකර නොවන සහ ආපසු හැරවිය හැකිවේ. ගිණුම් සංක්රමණය පැරණි ගිණුමෙන් ආරම්භ වේ. remove: අන්වර්ථය විසන්ධි කරන්න appearance: - advanced_web_interface: උසස් වියමන අතුරුමුහුණත + advanced_web_interface: සංකීර්ණ අතුරු මුහුණත advanced_web_interface_hint: 'ඔබට ඔබේ සම්පූර්ණ තිරයේ පළල භාවිතා කිරීමට අවශ්ය නම්, උසස් වෙබ් අතුරු මුහුණත ඔබට අවශ්ය පරිදි එකම වේලාවක බොහෝ තොරතුරු බැලීමට විවිධ තීරු වින්යාස කිරීමට ඉඩ දෙයි: නිවස, දැනුම්දීම්, ෆෙඩරේටඩ් කාලරාමුව, ඕනෑම ලැයිස්තු සහ හැෂ් ටැග්.' animations_and_accessibility: සජීවිකරණ සහ ප්රවේශ්යතාව confirmation_dialogs: තහවුරු කිරීමේ සංවාද discovery: සොයාගැනීම localization: - body: Mastodon ස්වේච්ඡා සේවකයන් විසින් පරිවර්තනය කර ඇත. + body: මාස්ටඩන් ස්වේච්ඡාවෙන් පරිවර්තනය කර ඇත. guide_link: https://crowdin.com/project/mastodon - guide_link_text: සෑම කෙනෙකුටම දායක විය හැකිය. - sensitive_content: සංවේදී අන්තර්ගතය + guide_link_text: පරිවර්තකයින්ට දායක වීමට හැකිය. + sensitive_content: සංවේදී අන්තර්ගත application_mailer: notification_preferences: ඊමේල් මනාප වෙනස් කරන්න salutation: "%{name}," settings: 'ඊමේල් මනාප වෙනස් කරන්න: %{link}' view: 'දැක්ම:' view_profile: පැතිකඩ බලන්න - view_status: පළ කිරීම බලන්න + view_status: ලිපිය බලන්න applications: - created: යෙදුම සාර්ථකව නිර්මාණය කරන ලදී + created: යෙදුම සාර්ථකව සෑදිණි destroyed: යෙදුම සාර්ථකව මකා ඇත + logout: නික්මෙන්න regenerate_token: ප්රවේශ ටෝකනය නැවත උත්පාදනය කරන්න token_regenerated: ප්රවේශ ටෝකනය සාර්ථකව ප්රතිජනනය කරන ලදී warning: මෙම දත්ත සමඟ ඉතා ප්රවේශම් වන්න. එය කිසි විටෙක කිසිවෙකු සමඟ බෙදා නොගන්න! your_token: ඔබේ ප්රවේශ ටෝකනය auth: + apply_for_account: ගිණුමක් ඉල්ලන්න delete_account: ගිණුම මකන්න delete_account_html: ඔබට ඔබගේ ගිණුම මකා දැමීමට අවශ්ය නම්, ඔබට මෙතැනින් ඉදිරියට යා හැක. තහවුරු කිරීම සඳහා ඔබෙන් අසනු ඇත. description: - prefix_invited_by_user: "@%{name} ඔබට Mastodon හි මෙම සේවාදායකයට සම්බන්ධ වීමට ආරාධනා කරයි!" + prefix_invited_by_user: "@%{name} මෙම මාස්ටඩන් සේවාදායකයට ආරාධනා කර ඇත!" prefix_sign_up: අදම මාස්ටඩන් හි ලියාපදිංචි වන්න! - suffix: ගිණුමක් සමඟ, ඔබට ඕනෑම Mastodon සේවාදායකයකින් සහ තවත් බොහෝ දේ භාවිතා කරන්නන් සමඟ පුද්ගලයින් අනුගමනය කිරීමට, යාවත්කාලීන කිරීම් පළ කිරීමට සහ පණිවිඩ හුවමාරු කර ගැනීමට හැකි වනු ඇත! dont_have_your_security_key: ඔබගේ ආරක්ෂක යතුර නොමැතිද? forgot_password: මුරපදය අමතක වුනාද? invalid_reset_password_token: මුරපද යළි පිහිටුවීමේ ටෝකනය අවලංගු හෝ කල් ඉකුත් වී ඇත. කරුණාකර අලුත් එකක් ඉල්ලන්න. @@ -787,12 +821,25 @@ si: logout: නික්මෙන්න migrate_account: වෙනත් ගිණුමකට යන්න migrate_account_html: ඔබට මෙම ගිණුම වෙනත් එකකට හරවා යැවීමට අවශ්ය නම්, ඔබට එය මෙහි වින්යාසගත කළ හැක. - or_log_in_with: හෝ සමඟින් පිවිසෙන්න + progress: + details: ඔබගේ විස්තර + rules: නීති පිළිගන්න + providers: + cas: CAS + saml: SAML register: ලියාපදිංචිය registration_closed: "%{instance} නව සාමාජිකයින් පිළිගන්නේ නැත" - reset_password: මුරපදය නැවත සකසන්න - security: ආරක්ෂාව + reset_password: මුරපදය යළි සකසන්න + rules: + accept: පිළිගන්න + back: ආපසු + title_invited: ඔබට ආරාධනා කර ඇත. + security: ආරක්ෂාව set_new_password: නව මුරපදය සකසන්න + setup: + title: ඔබගේ එනලිපි බලන්න + sign_in: + title: "%{domain} වෙත පිවිසෙන්න" status: account_status: ගිණුමේ තත්වය confirming: විද්යුත් තැපෑල තහවුරු කිරීම සම්පූර්ණ කිරීම සඳහා රැඳී සිටිමින්. @@ -801,11 +848,11 @@ si: redirecting_to: එය දැනට %{acct}වෙත හරවා යවන බැවින් ඔබගේ ගිණුම අක්රියයි. view_strikes: ඔබගේ ගිණුමට එරෙහිව පසුගිය වර්ජන බලන්න too_fast: පෝරමය ඉතා වේගයෙන් ඉදිරිපත් කර ඇත, නැවත උත්සාහ කරන්න. - use_security_key: ආරක්ෂක යතුර භාවිතා කරන්න + use_security_key: ආරක්ෂණ යතුර භාවිතා කරන්න challenge: confirm: ඉදිරියට hint_html: "ඉඟිය: අපි ඉදිරි පැය සඳහා නැවත ඔබගේ මුරපදය ඔබෙන් නොඉල්ලමු." - invalid_password: නොවන මුරපදයකි + invalid_password: මුරපදය වැරදිය prompt: ඉදිරියට යාමට මුරපදය තහවුරු කරන්න crypto: errors: @@ -813,20 +860,24 @@ si: invalid_signature: වලංගු Ed25519 අත්සනක් නොවේ date: formats: - default: "%b %d, %Y" - with_month_name: "%B %d, %Y" + default: "%Y %b %d" + with_month_name: "%Y %B %d" datetime: distance_in_words: - about_x_hours: "පැය %{count}" - about_x_months: "මාස %{count}" + about_x_hours: පැය %{count} + about_x_months: මාස %{count} + about_x_years: ව.%{count} + almost_x_years: ව.%{count} half_a_minute: මේ දැන් - less_than_x_minutes: "මීටර් %{count}" + less_than_x_minutes: විනාඩි %{count} less_than_x_seconds: මේ දැන් - x_minutes: "මීටර් %{count}" - x_months: "මාස %{count}" - x_seconds: "%{count}තත්" + over_x_years: ව.%{count} + x_days: ද.%{count} + x_minutes: විනාඩි %{count} + x_months: මාස %{count} + x_seconds: තත්. %{count} deletes: - challenge_not_passed: ඔබ ඇතුළත් කළ තොරතුරු නිවැරදි නැත + challenge_not_passed: ඔබ ඇතුල් කරන ලද තොරතුරු වැරදියි confirm_password: ඔබගේ අනන්යතාවය තහවුරු කිරීමට ඔබගේ වත්මන් මුරපදය ඇතුලත් කරන්න confirm_username: ක්රියා පටිපාටිය තහවුරු කිරීමට ඔබගේ පරිශීලක නාමය ඇතුලත් කරන්න proceed: ගිණුම මකන්න @@ -834,7 +885,7 @@ si: warning: before: 'ඉදිරියට යාමට පෙර, කරුණාකර මෙම සටහන් හොඳින් කියවන්න:' caches: වෙනත් සේවාදායකයන් විසින් හැඹිලිගත කර ඇති අන්තර්ගතය දිගටම පැවතිය හැක - data_removal: ඔබගේ පළ කිරීම් සහ අනෙකුත් දත්ත ස්ථිරවම ඉවත් කරනු ලැබේ + data_removal: ඔබගේ ලිපි සහ අනෙකුත් දත්ත සදහටම ඉවත් කෙරෙනු ඇත email_change_html: ඔබට ඔබගේ ගිණුම මකා කළ හැක email_contact_html: එය තවමත් නොපැමිණියේ නම්, ඔබට උදව් සඳහා %{email} විද්යුත් තැපෑලෙන් යැවිය හැක email_reconfirmation_html: ඔබට තහවුරු කිරීමේ විද්යුත් තැපෑල නොලැබුනේ නම්, ඔබට එය නැවත ඉල්ලා සිටිය හැක @@ -858,13 +909,12 @@ si: description_html: මේවා ඔබගේ ගිණුමට එරෙහිව ගන්නා ලද ක්රියා සහ %{instance}හි කාර්ය මණ්ඩලය විසින් ඔබට එවා ඇති අනතුරු ඇඟවීම් වේ. recipient: වෙත යොමු කරන ලදී reject_appeal: අභියාචනය ප්රතික්ෂේප කරන්න - status: 'පළ කිරීම #%{id}' - status_removed: පළ කිරීම දැනටමත් පද්ධතියෙන් ඉවත් කර ඇත + status: "#%{id} ලිපිය" + status_removed: ලිපිය දැනටමත් පද්ධතියෙන් ඉවත් කර ඇත title: "%{action} සිට %{date}" title_actions: - delete_statuses: පසු ඉවත් කිරීම + delete_statuses: ලිපි ඉවත් කිරීම disable: ගිණුම කැටි කිරීම - mark_statuses_as_sensitive: තනතුරු සංවේදී ලෙස සලකුණු කිරීම none: අවවාදයයි sensitive: ගිණුම සංවේදී ලෙස සලකුණු කිරීම silence: ගිණුම සීමා කිරීම @@ -874,6 +924,9 @@ si: your_appeal_rejected: ඔබගේ අභියාචනය ප්රතික්ෂේප කර ඇත domain_validator: invalid_domain: වලංගු ඩොමේන් නාමයක් නොවේ + edit_profile: + basic_information: මූලික තොරතුරු + other: වෙනත් errors: '400': ඔබ ඉදිරිපත් කළ ඉල්ලීම අවලංගු හෝ විකෘති විය. '403': ඔබට මෙම පිටුව බැලීමට අවසර නැත. @@ -883,42 +936,43 @@ si: '422': content: ආරක්ෂක සත්යාපනය අසාර්ථක විය. ඔබ කුකීස් අවහිර කරනවාද? title: ආරක්ෂක සත්යාපනය අසාර්ථක විය - '429': ඉල්ලීම් වැඩියි + '429': ඉල්ලීම් බොහෝය '500': content: අපට කණගාටුයි, නමුත් අපගේ පැත්තෙන් යමක් වැරදී ඇත. - title: මෙම පිටුව නිවැරදි නොවේ + title: මෙම පිටුව වැරදියි '503': තාවකාලික සේවාදායකයේ අසාර්ථක වීමක් හේතුවෙන් පිටුව සේවය කිරීමට නොහැකි විය. - noscript_html: Mastodon වෙබ් යෙදුම භාවිතා කිරීමට, කරුණාකර JavaScript සක්රීය කරන්න. විකල්පයක් ලෙස, ඔබේ වේදිකාව සඳහා එකක් උත්සාහ කරන්න. + noscript_html: මාස්ටඩන් වියමන යෙදුම භාවිතා කිරීමට ජාවාස්ක්රිප්ට් සබල කරන්න. ඊට අමතරව, ඔබගේ වේදිකාව සඳහා වන නිසග යෙදුමක් අත්හදා බලන්න. existing_username_validator: not_found: එම පරිශීලක නාමය සහිත දේශීය පරිශීලකයෙකු සොයා ගැනීමට නොහැකි විය not_found_multiple: '%{usernames}සොයා ගැනීමට නොහැකි විය' exports: archive_takeout: date: දිනය - download: ඔබගේ සුරක්ෂිතභාවය බාගන්න - hint_html: ඔබට ඔබගේ පළ කිරීම් සහ උඩුගත කළ මාධ්යහි සංරක්ෂිතයක් ඉල්ලා සිටිය හැක. නිර්යාත කළ දත්ත ActivityPub ආකෘතියෙන්, ඕනෑම අනුකූල මෘදුකාංගයකට කියවිය හැකිය. ඔබට දින 7කට වරක් ලේඛනාගාරයක් ඉල්ලා සිටිය හැක. + download: ඔබගේ සංරක්ෂිතය බාගන්න + hint_html: ඔබට ලිපි සහ උඩුගත කළ මාධ්යවල සංරක්ෂණයක් ඉල්ලීමට හැකිය. නිර්යාත කළ දත්ත ActivityPub ආකෘතියට ගැළපෙන ඕනෑම මෘදුකාංගයකින් කියවීමට හැකිය. ඔබට දවස් 7 කට වරක් සංරක්ෂණයක් ඉල්ලීමට හැකිය. in_progress: ඔබගේ සංරක්ෂිතය සම්පාදනය කරමින්... request: ඔබගේ සංරක්ෂිතය ඉල්ලන්න size: ප්රමාණය blocks: ඔබ අවහිර කරන්න - bookmarks: පොත් යොමු කරන්න + bookmarks: පොත්යොමු + csv: CSV domain_blocks: වසම් අවහිර කිරීම් - lists: ලැයිස්තුව + lists: ලැයිස්තු mutes: ඔබ නිහඬ කරන්න - storage: මාධ්ය ගබඩාව + storage: මාධ්ය ආචයනය featured_tags: add_new: අලුතින් එකතු කරන්න - hint_html: "විශේෂාංගගත හැෂ් ටැග් මොනවාද? ඒවා ඔබේ පොදු පැතිකඩෙහි ප්රමුඛව ප්රදර්ශනය වන අතර එම හැෂ් ටැග් යටතේ ඔබේ පොදු පළ කිරීම් බ්රවුස් කිරීමට මිනිසුන්ට ඉඩ සලසයි. නිර්මාණාත්මක කෘති හෝ දිගු කාලීන ව්යාපෘති පිළිබඳ වාර්තාවක් තබා ගැනීම සඳහා ඔවුන් විශිෂ්ට මෙවලමක් වේ." filters: contexts: account: පැතිකඩයන් - home: නිවස සහ ලැයිස්තු + home: මුල සහ ලැයිස්තු notifications: දැනුම්දීම් - public: පොදු කාලරේඛා + public: ප්රසිද්ධ කාලරේඛා thread: සංවාද edit: add_keyword: මූල පදය එක් කරන්න keywords: මූල පද + statuses: තනි ලිපි title: පෙරහන සංස්කරණය errors: deprecated_api_multiple_keywords: මෙම පරාමිති පෙරහන් මූල පද එකකට වඩා අදාළ වන බැවින් මෙම යෙදුමෙන් වෙනස් කළ නොහැක. වඩාත් මෑත යෙදුමක් හෝ වෙබ් අතුරු මුහුණතක් භාවිතා කරන්න. @@ -926,21 +980,32 @@ si: index: contexts: '%{contexts}හි පෙරහන්' delete: මකන්න - empty: ඔබට පෙරහන් නොමැත. - expires_in: '%{distance}කින් කල් ඉකුත් වේ' - expires_on: '%{date}දින කල් ඉකුත් වේ' + empty: ඔබ සතුව පෙරහන් නැත. + expires_in: "%{distance} කින් ඉකුත් වේ" + expires_on: "%{date} දී ඉකුත් වේ" keywords: - one: "%{count} මූල පදය" - other: "%{count} මූල පද" + one: මූල පද %{count} + other: මූල පද %{count} + statuses: + one: ලිපි %{count} + other: ලිපි %{count} title: පෙරහන් new: save: නව පෙරහන සුරකින්න title: නව පෙරහනක් එකතු කරන්න + statuses: + back_to_filter: පෙරහනට ආපසු + batch: + remove: පෙරහනෙන් ඉවතලන්න + index: + title: පෙරූ ලිපි generic: all: සියල්ල - changes_saved_msg: වෙනස්කම් සාර්ථකව සුරකින ලදී! + cancel: අවලංගු + changes_saved_msg: වෙනස්කම් සාර්ථකව සුරැකිණි! copy: පිටපතක් delete: මකන්න + deselect: සියල්ල නොතෝරන්න none: කිසිවක් නැත order_by: විසින් ඇණවුම් කරන්න save_changes: වෙනස්කම් සුරකින්න @@ -950,24 +1015,33 @@ si: other: යමක් තවමත් හරි නැත! කරුණාකර පහත දෝෂ %{count} ක් සමාලෝචනය කරන්න imports: errors: + empty: හිස් CSV ගොනුවකි over_rows_processing_limit: පේළි %{count} කට වඩා අඩංගු වේ + too_large: ගොනුව ඉතා විශාලයි + imported: ආයාත විය modes: - merge: ඒකාබද්ධ කරන්න + merge: ඒකාබද්ධ merge_long: පවතින වාර්තා තබා නව ඒවා එකතු කරන්න overwrite: උඩින් ලියන්න overwrite_long: වත්මන් වාර්තා නව ඒවා සමඟ ප්රතිස්ථාපනය කරන්න preface: ඔබ අනුගමන කරන හෝ අවහිර කරන පුද්ගලයින්ගේ ලැයිස්තුවක් වැනි වෙනත් සේවාදායකයකින් ඔබ නිර්යාත කර ඇති දත්ත ඔබට ආයාත කළ හැක. + status: තත්වය success: ඔබගේ දත්ත සාර්ථකව උඩුගත කර ඇති අතර නියමිත වේලාවට සැකසෙනු ඇත + titles: + lists: ලැයිස්තු ආයාත වෙමින් + type_groups: + constructive: අනුගමන හා පොත්යොමු types: - blocking: අවහිර කිරීමේ ලැයිස්තුව - bookmarks: පොත් යොමු - domain_blocking: වසම් අවහිර කිරීමේ ලැයිස්තුව - following: පහත ලැයිස්තුව + blocking: අවහිර ලැයිස්තුව + bookmarks: පොත්යොමු + domain_blocking: වසම් අවහිර ලැයිස්තුව + following: අනුගමන ලැයිස්තුව + lists: ලැයිස්තු muting: නිහඬ කිරීමේ ලැයිස්තුව upload: උඩුගත කරන්න invites: - delete: අක්රිය කරන්න - expired: කල් ඉකුත් වී ඇත + delete: අක්රිය කරන්න + expired: ඉකුත් වී ඇත expires_in: '1800': විනාඩි 30 '21600': පැය 6 @@ -986,7 +1060,7 @@ si: table: expires_at: කල් ඉකුත් වේ uses: භාවිතා කරයි - title: මිනිසුන්ට ආරාධනා කරන්න + title: ආරාධනා කරන්න login_activities: authentication_methods: otp: ද්වි-සාධක සත්යාපන යෙදුම @@ -1000,7 +1074,7 @@ si: title: සත්යාපන ඉතිහාසය media_attachments: validations: - images_and_video: දැනටමත් පින්තූර අඩංගු පළ කිරීමකට වීඩියෝවක් ඇමිණිය නොහැක + images_and_video: දැනටමත් රූප අඩංගු ලිපියකට දෘශ්යකයක් ඇමිණීමට නොහැකිය not_ready: සැකසීම අවසන් නොකළ ගොනු ඇමිණිය නොහැක. මොහොතකින් නැවත උත්සාහ කරන්න! too_many: ගොනු 4කට වඩා ඇමිණිය නොහැක migrations: @@ -1035,7 +1109,7 @@ si: other_data: වෙනත් දත්ත කිසිවක් ස්වයංක්රීයව ගෙන නොයනු ඇත redirect: ඔබගේ ජංගම ගිණුමේ පැතිකඩ යළි-යොමු කිරීමේ දැන්වීමක් සමඟ යාවත්කාලීන කෙරෙන අතර සෙවුම් වලින් බැහැර කරනු ලැබේ moderation: - title: මධ්යස්ථභාවය + title: මැදිහත්කරණය move_handler: carry_blocks_over_text: මෙම පරිශීලකයා ඔබ අවහිර කර තිබූ %{acct}සිට මාරු විය. carry_mutes_over_text: මෙම පරිශීලකයා ඔබ නිශ්ශබ්ද කර තිබූ %{acct}වෙතින් මාරු විය. @@ -1043,37 +1117,31 @@ si: notification_mailer: admin: report: - subject: "%{name} වාර්තාවක් ඉදිරිපත් කළේය" + subject: "%{name} වාර්තාවක් යොමු කර ඇත" sign_up: subject: "%{name} අත්සන් කර ඇත" favourite: - body: 'ඔබේ පළ කිරීම %{name}විසින් ප්රිය කරන ලදී:' - subject: "%{name} ඔබගේ පළ කිරීම ප්රිය කරන ලදී" - title: නව ප්රියතම + body: "%{name} ඔබගේ ලිපියට ප්රිය කළා:" + subject: "%{name} ඔබගේ ලිපියට ප්රිය කළා" + title: නව ප්රියතමය follow: body: "%{name} දැන් ඔබව අනුගමනය කරයි!" subject: "%{name} දැන් ඔබව අනුගමනය කරයි" title: නව අනුගාමිකයෙක් follow_request: - action: අනුගමනය කරන ඉල්ලීම් කළමනාකරණය කරන්න + action: අනුගමන ඉල්ලීම් කළමනාකරණය body: "%{name} ඔබව අනුගමනය කිරීමට ඉල්ලා ඇත" subject: 'පොරොත්තු අනුගාමිකයා: %{name}' - title: නව අනුගමනය ඉල්ලීම + title: නව අනුගමන ඉල්ලීම mention: action: පිළිතුර - body: 'ඔබව මෙහි %{name} කින් සඳහන් කර ඇත:' - subject: ඔබව %{name}මගින් සඳහන් කර ඇත + body: "%{name} ඔබව මෙහි සඳහන් කර ඇත:" + subject: "%{name} ඔබව සඳහන් කර ඇත" title: නව සඳැහුම - poll: - subject: '%{name} න් මත විමසුමක් අවසන් විය' - reblog: - body: 'ඔබේ පළ කිරීම %{name}කින් වැඩි කරන ලදී:' - subject: "%{name} ඔබේ පළ කිරීම ඉහළ නැංවීය" - title: නව තල්ලුවක් status: subject: "%{name} දැන් පළ කළා" update: - subject: "%{name} පළ කිරීමක් සංස්කරණය කළා" + subject: "%{name} ලිපිය සංශෝධනය කළා" notifications: email_events: ඊමේල් දැනුම්දීම් සඳහා සිදුවීම් email_events_hint: 'ඔබට දැනුම්දීම් ලැබීමට අවශ්ය සිදුවීම් තෝරන්න:' @@ -1083,11 +1151,8 @@ si: decimal_units: format: "%n%u" units: - billion: බී million: ද.ල. - quadrillion: ප්රශ්නය thousand: ද. - trillion: ටී otp_authentication: code_hint: තහවුරු කිරීමට ඔබගේ සත්යාපන යෙදුම මගින් ජනනය කරන ලද කේතය ඇතුළු කරන්න description_html: ඔබ සත්යාපන යෙදුමක් භාවිතයෙන් ද්වි-සාධක සත්යාපනය සක්රීය කරන්නේ නම්, ලොගින් වීමේදී ඔබට ඔබගේ දුරකථනය සන්තකයේ තබා ගැනීමට අවශ්ය වනු ඇත, එය ඔබට ඇතුළු වීමට ටෝකන ජනනය කරයි. @@ -1104,19 +1169,21 @@ si: truncate: "…" polls: errors: - already_voted: ඔබ දැනටමත් මෙම මත විමසුමට ඡන්දය දී ඇත - duplicate_options: අනුපිටපත් අයිතම අඩංගු වේ + already_voted: ඔබ මෙම මත විමසුමට ඡන්දය දී ඇත duration_too_long: අනාගතයට බොහෝ දුරයි - duration_too_short: ඉතා ඉක්මනින් වේ - expired: මත විමසුම දැනටමත් අවසන් වී ඇත + expired: මත විමසුම දැනටමත් නිමා වී ඇත invalid_choice: තෝරාගත් ඡන්ද විකල්පය නොපවතී - over_character_limit: එක් එක් අක්ෂර %{max} ට වඩා දිගු විය නොහැක - too_few_options: එක් අයිතමයකට වඩා තිබිය යුතුය - too_many_options: අයිතම %{max} කට වඩා අඩංගු විය නොහැක + self_vote: ඔබගේ මත විමසුමට ජන්දය දීමට නොහැකිය + too_few_options: එක් අථකයකට වඩා තිබිය යුතුය + too_many_options: අථක %{max} කට වඩා අඩංගු නොවිය යුතුය preferences: other: වෙනත් - posting_defaults: පෙරනිමි පළ කිරීම - public_timelines: පොදු කාලරේඛා + posting_defaults: සැමවිට පළ කරන ආකාරය + public_timelines: ප්රසිද්ධ කාලරේඛා + privacy: + search: සොයන්න + privacy_policy: + title: රහස්යතා ප්රතිපත්තිය reactions: errors: limit_reached: විවිධ ප්රතික්රියා වල සීමාව ළඟා විය @@ -1124,7 +1191,7 @@ si: relationships: activity: ගිණුමේ ක්රියාකාරකම් dormant: නිදිමතයි - follow_selected_followers: තෝරාගත් අනුගාමිකයින් අනුගමනය කරන්න + follow_selected_followers: තේරූ අනුගාමිකයින් අනුගමනය කරන්න followers: අනුගාමිකයින් following: අනුගමනය invited: ආරාධනා කළා @@ -1132,11 +1199,11 @@ si: most_recent: මෑතකාලීන moved: මාරු කළා mutual: අන්යෝන්ය - primary: ප්රාථමික + primary: ප්රාථමික relationship: සම්බන්ධතාවය - remove_selected_domains: තෝරාගත් වසම් වලින් සියලුම අනුගාමිකයින් ඉවත් කරන්න - remove_selected_followers: තෝරාගත් අනුගාමිකයින් ඉවත් කරන්න - remove_selected_follows: තෝරාගත් පරිශීලකයින් අනුගමනය නොකරන්න + remove_selected_domains: තේරූ වසම් වල සියලුම අනුගාමිකයින් ඉවත් කරන්න + remove_selected_followers: තේරූ අනුගාමිකයින් ඉවත් කරන්න + remove_selected_follows: තේරූ අය අනුගමනය නොකරන්න status: ගිණුමේ තත්වය remote_follow: missing_resource: ඔබගේ ගිණුම සඳහා අවශ්ය යළි-යොමුවීම් URL එක සොයා ගැනීමට නොහැකි විය @@ -1146,56 +1213,62 @@ si: rss: content_warning: 'අන්තර්ගත අනතුරු ඇඟවීම:' descriptions: - account: '@%{acct}සිට පොදු පළ කිරීම්' - tag: '#%{hashtag}ටැග් කර ඇති පොදු පළ කිරීම්' + account: "@%{acct} වෙතින් ප්රසිද්ධ ලිපි" scheduled_statuses: - over_daily_limit: ඔබ අද දිනට නියමිත පළ කිරීම් %{limit} සීමාව ඉක්මවා ඇත - over_total_limit: ඔබ නියමිත පළ කිරීම් %{limit} සීමාව ඉක්මවා ඇත too_soon: නියමිත දිනය අනාගතයේ විය යුතුය sessions: activity: අවසාන ක්රියාකාරකම browser: අතිරික්සුව browsers: alipay: අලිපේ + blackberry: බ්ලැක්බෙරි chrome: ක්රෝම් edge: මයික්රොසොෆ්ට් එඩ්ගේ electron: ඉලෙක්ට්රෝන් firefox: ෆයර්ෆොක්ස් generic: නොදන්නා අතිරික්සුවකි + huawei_browser: හුආවේ අතිරික්සුව ie: ඉන්ටර්නෙට් එක්ස්ප්ලෝරර් micro_messenger: මයික්රොමැසෙන්ජර් - nokia: Nokia S40 Ovi බ්රව්සරය + nokia: නොකියා S40 Ovi අතිරික්සුව opera: ඔපෙරා otter: ඔටර් + phantom_js: PhantomJS qq: කියුකියු අතිරික්සුව safari: සෆාරි + uc_browser: UC අතිරික්සුව + unknown_browser: නොදන්නා අතිරික්සුවකි weibo: වෙයිබො - current_session: වත්මන් සැසිය - description: "%{browser} මත %{platform}" - explanation: මේවා දැනට ඔබගේ Mastodon ගිණුමට ලොග් වී ඇති වෙබ් බ්රව්සර් වේ. + current_session: වත්මන් වාරය + description: "%{platform} හි %{browser}" + explanation: ඔබගේ මාස්ටඩන් ගිණුමට පිවිසීම සඳහා භාවිතා කර තිබෙන අතිරික්සු. ip: අ.ජා. කෙ. (IP) platforms: adobe_air: ඇඩෝබි එයාර් android: ඇන්ඩ්රොයිඩ් + blackberry: බ්ලැක්බෙරි + chrome_os: ChromeOS firefox_os: ෆයර්ෆොක්ස් ඕඑස් ios: අයිඕඑස් + kai_os: KaiOS linux: ලිනක්ස් mac: මැක්ඕඑස් + unknown_platform: නොදන්නා වේදිකාවකි windows: වින්ඩෝස් windows_mobile: වින්ඩෝස් මොබයිල් windows_phone: වින්ඩෝස් පෝන් revoke: අවලංගු කරන්න - revoke_success: සැසිය සාර්ථකව අවලංගු කරන ලදී - title: සැසිවාර + revoke_success: වාරය සාර්ථකව අවලංගු කෙරිණි + title: වාර view_authentication_history: ඔබගේ ගිණුමේ සත්යාපන ඉතිහාසය බලන්න settings: account: ගිණුම account_settings: ගිණුමේ සැකසුම් aliases: ගිණුම් අන්වර්ථ නාමයන් appearance: පෙනුම - authorized_apps: අවසර ලත් යෙදුම් - back: Mastodon වෙත නැවත යන්න - delete: ගිණුම මකා දැමීම + authorized_apps: බලයලත් යෙදුම් + back: මාස්ටඩන් වෙත ආපසු + delete: ගිණුම මැකීම development: සංවර්ධනය edit_profile: පැතිකඩ සංස්කරණය export: දත්ත නිර්යාතය @@ -1204,13 +1277,12 @@ si: import_and_export: ආයාත හා නිර්යාත migrate: ගිණුම් සංක්රමණය notifications: දැනුම්දීම් - preferences: මනාප - profile: පැතිකඩ - relationships: අනුගාමිකයින් සහ අනුගාමිකයින් - statuses_cleanup: ස්වයංක්රීය පළ කිරීම් මකාදැමීම - strikes: මධ්යස්ථ වැඩ වර්ජන + preferences: අභිප්රේත + profile: ප්රසිද්ධ පැතිකඩ + relationships: අනුගමන හා අනුගාමික + statuses_cleanup: ස්වයංක්රීය ලිපි මැකීම two_factor_authentication: ද්වි සාධක Aut - webauthn_authentication: ආරක්ෂක යතුරු + webauthn_authentication: ආරක්ෂණ යතුරු statuses: attached: audio: @@ -1218,35 +1290,33 @@ si: other: "%{count} ශ්රව්ය" description: 'අමුණා ඇත: %{attached}' image: - one: "%{count} රූපය" - other: "පින්තූර %{count}" + one: රූප %{count} + other: රූප %{count} video: - one: "%{count} වීඩියෝ" - other: "වීඩියෝ %{count}" - boosted_from_html: '%{acct_link}සිට වැඩි කරන ලදී' + one: දෘශ්යක %{count} + other: දෘශ්යක %{count} content_warning: 'අන්තර්ගත අනතුරු ඇඟවීම: %{warning}' - default_language: අතුරු මුහුණත් භාෂාවට සමානයි + default_language: අතුරු මුහුණතේ භාෂාවම disallowed_hashtags: one: 'අනුමත නොකළ හැෂ් ටැගයක් අඩංගු විය: %{tags}' other: 'අනුමත නොකළ හැෂ් ටැග් අඩංගු විය: %{tags}' edited_at_html: සංස්කරණය %{date} errors: - in_reply_not_found: ඔබ පිළිතුරු දීමට උත්සාහ කරන පළ කිරීම පවතින බවක් නොපෙනේ. + in_reply_not_found: ඔබ පිළිතුරු දීමට තැත් කරන ලිපිය නොපවතින බව පෙනෙයි. open_in_web: වෙබයේ විවෘත කරන්න over_character_limit: අක්ෂර සීමාව %{max} ඉක්මවා ඇත pin_errors: - direct: සඳහන් කළ පරිශීලකයින්ට පමණක් පෙනෙන පළ කිරීම් ඇමිණිය නොහැක - limit: ඔබ දැනටමත් උපරිම පළ කිරීම් සංඛ්යාව අමුණා ඇත - ownership: වෙනත් කෙනෙකුගේ පළ කිරීමක් ඇමිණිය නොහැක - reblog: බූස්ට් එකක් ඇලවිය නොහැක + direct: සඳහන් කළ අයට පමණක් පෙනෙන ලිපි ඇමිණීමට නොහැකිය + limit: දැනටමත් මුදුනට ඇමිණිමට හැකි ලිපි සීමාවට ළඟා වී ඇත + ownership: වෙනත් අයගේ ලිපි ඇමිණීමට නොහැකිය poll: total_people: - one: "%{count} පුද්ගලයෙක්" - other: "පුද්ගලයන් %{count}" + one: පුද්ගලයින් %{count} + other: පුද්ගලයින් %{count} total_votes: - one: "%{count} ඡන්ද" - other: "ඡන්ද %{count} යි" - vote: ඡන්දය දෙන්න + one: ඡන්ද %{count} යි + other: ඡන්ද %{count} යි + vote: ඡන්දය show_more: තව පෙන්වන්න show_newer: අලුත්ම පෙන්වන්න show_older: පැරණි පෙන්වන්න @@ -1254,46 +1324,40 @@ si: title: '%{name}: "%{quote}"' visibilities: direct: සෘජු - private: අනුගාමිකයින්-පමණි + private: අනුගාමිකයින් පමණි private_long: අනුගාමිකයින්ට පමණක් පෙන්වන්න public: ප්රසිද්ධ public_long: හැමෝටම පේනවා unlisted: ලැයිස්තුගත නොකළ unlisted_long: සෑම කෙනෙකුටම දැකිය හැක, නමුත් පොදු කාලරාමුවෙහි ලැයිස්තුගත කර නොමැත statuses_cleanup: - enabled: පැරණි පළ කිරීම් ස්වයංක්රීයව මකන්න - enabled_hint: ඔබේ පළ කිරීම් පහත ව්යතිරේකවලින් එකකට ගැලපෙන්නේ නම් මිස, ඒවා නිශ්චිත වයස් සීමාවකට ළඟා වූ පසු ස්වයංක්රීයව මකයි - exceptions: ව්යතිරේක - explanation: පළ කිරීම් මකා දැමීම මිල අධික මෙහෙයුමක් වන බැවින්, සේවාදායකය වෙනත් ආකාරයකින් කාර්යබහුල නොවන විට කාලයත් සමඟ මෙය සෙමින් සිදු කෙරේ. මෙම හේතුව නිසා, ඔබේ පළ කිරීම් වයස් සීමාවට ළඟා වූ පසු ටික වේලාවකට පසුව මකා දැමිය හැක. - ignore_favs: ප්රියතමයන් නොසලකා හරින්න - ignore_reblogs: වැඩි කිරීම් නොසලකා හරින්න + enabled: පරණ ලිපි ස්වයංක්රීයව මකන්න + exceptions: හැර දැමීම් + ignore_favs: ප්රියතමයන් නොසලකන්න interaction_exceptions: අන්තර්ක්රියා මත පදනම් වූ ව්යතිරේක - interaction_exceptions_explanation: පළ කිරීම් වරක් ඒවා ඉක්මවා ගිය පසු ප්රියතම හෝ බූස්ට් සීමාවට පහළින් ගියහොත් ඒවා මැකීමට සහතිකයක් නොමැති බව සලකන්න. - keep_direct: සෘජු පණිවිඩ තබා ගන්න - keep_direct_hint: ඔබගේ සෘජු පණිවිඩ කිසිවක් මකන්නේ නැත - keep_media: මාධ්ය ඇමුණුම් සමඟ පළ කිරීම් තබා ගන්න - keep_media_hint: මාධ්ය ඇමුණුම් ඇති ඔබේ පළ කිරීම් කිසිවක් මකන්නේ නැත + keep_direct: සෘජු පණිවිඩ තබාගන්න + keep_direct_hint: ඔබගේ සෘජු පණිවිඩ කිසිවක් නොමැකෙයි + keep_media: මාධ්ය ඇමුණුම් සහිත ලිපි තබාගන්න + keep_media_hint: මාධ්ය ඇමුණුම් සහිත ඔබගේ ලිපි කිසිවක් නොමැකෙයි keep_pinned: ඇමිණූ ලිපි තබාගන්න keep_pinned_hint: ඔබ ඇමිණූ ලිපි කිසිවක් නොමැකෙයි - keep_polls: ඡන්ද තබා ගන්න - keep_polls_hint: ඔබගේ ඡන්ද විමසීම් කිසිවක් මකන්නේ නැත - keep_self_bookmark: ඔබ පිටු සලකුණු කළ පළ කිරීම් තබා ගන්න - keep_self_bookmark_hint: ඔබ ඔබේම පළ කිරීම් පිටු සලකුණු කර ඇත්නම් ඒවා මකා නොදමන්න - keep_self_fav: ඔබ කැමති පළ කිරීම් තබා ගන්න - keep_self_fav_hint: ඔබ ඒවාට කැමති නම් ඔබේම පළ කිරීම් මකා නොදමන්න + keep_polls: මත විමසුම් තබාගන්න + keep_polls_hint: ඔබගේ මත විමසුම් නොමැකෙයි + keep_self_bookmark: ඔබ පොත්යොමු තැබූ ලිපි තබාගන්න + keep_self_bookmark_hint: ඔබගේම ලිපි වලට පොත්යොමු තබා ඇත්නම් ඒවා මකා නොදැමෙයි + keep_self_fav: ඔබ ප්රිය කළ ලිපි තබාගන්න + keep_self_fav_hint: ඔබගේම ලිපි වලට ප්රිය කර ඇත්නම් ඒවා මකා නොදැමෙයි min_age: - '1209600': සති 2 යි - '15778476': මාස 6 යි - '2629746': මාස 1 යි - '31556952': වසර 1 යි - '5259492': මාස 2 ක් - '604800': 1 සතිය - '63113904': අවුරුදු 2 ක් - '7889238': මාස 3 යි - min_age_label: වයස් සීමාව - min_favs: අඩුම තරමින් පෝස්ට් ප්රිය කරන ලෙස තබා ගන්න - min_reblogs: අඩුම තරමේ පෝස්ට් බූස්ට් කරගෙන තියාගන්න - min_reblogs_hint: අඩුම තරමින් මෙම වාර ගණන වැඩි කර ඇති ඔබගේ පළ කිරීම් කිසිවක් මකා නොදමන්න. බූස්ට් ගණන නොතකා පළ කිරීම් මැකීමට හිස්ව තබන්න + '1209600': සති 2 + '15778476': මාස 6 + '2629746': මාස 1 + '31556952': අවුරුදු 1 + '5259492': මාස 2 + '604800': සති 1 + '63113904': අවුරුදු 2 + '7889238': මාස 3 + min_age_label: කාල සීමාව + min_favs: අවම වශයෙන් ප්රිය කළ ලිපි තබාගන්න stream_entries: sensitive_content: සංවේදී අන්තර්ගතයකි strikes: @@ -1302,22 +1366,26 @@ si: tags: does_not_match_previous_name: පෙර නමට නොගැලපේ themes: - contrast: Mastodon (ඉහළ වෙනස) - default: මැස්ටෝඩන් (අඳුරු) - mastodon-light: මැස්ටෝඩන් (ආලෝකය) + default: මාස්ටඩන් (අඳුරු) + mastodon-light: මාස්ටඩන් (දීප්ත) + time: + formats: + default: "%Y %b %d, %H:%M" + month: "%Y %b" + with_time_zone: "%Y %b %d, %H:%M %Z" two_factor_authentication: - add: එකතු කරන්න + add: එකතු disable: 2FA අබල කරන්න disabled_success: ද්වි-සාධක සත්යාපනය සාර්ථකව අබල කර ඇත edit: සංස්කරණය enabled: ද්වි-සාධක සත්යාපනය සක්රීය කර ඇත enabled_success: ද්වි-සාධක සත්යාපනය සාර්ථකව සබල කර ඇත - generate_recovery_codes: ප්රතිසාධන කේත ජනනය කරන්න + generate_recovery_codes: ප්රතිසාධන කේත උත්පාදනය කරන්න lost_recovery_codes: ඔබගේ දුරකථනය නැති වුවහොත් ඔබගේ ගිණුමට ප්රවේශය නැවත ලබා ගැනීමට ප්රතිසාධන කේත ඔබට ඉඩ සලසයි. ඔබට ඔබේ ප්රතිසාධන කේත නැති වී ඇත්නම්, ඔබට ඒවා මෙහි නැවත උත්පාදනය කළ හැක. ඔබගේ පැරණි ප්රතිසාධන කේත අවලංගු වනු ඇත. - methods: ද්වි සාධක ක්රම + methods: ද්වි සාධක ක්රම otp: Authenticator යෙදුම - recovery_codes: උපස්ථ ප්රතිසාධන කේත - recovery_codes_regenerated: ප්රතිසාධන කේත සාර්ථකව ප්රතිජනනය කරන ලදී + recovery_codes: ප්රතිසාධන කේත උපස්ථය + recovery_codes_regenerated: ප්රතිසාධන කේත නැවත උත්පාදනය කෙරිණි recovery_instructions_html: ඔබට කවදා හෝ ඔබගේ දුරකථනයට ප්රවේශය අහිමි වුවහොත්, ඔබගේ ගිණුමට ප්රවේශය නැවත ලබා ගැනීමට පහත ප්රතිසාධන කේත වලින් එකක් භාවිතා කළ හැක. ප්රතිසාධන කේත ආරක්ෂිතව තබා ගන්න. උදාහරණයක් ලෙස, ඔබට ඒවා මුද්රණය කර වෙනත් වැදගත් ලේඛන සමඟ ගබඩා කළ හැකිය. webauthn: ආරක්ෂණ යතුරු user_mailer: @@ -1331,11 +1399,11 @@ si: subject: '%{date} සිට ඔබගේ අභියාචනය ප්රතික්ෂේප කර ඇත' title: අභියාචනය ප්රතික්ෂේප විය backup_ready: - explanation: ඔබ ඔබේ Mastodon ගිණුමේ සම්පූර්ණ උපස්ථයක් ඉල්ලා ඇත. එය දැන් බාගත කිරීම සඳහා සූදානම්! + explanation: ඔබගේ මාස්ටඩන් ගිණුමේ පූර්ණ උපස්ථයක් ඉල්ලා ඇත. එය දැන් බාගැනීමට හැකිය! subject: ඔබගේ සංරක්ෂිතය බාගැනීමට සූදානම්ය title: සංරක්ෂිත රැගෙන යාම suspicious_sign_in: - change_password: ඔබගේ මුරපදය වෙනස් කරන්න + change_password: මුරපදය වෙනස් කරන්න details: 'පුරනය වීමේ විස්තර මෙන්න:' explanation: අපි නව IP ලිපිනයකින් ඔබගේ ගිණුමට පුරනය වීමක් අනාවරණය කරගෙන ඇත. further_actions_html: මෙය ඔබ නොවේ නම්, අපි ඔබට වහාම %{action} ලෙස නිර්දේශ කර ඔබගේ ගිණුම සුරක්ෂිතව තබා ගැනීමට සාධක දෙකක සත්යාපනය සබල කරන්න. @@ -1345,40 +1413,36 @@ si: appeal: අභියාචනයක් ඉදිරිපත් කරන්න appeal_description: මෙය දෝෂයක් බව ඔබ විශ්වාස කරන්නේ නම්, ඔබට %{instance}හි කාර්ය මණ්ඩලයට අභියාචනයක් ඉදිරිපත් කළ හැක. categories: - spam: ආයාචිත තැපැල් + spam: ආයාචිත violation: අන්තර්ගතය පහත ප්රජා මාර්ගෝපදේශ උල්ලංඝනය කරයි explanation: - delete_statuses: ඔබගේ සමහර පළ කිරීම් ප්රජා මාර්ගෝපදේශ එකක් හෝ කිහිපයක් උල්ලංඝනය කරන බව සොයා ගෙන ඇති අතර පසුව %{instance}හි උපපරිපාලකයින් විසින් ඉවත් කර ඇත. disable: ඔබට තවදුරටත් ඔබගේ ගිණුම භාවිතා කළ නොහැක, නමුත් ඔබගේ පැතිකඩ සහ අනෙකුත් දත්ත නොවෙනස්ව පවතී. ඔබට ඔබගේ දත්තවල උපස්ථයක් ඉල්ලා සිටීමට, ගිණුම් සැකසීම් වෙනස් කිරීමට හෝ ඔබගේ ගිණුම මකා දැමීමට හැකිය. - mark_statuses_as_sensitive: ඔබගේ සමහර පළ කිරීම් %{instance}හි පරිපාලකයින් විසින් සංවේදී ලෙස සලකුණු කර ඇත. මෙයින් අදහස් කරන්නේ පෙරදසුනක් දර්ශනය වීමට පෙර පුද්ගලයින්ට පළ කිරීම් වල මාධ්ය තට්ටු කිරීමට අවශ්ය වනු ඇති බවයි. අනාගතයේදී පළ කිරීමේදී ඔබට මාධ්ය සංවේදී ලෙස සලකුණු කළ හැක. - sensitive: මෙතැන් සිට, ඔබගේ උඩුගත කරන ලද සියලුම මාධ්ය ගොනු සංවේදී ලෙස සලකුණු කර ක්ලික්-හරහා අනතුරු ඇඟවීමක් පිටුපස සඟවනු ඇත. - silence: ඔබට තවමත් ඔබගේ ගිණුම භාවිතා කළ හැකි නමුත් දැනටමත් ඔබව අනුගමනය කරන පුද්ගලයින් පමණක් මෙම සේවාදායකයේ ඔබගේ පළ කිරීම් දකිනු ඇති අතර, විවිධ සොයාගැනීම් විශේෂාංග වලින් ඔබව බැහැර කරනු ලැබිය හැක. කෙසේ වෙතත්, අනෙක් අය තවමත් ඔබව අතින් අනුගමනය කළ හැක. + sensitive: මේ මොහොත් සිට ඔබ උඩුගත කරන සියලුම මාධ්ය ගොනු සංවේදී ලෙස සලකා අවවාදයක් පිටුපස සඟවනු ඇත. suspend: ඔබට තවදුරටත් ඔබගේ ගිණුම භාවිතා කළ නොහැකි අතර, ඔබගේ පැතිකඩ සහ අනෙකුත් දත්ත තවදුරටත් ප්රවේශ විය නොහැක. දින 30කින් පමණ දත්ත සම්පූර්ණයෙන් ඉවත් කරන තෙක් ඔබට තවමත් ඔබේ දත්තවල උපස්ථයක් ඉල්ලා සිටීමට පුරනය විය හැක, නමුත් ඔබව අත්හිටුවීම මගහැර යාම වැළැක්වීමට අපි මූලික දත්ත කිහිපයක් රඳවා ගන්නෙමු. reason: 'හේතුව:' - statuses: 'උපුටා දක්වන ලද පළ කිරීම්:' subject: - delete_statuses: '%{acct} හි ඔබගේ පළ කිරීම් ඉවත් කර ඇත' + delete_statuses: "%{acct} හි ඔබගේ ලිපිය ඉවත් කර ඇත" disable: ඔබගේ ගිණුම %{acct} කර ඇත - mark_statuses_as_sensitive: '%{acct} හි ඔබගේ පළ කිරීම් සංවේදී ලෙස සලකුණු කර ඇත' - none: '%{acct}සඳහා අනතුරු ඇඟවීම' - sensitive: '%{acct} හි ඔබගේ පළ කිරීම් මෙතැන් සිට සංවේදී ලෙස සලකුණු කෙරේ' + mark_statuses_as_sensitive: ඔබගේ %{acct} ලිපි සංවේදී බව සලකුණු කර ඇත + none: "%{acct} සඳහා අවවාදය" + sensitive: ඔබගේ %{acct} ලිපිය මේ මොහොතේ සිට සංවේදී ලෙස සලකයි silence: ඔබගේ ගිණුම %{acct} සීමා කර ඇත suspend: ඔබගේ ගිණුම %{acct} අත්හිටුවා ඇත title: - delete_statuses: පළ කිරීම් ඉවත් කරන ලදී + delete_statuses: ලිපි ඉවත් කර ඇත disable: ගිණුම නිශ්චල කර ඇත - mark_statuses_as_sensitive: පළ කිරීම් සංවේදී ලෙස ලකුණු කර ඇත + mark_statuses_as_sensitive: ලිපි සංවේදී බව සලකුණු කර ඇත none: අවවාදයයි - sensitive: ගිණුම සංවේදී ලෙස ලකුණු කර ඇත - silence: ගිණුම සීමා සහිතයි + sensitive: ගිණුම සංවේදී බව යොදා ඇත + silence: ගිණුම සීමා කර ඇත suspend: ගිණුම අත්හිටුවා ඇත welcome: - edit_profile_action: සැකසුම් පැතිකඩ + edit_profile_action: පැතිකඩ පිහිටුවන්න explanation: ඔබ ආරම්භ කිරීමට උපදෙස් කිහිපයක් මෙන්න - final_action: පළ කිරීම ආරම්භ කරන්න + final_action: ලිපි පළ කරන්න full_handle: ඔබේ සම්පූර්ණ හසුරුව full_handle_hint: මෙය ඔබ ඔබේ මිතුරන්ට පවසනු ඇත, එවිට ඔවුන්ට වෙනත් සේවාදායකයකින් ඔබට පණිවිඩ යැවීමට හෝ අනුගමනය කිරීමට හැකිය. - subject: Mastodon වෙත සාදරයෙන් පිළිගනිමු + subject: මාස්ටඩන් වෙත පිළිගනිමු title: නැවට සාදරයෙන් පිළිගනිමු, %{name}! users: follow_limit_reached: ඔබට පුද්ගලයින් %{limit} කට වඩා අනුගමනය කළ නොහැක @@ -1387,9 +1451,10 @@ si: seamless_external_login: ඔබ බාහිර සේවාවක් හරහා ලොග් වී ඇත, එබැවින් මුරපදය සහ ඊමේල් සැකසුම් නොමැත. signed_in_as: 'මෙසේ පුරනය වී ඇත:' verification: - verification: සත්යාපනය + here_is_how: කෙසේදැයි මෙන්න + verification: සත්යාපනය webauthn_credentials: - add: නව ආරක්ෂක යතුර එක් කරන්න + add: නව ආරක්ෂණ යතුර එක් කරන්න create: error: ඔබගේ ආරක්ෂක යතුර එක් කිරීමේ ගැටලුවක් ඇති විය. කරුණාකර නැවත උත්සාහ කරන්න. success: ඔබගේ ආරක්ෂක යතුර සාර්ථකව එක් කරන ලදී. @@ -1399,9 +1464,9 @@ si: destroy: error: ඔබගේ ආරක්ෂක යතුර මැකීමේ ගැටලුවක් ඇති විය. කරුණාකර නැවත උත්සාහ කරන්න. success: ඔබගේ ආරක්ෂක යතුර සාර්ථකව මකා ඇත. - invalid_credential: වලංගු නොවන ආරක්ෂක යතුර + invalid_credential: ආරක්ෂණ යතුර වලංගු නොවේ nickname_hint: ඔබගේ නව ආරක්ෂක යතුරේ අන්වර්ථ නාමය ඇතුළත් කරන්න not_enabled: ඔබ තවමත් WebAuthn සබල කර නැත not_supported: මෙම බ්රවුසරය ආරක්ෂක යතුරු සඳහා සහය නොදක්වයි otp_required: ආරක්ෂක යතුරු භාවිතා කිරීමට කරුණාකර පළමුව ද්වි-සාධක සත්යාපනය සක්රීය කරන්න. - registered_on: '%{date}හි ලියාපදිංචි වී ඇත' + registered_on: "%{date} දී ලියාපදිංචි වී ඇත" diff --git a/config/locales/simple_form.cy.yml b/config/locales/simple_form.cy.yml index b1bdc13be1..ebd590b8a7 100644 --- a/config/locales/simple_form.cy.yml +++ b/config/locales/simple_form.cy.yml @@ -291,7 +291,11 @@ cy: reblog: Mae rhywun wedi hybu eich postiad report: Cyflwynwyd adroddiad newydd software_updates: + all: Rhoi gwybod am bob ddiweddariad + critical: Rhoi gwybod am ddiweddariadau critigol yn unig label: Mae fersiwn Mastodon newydd ar gael + none: Byth rhoi gwybod am ddiweddariadau (nid argymhellir) + patch: Rhoi gwybod am ddiweddariadau trwsio byg trending_tag: Mae pwnc llosg newydd angen adolygiad rule: text: Rheol diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 2012e1a7f7..2b777aaa2b 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -87,6 +87,7 @@ en: filters: action: Chose which action to perform when a post matches the filter actions: + half_warn: Hide the filtered content (exclude account info) behind a warning mentioning the filter's title hide: Completely hide the filtered content, behaving as if it did not exist warn: Hide the filtered content behind a warning mentioning the filter's title form_admin_settings: @@ -226,6 +227,7 @@ en: phrase: Keyword or phrase setting_advanced_layout: Enable advanced web interface setting_aggregate_reblogs: Group boosts in timelines + setting_allow_quote: Allow quote your posts setting_always_send_emails: Always send e-mail notifications setting_auto_play_gif: Auto-play animated GIFs setting_bio_markdown: Enable profile markdown @@ -259,6 +261,7 @@ en: mutuals_only: Mutuals only outside_only: Followings or followers only setting_expand_spoilers: Always expand posts marked with content warnings + setting_hide_blocking_quote: Hide posts which have a quote written by the user you are blocking setting_hide_followers_count: Hide followers count setting_hide_following_count: Hide following count setting_hide_network: Hide your social graph @@ -274,6 +277,8 @@ en: setting_send_without_domain_blocks: Send your post to all server with administrator set as rejecting-post-server for protect you [DEPRECATED] setting_show_application: Disclose application used to send posts setting_show_emoji_reaction_on_timeline: Show all stamps on timeline + setting_show_quote_in_home: Show quotes in home, list or antenna timelines + setting_show_quote_in_public: Show quotes in public timelines setting_simple_timeline_menu: Reduce post menu on timeline setting_single_ref_to_quote: Deliver single reference to other server as quote setting_stay_privacy: Not change privacy after post @@ -308,11 +313,13 @@ en: name: Hashtag filters: actions: + half_warn: Half hide with a warning hide: Hide completely warn: Hide with a warning options: exclude_follows: Exclude following users exclude_localusers: Exclude local users + with_quote: Also check quote or references form_admin_settings: activity_api_enabled: Publish aggregate statistics about user activity in the API backups_retention_period: User archive retention period @@ -347,6 +354,7 @@ en: trendable_by_default: Allow trends without prior review trends: Enable trends trends_as_landing_page: Use trends as the landing page + unlocked_friend: Accept all friend server follows automatically interactions: must_be_follower: Block notifications from non-followers must_be_following: Block notifications from people you don't follow @@ -371,6 +379,7 @@ en: follow_request: Someone requested to follow you mention: Someone mentioned you pending_account: New account needs review + pending_friend_server: New friend server needs review reblog: Someone boosted your post report: New report is submitted software_updates: diff --git a/config/locales/simple_form.es-MX.yml b/config/locales/simple_form.es-MX.yml index 8dfac1d516..4a79314825 100644 --- a/config/locales/simple_form.es-MX.yml +++ b/config/locales/simple_form.es-MX.yml @@ -322,7 +322,7 @@ es-MX: url: URL de Endpoint 'no': 'No' not_recommended: No recomendado - overridden: Sobrescrito + overridden: Reemplazado recommended: Recomendado required: mark: "*" diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index 2a03f00fdb..41057cbed1 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -100,6 +100,7 @@ ja: filters: action: 投稿がフィルタに一致したときに実行するアクションを選択 actions: + half_warn: フィルターに一致した投稿の本文のみを非表示にし、フィルターのタイトルを含む警告を表示します hide: フィルタに一致した投稿を完全に非表示にします warn: フィルタに一致した投稿を非表示にし、フィルタのタイトルを含む警告を表示します form_admin_settings: @@ -239,6 +240,7 @@ ja: phrase: キーワードまたはフレーズ setting_advanced_layout: 上級者向けUIを有効にする setting_aggregate_reblogs: ブーストをまとめる + setting_allow_quote: 引用を許可する setting_always_send_emails: 常にメール通知を送信する setting_auto_play_gif: アニメーションGIFを自動再生する setting_bio_markdown: プロフィールのMarkdownを有効にする @@ -273,6 +275,7 @@ ja: setting_emoji_reaction_streaming_notify_impl2: Nyastodon, Catstodon, glitch-soc互換のスタンプ機能を有効にする setting_enable_emoji_reaction: スタンプ機能を使用する setting_expand_spoilers: 閲覧注意としてマークされた投稿を常に展開する + setting_hide_blocking_quote: ブロックしたユーザーの投稿を引用した投稿を隠す setting_hide_followers_count: フォロワー数を隠す setting_hide_following_count: フォロー数を隠す setting_hide_network: 繋がりを隠す @@ -280,6 +283,8 @@ ja: setting_hide_statuses_count: 投稿数を隠す setting_link_preview: リンクのプレビューを生成する setting_lock_follow_from_bot: botからのフォローを承認制にする + setting_show_quote_in_home: ホーム・リスト・アンテナなどで引用を表示する + setting_show_quote_in_public: 公開タイムライン(ローカル・連合)で引用を表示する setting_stay_privacy: 投稿時に公開範囲を保存する setting_noai: 自分のコンテンツのAI学習利用に対して不快感を表明する setting_public_post_to_unlisted: サードパーティから公開範囲「公開」で投稿した場合、「ローカル公開」に変更する @@ -323,11 +328,13 @@ ja: name: ハッシュタグ filters: actions: + half_warn: アカウント名だけを出し、本文は警告で隠す hide: 完全に隠す warn: 警告付きで隠す options: exclude_follows: フォロー中のユーザーをフィルターの対象にしない exclude_localusers: ローカルユーザーをフィルターの対象にしない + with_quote: 引用・参照の内容をフィルターの対象に含める form_admin_settings: activity_api_enabled: APIでユーザーアクティビティに関する集計統計を公開する backups_retention_period: ユーザーアーカイブの保持期間 @@ -362,6 +369,7 @@ ja: trendable_by_default: 審査前のトレンドの掲載を許可する trends: トレンドを有効にする trends_as_landing_page: 新規登録画面にトレンドを表示する + unlocked_friend: 全てのフレンドサーバー申請を自動承認する interactions: must_be_follower: フォロワー以外からの通知をブロック must_be_following: フォローしていないユーザーからの通知をブロック @@ -386,6 +394,7 @@ ja: follow_request: フォローリクエストを受けた時 mention: 返信が来た時 pending_account: 新しいアカウントの承認が必要な時 + pending_friend_server: 新しいフレンドサーバーの承認が必要な時 reblog: 投稿がブーストされた時 report: 新しい通報が送信された時 software_updates: diff --git a/config/locales/simple_form.my.yml b/config/locales/simple_form.my.yml index ebf9d96e13..69afe2116d 100644 --- a/config/locales/simple_form.my.yml +++ b/config/locales/simple_form.my.yml @@ -322,6 +322,7 @@ my: url: URL ဆုံးမှတ် 'no': 'မလုပ်ပါ' not_recommended: ထောက်ခံထားမှုမရှိ + overridden: ပယ်ဖျက် recommended: ထောက်ခံထားပြီး required: mark: "*" diff --git a/config/locales/simple_form.si.yml b/config/locales/simple_form.si.yml index 636d0ec995..c0871b9059 100644 --- a/config/locales/simple_form.si.yml +++ b/config/locales/simple_form.si.yml @@ -6,13 +6,11 @@ si: account_migration: acct: ඔබට යාමට අවශ්ය ගිණුමේ username@domain සඳහන් කරන්න account_warning_preset: - text: ඔබට URL, හෑෂ් ටැග් සහ සඳහන් කිරීම් වැනි පෝස්ට් සින්ටැක්ස් භාවිතා කළ හැක + text: ඔබට ඒ.ස.නි., පූරක අනන්යන සහ සැඳහුම් වැනි ලිපි පද ගැළපුම් භාවිතා කිරීමට හැකිය title: විකල්ප. ලබන්නාට නොපෙනේ admin_account_action: - include_statuses: මධ්යස්ථ ක්රියාව හෝ අනතුරු ඇඟවීමට හේතු වී ඇත්තේ කුමන පළ කිරීම්දැයි පරිශීලකයා දකිනු ඇත send_email_notification: පරිශීලකයාට ඔවුන්ගේ ගිණුම සමඟ සිදු වූ දේ පිළිබඳ පැහැදිලි කිරීමක් ලැබෙනු ඇත - text_html: විකල්ප. ඔබට post syntax භාවිතා කළ හැක. කාලය ඉතිරි කර ගැනීම සඳහා ඔබට අනතුරු ඇඟවීමේ කළ හැක - type_html: %{acct}සමඟ කළ යුතු දේ තෝරන්න + type_html: "%{acct}සමඟ කළ යුතු දේ තෝරන්න" types: disable: පරිශීලකයාගේ ගිණුම භාවිතා කිරීමෙන් වළක්වන්න, නමුත් ඔවුන්ගේ අන්තර්ගතය මකා දැමීම හෝ සඟවන්න එපා. none: වෙනත් ක්රියාවක් අවුලුවාලීමකින් තොරව, පරිශීලකයාට අනතුරු ඇඟවීමක් යැවීමට මෙය භාවිතා කරන්න. @@ -23,7 +21,7 @@ si: ends_at: විකල්ප. මෙම අවස්ථාවේදී නිවේදනය ස්වයංක්රීයව ප්රකාශනය කිරීමෙන් ඉවත් වනු ඇත scheduled_at: නිවේදනය වහාම ප්රකාශයට පත් කිරීමට හිස්ව තබන්න starts_at: විකල්ප. ඔබගේ නිවේදනය නිශ්චිත කාල පරාසයකට බැඳී ඇත්නම් - text: ඔබට post syntax භාවිතා කළ හැක. කරුණාකර පරිශීලකයාගේ තිරය මත නිවේදනය ලබා ගන්නා ඉඩ ගැන සැලකිලිමත් වන්න + text: ඔබට ලිපි පද ගැළපුම් භාවිතා කිරීමට හැකිය. කරුණාකර නිවේදනයෙන් පරිශ්රීලකයින්ගේ තිරයේ ඉඩ කෙතරම් ඇහිරෙනවා ද පිළිබඳව සැලකිලිමත් වන්න appeal: text: ඔබට වර්ජනයකට අභියාචනා කළ හැක්කේ එක් වරක් පමණි defaults: @@ -37,17 +35,16 @@ si: email: ඔබට තහවුරු කිරීමේ විද්යුත් තැපෑලක් එවනු ලැබේ header: PNG, GIF හෝ JPG. වැඩිම %{size}. %{dimensions}px දක්වා අඩු කරනු ඇත inbox_url: ඔබට භාවිතා කිරීමට අවශ්ය රිලේ හි මුල් පිටුවෙන් URL එක පිටපත් කරන්න - irreversible: පෙරහන පසුව ඉවත් කළද, පෙරූ පළ කිරීම් ආපසු හැරවිය නොහැකි ලෙස අතුරුදහන් වනු ඇත - locale: පරිශීලක අතුරුමුහුණතේ භාෂාව, ඊමේල් සහ තල්ලු දැනුම්දීම් + irreversible: පෙරහන පසුව ඉවත් කළ ද, පෙරූ ලිපි අප්රතිවර්ත්යව අතුරුදහන් වනු ඇත + locale: වි-තැපැල්, තල්ලු දැනුම්දීම් සහ පරිශ්රීලක අතුරුමුහුණතේ භාෂාව password: අවම වශයෙන් අක්ෂර 8 ක් භාවිතා කරන්න - phrase: පළ කිරීමක පෙළ හෝ අන්තර්ගත අනතුරු ඇඟවීම නොසලකා ගැලපේ + phrase: ලිපිවල පෙළ හෝ අන්තර්ගත අවවාද නොසලකා ගැළපෙනු ඇත scopes: යෙදුමට ප්රවේශ වීමට ඉඩ දෙන්නේ කුමන API වලටද. ඔබ ඉහළ මට්ටමේ විෂය පථයක් තෝරා ගන්නේ නම්, ඔබට තනි ඒවා තෝරා ගැනීමට අවශ්ය නොවේ. - setting_aggregate_reblogs: මෑතකදී බූස්ට් කරන ලද පළ කිරීම් සඳහා නව බූස්ට් පෙන්වන්න එපා (අලුතින් ලැබුණු බූස්ට් වලට පමණක් බලපායි) - setting_always_send_emails: සාමාන්යයෙන් ඔබ Mastodon සක්රියව භාවිතා කරන විට විද්යුත් තැපැල් දැනුම්දීම් නොයවනු ඇත + setting_always_send_emails: ඔබ නිතර මාස්ටඩන් භාවිතා කරන විට වි-තැපැල් දැනුම්දීම් නොලැබෙයි setting_default_sensitive: සංවේදී මාධ්ය පෙරනිමියෙන් සඟවා ඇති අතර ක්ලික් කිරීමකින් හෙළිදරව් කළ හැක - setting_display_media_default: සංවේදී ලෙස සලකුණු කළ මාධ්ය සඟවන්න - setting_display_media_hide_all: සෑම විටම මාධ්ය සඟවන්න - setting_display_media_show_all: සෑම විටම මාධ්ය පෙන්වන්න + setting_display_media_default: සංවේදී බව සලකුණු කළ මාධ්ය සඟවන්න + setting_display_media_hide_all: සැමවිට මාධ්ය සඟවන්න + setting_display_media_show_all: සැමවිට මාධ්ය පෙන්වන්න setting_use_blurhash: අනුක්රමණ සැඟවුණු දෘශ්යවල වර්ණ මත පදනම් වන නමුත් ඕනෑම විස්තරයක් අපැහැදිලි කරයි setting_use_pending_items: සංග්රහය ස්වයංක්රීයව අනුචලනය කරනවා වෙනුවට ක්ලික් කිරීමක් පිටුපස කාලරේඛා යාවත්කාලීන සඟවන්න whole_word: මූල පදය හෝ වාක්ය ඛණ්ඩය අක්ෂරාංක පමණක් වන විට, එය යෙදෙන්නේ එය සම්පූර්ණ වචනයට ගැලපේ නම් පමණි @@ -57,14 +54,14 @@ si: domain: මෙය විද්යුත් තැපැල් ලිපිනයේ හෝ එය භාවිතා කරන MX වාර්තාවේ පෙන්වන ඩොමේන් නාමය විය හැක. ලියාපදිංචි වූ පසු ඒවා පරීක්ෂා කරනු ලැබේ. with_dns_records: ලබා දී ඇති වසමේ DNS වාර්තා විසඳීමට උත්සාහ කරන අතර ප්රතිඵල ද අවහිර කරනු ලැබේ filters: - action: පළ කිරීමක් පෙරහනට ගැළපෙන විට සිදු කළ යුතු ක්රියාව තෝරන්න + action: ලිපියක් පෙරහනට ගැළපෙන විට ඉටු විය යුතු ක්රියාමාර්ගය තෝරන්න actions: hide: පෙරහන් කළ අන්තර්ගතය සම්පූර්ණයෙන්ම සඟවන්න, එය නොපවතින ලෙස හැසිරෙන්න warn: පෙරහන මාතෘකාව සඳහන් කරන අනතුරු ඇඟවීමක් පිටුපස පෙරූ අන්තර්ගතය සඟවන්න form_challenge: current_password: ඔබ ආරක්ෂිත ප්රදේශයකට ඇතුල් වේ imports: - data: CSV ගොනුව වෙනත් Mastodon සේවාදායකයකින් අපනයනය කරන ලදී + data: CSV ගොනුව වෙනත් මාස්ටඩන් සේවාදායකයකින් නිර්යාත කර ඇත invite_request: text: මෙය ඔබගේ අයදුම්පත සමාලෝචනය කිරීමට අපට උපකාරී වනු ඇත ip_block: @@ -83,7 +80,7 @@ si: tag: name: ඔබට අකුරු වල ආවරණය පමණක් වෙනස් කළ හැකිය, උදාහරණයක් ලෙස, එය වඩාත් කියවිය හැකි කිරීමට user: - chosen_languages: පරීක්ෂා කළ විට, තෝරාගත් භාෂාවලින් පළ කිරීම් පමණක් පොදු කාලරේඛා තුළ සංදර්ශන කෙරේ + chosen_languages: සබල නම්, තෝරාගත් භාෂාවල ලිපි පමණක් ප්රසිද්ධ කාල රේඛාවේ දිස්වේ webhook: events: යැවීමට සිදුවීම් තෝරන්න url: සිදුවීම් යවනු ලබන ස්ථානය @@ -92,22 +89,24 @@ si: fields: name: නම්පත value: අන්තර්ගතය + show_collections: අනුගමන හා අනුගාමිකයින් පැතිකඩෙහි පෙන්වන්න + unlocked: නව අනුගාමිකයින් ස්වයංක්රීයව පිළිගන්න account_alias: acct: පැරණි ගිණුමේ හැසිරවීම account_migration: acct: නව ගිණුමේ හැසිරවීම account_warning_preset: text: පෙර සැකසූ පෙළ - title: ශීර්ෂය + title: සිරැසිය admin_account_action: - include_statuses: විද්යුත් තැපෑලෙහි වාර්තා කරන ලද පළ කිරීම් ඇතුළත් කරන්න + include_statuses: වි-තැපෑලට වාර්තා කරන ලද ලිපි ද ඇතුළත් කරන්න send_email_notification: විද්යුත් තැපෑලෙන් පරිශීලකයාට දැනුම් දෙන්න text: අභිරුචි අනතුරු ඇඟවීම type: ක්රියාමාර්ගය types: disable: කැටි කරන්න none: අනතුරු ඇඟවීමක් යවන්න - sensitive: පවතී + sensitive: සංවේදීතාව silence: සීමාව suspend: අත්හිටුවන්න warning_preset_id: අනතුරු ඇඟවීමේ පෙරසිටුවක් භාවිතා කරන්න @@ -120,14 +119,14 @@ si: appeal: text: මෙම තීරණය ආපසු හැරවිය යුත්තේ මන්දැයි පැහැදිලි කරන්න defaults: - autofollow: ඔබගේ ගිණුම අනුගමනය කිරීමට ආරාධනා කරන්න - avatar: අවතාරය - bot: මෙය ස්වයං ක්රමලේඛගත ගිණුමකි - chosen_languages: භාෂා පෙරහන් කරන්න - confirm_new_password: නව මුර පදය තහවුරු කරන්න - confirm_password: මුරපදය තහවුරු කර ඇත - context: සන්දර්භ පෙරහන් කරන්න - current_password: වත්මන් මුර පදය + autofollow: ඔබගේ ගිණුම අනුගමනයයට ආරාධනා කරන්න + avatar: පැතිකඩ ඡායාරූපය + bot: මෙම ගිණුම ස්වයංක්රියයි + chosen_languages: භාෂා පෙරන්න + confirm_new_password: නව මුරපදය තහවුරු කරන්න + confirm_password: මුරපදය තහවුරු කරන්න + context: සන්දර්භ පෙරන්න + current_password: වත්මන් මුරපදය data: දත්ත display_name: ප්රදර්ශන නාමය email: වි-තැපැල් ලිපිනය @@ -137,39 +136,35 @@ si: honeypot: "%{label} (පුරවන්න එපා)" inbox_url: රිලේ එන ලිපි URL irreversible: සැඟවීම වෙනුවට අතහරින්න - locale: අතුරු මුහුණත භාෂාව + locale: අතුරු මුහුණතේ භාෂාව max_uses: උපරිම භාවිත ගණන new_password: නව මුරපදය - note: ජෛව otp_attempt: ද්වි සාධක කේතය password: මුරපදය phrase: මූල පදය හෝ වාක්ය ඛණ්ඩය - setting_advanced_layout: උසස් වෙබ් අතුරු මුහුණත සබල කරන්න - setting_aggregate_reblogs: කණ්ඩායම් කාලරේඛාව වැඩි කරයි + setting_advanced_layout: සංකීර්ණ අතුරු මුහුණත සබල කරන්න setting_always_send_emails: සෑම විටම විද්යුත් තැපැල් දැනුම්දීම් යවන්න setting_auto_play_gif: සජීවිකරණ GIF ස්වයංක්රීයව ධාවනය කරන්න - setting_boost_modal: වැඩි කිරීමට පෙර තහවුරු කිරීමේ සංවාදය පෙන්වන්න - setting_default_language: පළ කිරීමේ භාෂාව - setting_default_privacy: පුද්ගලිකත්වය පළ කිරීම - setting_default_sensitive: සෑම විටම මාධ්ය සංවේදී ලෙස සලකුණු කරන්න - setting_delete_modal: පළ කිරීමක් මැකීමට පෙර තහවුරු කිරීමේ සංවාදය පෙන්වන්න - setting_disable_swiping: ස්වයිප් චලන අක්රීය කරන්න + setting_default_language: ලිපිවල භාෂාව + setting_default_privacy: ලිපියේ රහස්යතාව + setting_default_sensitive: සෑමවිට මාධ්ය සංවේදී බව සලකුණු කරන්න + setting_delete_modal: ලිපියක් මැකීමට පෙර ඒ ගැන විමසන්න setting_display_media: මාධ්ය සංදර්ශකය - setting_display_media_default: පෙරනිමිය + setting_display_media_default: පෙරනිමි setting_display_media_hide_all: සියල්ල සඟවන්න setting_display_media_show_all: සියල්ල පෙන්වන්න - setting_expand_spoilers: අන්තර්ගත අනතුරු ඇඟවීම් සමඟ සලකුණු කර ඇති පළ කිරීම් සැමවිටම පුළුල් කරන්න + setting_expand_spoilers: අන්තර්ගත අවවාද සහිත ලිපි සැමවිට දිගහරින්න setting_hide_network: ඔබගේ ජාලය සඟවන්න setting_reduce_motion: සජීවිකරණවල චලනය අඩු කරන්න - setting_system_font_ui: පද්ධතියේ පෙරනිමි අකුරු භාවිතා කරන්න + setting_system_font_ui: පද්ධතියේ පෙරනිමි රුවකුරු භාවිතා කරන්න setting_theme: අඩවියේ තේමාව setting_trends: අද ප්රවණතා පෙන්වන්න setting_unfollow_modal: යමෙකු අනුගමනය නොකිරීමට පෙර තහවුරු කිරීමේ සංවාදය පෙන්වන්න setting_use_blurhash: සැඟවුණු මාධ්ය සඳහා වර්ණවත් අනුක්රමික පෙන්වන්න - setting_use_pending_items: මන්දගාමී මාදිලිය + setting_use_pending_items: මන්දගාමී ප්රකාරය severity: බරපතලකම sign_in_token_attempt: ආරක්ෂණ කේතය - title: ශීර්ෂය + title: සිරැසිය type: ආයාත වර්ගය username: පරිශීලක නාමය username_or_email: පරි. නාමය හෝ වි-තැපෑල @@ -180,12 +175,16 @@ si: name: හෑෂ් ටැගය filters: actions: - hide: සම්පූර්ණයෙන්ම සඟවන්න - warn: අනතුරු ඇඟවීමක් සමඟ සඟවන්න + hide: මුළුමනින්ම සඟවන්න + warn: අවවාදයක් සහිතව සඟවන්න + form_admin_settings: + custom_css: අභිරුචි CSS + profile_directory: පැතිකඩ නාමාවලිය සබල කරන්න + site_terms: රහස්යතා ප්රතිපත්තිය + site_title: සේවාදායකයේ නම + theme: පෙරනිමි තේමාව interactions: - must_be_follower: අනුගාමිකයින් නොවන අයගේ දැනුම්දීම් අවහිර කරන්න - must_be_following: ඔබ අනුගමනය නොකරන පුද්ගලයින්ගේ දැනුම්දීම් අවහිර කරන්න - must_be_following_dm: ඔබ අනුගමනය නොකරන පුද්ගලයින්ගෙන් සෘජු පණිවිඩ අවහිර කරන්න + must_be_following_dm: ඔබ නොදන්නා අයගෙන් සෘජු පණිවිඩ ලැබීම අවහිර කරන්න invite: comment: අදහස invite_request: @@ -194,31 +193,34 @@ si: comment: අදහස ip: අ.ජා. කෙ. (IP) severities: - no_access: ප්රවේශය අවහිර කරන්න + no_access: ප්රවේශය අවහිර කරන්න sign_up_requires_approval: ලියාපදිංචි වීම සීමා කරන්න severity: නීතිය notification_emails: - appeal: යමෙක් උපපරිපාලක තීරණයකට අභියාචනා කරයි digest: digest ඊමේල් යවන්න - favourite: කවුරුහරි ඔබේ පළ කිරීම ප්රිය කළා - follow: කවුරුහරි ඔබව අනුගමනය කළා - follow_request: කවුරුහරි ඔබව අනුගමනය කරන ලෙස ඉල්ලා සිටියේය - mention: කවුරුහරි ඔබව සඳහන් කළා - pending_account: නව ගිණුම සමාලෝචනය අවශ්යයි - reblog: කවුරුහරි ඔබේ පළ කිරීම වැඩි කළා - report: නව වාර්තාවක් ඉදිරිපත් කෙරේ - trending_tag: නව ප්රවණතාවයට සමාලෝචනයක් අවශ්ය වේ + favourite: යමෙක් ඔබගේ ලිපියට ප්රිය කළා + follow: යමෙක් ඔබව අනුගමනය කළා + mention: යමෙක් ඔබව සඳහන් කළා + report: නව වාර්තාවක් යොමු කර ඇත rule: text: නීතිය tag: listable: මෙම හැෂ් ටැගය සෙවීම් සහ යෝජනා වල දිස් වීමට ඉඩ දෙන්න name: හෑෂ් ටැගය trendable: මෙම හැෂ් ටැගය ප්රවණතා යටතේ දිස් වීමට ඉඩ දෙන්න - usable: මෙම හැෂ් ටැගය භාවිතා කිරීමට පළ කිරීම් වලට ඉඩ දෙන්න + usable: ලිපි සඳහා මෙම පූරක අනන්යනය භාවිතයට ඉඩදෙන්න + user: + role: භූමිකාව + time_zone: වේලා කලාපය + user_role: + color: චිහ්නයේ පාට + name: නම + permissions_as_keys: අවසර + position: ප්රමුඛත්වය webhook: events: සබල කළ සිදුවීම් url: අන්ත ලක්ෂ්ය URL - 'no': 'නැත' + 'no': නැහැ recommended: නිර්දේශිත required: mark: "*" diff --git a/config/navigation.rb b/config/navigation.rb index e9552f4f7a..25bc5ecd43 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -66,6 +66,7 @@ SimpleNavigation::Configuration.run do |navigation| s.item :custom_emojis, safe_join([fa_icon('smile-o fw'), t('admin.custom_emojis.title')]), admin_custom_emojis_path, highlights_on: %r{/admin/custom_emojis}, if: -> { current_user.can?(:manage_custom_emojis) } s.item :webhooks, safe_join([fa_icon('inbox fw'), t('admin.webhooks.title')]), admin_webhooks_path, highlights_on: %r{/admin/webhooks}, if: -> { current_user.can?(:manage_webhooks) } s.item :relays, safe_join([fa_icon('exchange fw'), t('admin.relays.title')]), admin_relays_path, highlights_on: %r{/admin/relays}, if: -> { !limited_federation_mode? && current_user.can?(:manage_federation) } + s.item :friend_servers, safe_join([fa_icon('users fw'), t('admin.friend_servers.title')]), admin_friend_servers_path, highlights_on: %r{/admin/friend_servers}, if: -> { current_user.can?(:manage_federation) } end n.item :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_path, link_html: { target: 'sidekiq' }, if: -> { current_user.can?(:view_devops) } diff --git a/config/routes.rb b/config/routes.rb index ed5995b2f4..1910e3427a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -91,10 +91,10 @@ Rails.application.routes.draw do devise_for :users, path: 'auth', format: false, controllers: { omniauth_callbacks: 'auth/omniauth_callbacks', - sessions: 'auth/sessions', - registrations: 'auth/registrations', - passwords: 'auth/passwords', - confirmations: 'auth/confirmations', + sessions: 'auth/sessions', + registrations: 'auth/registrations', + passwords: 'auth/passwords', + confirmations: 'auth/confirmations', } get '/users/:username', to: redirect('/@%{username}'), constraints: lambda { |req| req.format.nil? || req.format.html? } diff --git a/config/routes/admin.rb b/config/routes/admin.rb index c3ae2efa93..8c10f5935b 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -69,6 +69,15 @@ namespace :admin do end end + resources :friend_servers, only: [:index, :new, :edit, :create, :update, :destroy] do + member do + post :follow + post :unfollow + post :accept + post :reject + end + end + resources :instances, only: [:index, :show, :destroy], constraints: { id: %r{[^/]+} }, format: 'html' do member do post :clear_delivery_errors diff --git a/config/settings.yml b/config/settings.yml index c0b5f4109b..1fb106e680 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -43,6 +43,7 @@ defaults: &defaults enable_emoji_reaction: true check_lts_version_only: true enable_public_unlisted_visibility: true + unlocked_friend: false development: <<: *defaults diff --git a/db/migrate/20230930233930_add_quote_to_status_references.rb b/db/migrate/20230930233930_add_quote_to_status_references.rb new file mode 100644 index 0000000000..f2bd6cd48d --- /dev/null +++ b/db/migrate/20230930233930_add_quote_to_status_references.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require Rails.root.join('lib', 'mastodon', 'migration_helpers') + +class AddQuoteToStatusReferences < ActiveRecord::Migration[7.0] + include Mastodon::MigrationHelpers + + disable_ddl_transaction! + + class StatusReference < ApplicationRecord; end + + def up + safety_assured do + add_column_with_default :status_references, :quote, :boolean, default: false, allow_null: false + StatusReference.where(attribute_type: 'QT').update_all(quote: true) # rubocop:disable Rails/SkipsModelValidations + end + end + + def down + safety_assured do + remove_column :status_references, :quote + end + end +end diff --git a/db/migrate/20231001031337_add_quote_to_statuses.rb b/db/migrate/20231001031337_add_quote_to_statuses.rb new file mode 100644 index 0000000000..c60ec58ecf --- /dev/null +++ b/db/migrate/20231001031337_add_quote_to_statuses.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require Rails.root.join('lib', 'mastodon', 'migration_helpers') + +class AddQuoteToStatuses < ActiveRecord::Migration[7.0] + include Mastodon::MigrationHelpers + + disable_ddl_transaction! + + class StatusReference < ApplicationRecord + belongs_to :status + belongs_to :target_status, class_name: 'Status' + end + + def up + safety_assured do + add_column_with_default :statuses, :quote_of_id, :bigint, default: nil, allow_null: true + + StatusReference.transaction do + StatusReference.where(quote: true).includes(:status).each do |ref| + ref.status.update(quote_of_id: ref.target_status_id) + end + end + end + end + + def down + safety_assured do + remove_column :statuses, :quote_of_id + end + end +end diff --git a/db/migrate/20231001050733_add_with_quote_to_custom_filters.rb b/db/migrate/20231001050733_add_with_quote_to_custom_filters.rb new file mode 100644 index 0000000000..074f552482 --- /dev/null +++ b/db/migrate/20231001050733_add_with_quote_to_custom_filters.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require Rails.root.join('lib', 'mastodon', 'migration_helpers') + +class AddWithQuoteToCustomFilters < ActiveRecord::Migration[7.0] + include Mastodon::MigrationHelpers + + disable_ddl_transaction! + + def change + safety_assured do + add_column_with_default :custom_filters, :with_quote, :boolean, default: true, allow_null: false + end + end +end diff --git a/db/migrate/20231005074832_create_friend_domains.rb b/db/migrate/20231005074832_create_friend_domains.rb new file mode 100644 index 0000000000..95eaf8a74c --- /dev/null +++ b/db/migrate/20231005074832_create_friend_domains.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require Rails.root.join('lib', 'mastodon', 'migration_helpers') + +class CreateFriendDomains < ActiveRecord::Migration[7.0] + include Mastodon::MigrationHelpers + + disable_ddl_transaction! + + def change + create_table :friend_domains do |t| + t.string :domain, null: false, default: '', index: { unique: true } + t.string :inbox_url, null: false, default: '', index: { unique: true } + t.integer :active_state, null: false, default: 0 + t.integer :passive_state, null: false, default: 0 + t.string :active_follow_activity_id, null: true + t.string :passive_follow_activity_id, null: true + t.boolean :available, null: false, default: true + t.boolean :pseudo_relay, null: false, default: false + t.boolean :unlocked, null: false, default: false + t.boolean :allow_all_posts, null: false, default: true + t.datetime :created_at, null: false + t.datetime :updated_at, null: false + end + end +end diff --git a/db/migrate/20231006030102_add_reject_friend_to_domain_blocks.rb b/db/migrate/20231006030102_add_reject_friend_to_domain_blocks.rb new file mode 100644 index 0000000000..01204db5f1 --- /dev/null +++ b/db/migrate/20231006030102_add_reject_friend_to_domain_blocks.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require Rails.root.join('lib', 'mastodon', 'migration_helpers') + +class AddRejectFriendToDomainBlocks < ActiveRecord::Migration[7.0] + include Mastodon::MigrationHelpers + + disable_ddl_transaction! + + def change + safety_assured do + add_column_with_default :domain_blocks, :reject_friend, :boolean, default: false, allow_null: false + end + end +end diff --git a/db/migrate/20231007090808_improve_search_for_account_statuses.rb b/db/migrate/20231007090808_improve_search_for_account_statuses.rb new file mode 100644 index 0000000000..e1817ed95c --- /dev/null +++ b/db/migrate/20231007090808_improve_search_for_account_statuses.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require Rails.root.join('lib', 'mastodon', 'migration_helpers') + +class ImproveSearchForAccountStatuses < ActiveRecord::Migration[7.0] + include Mastodon::MigrationHelpers + + disable_ddl_transaction! + + def change + safety_assured do + add_index :statuses, [:account_id, :reblog_of_id, :deleted_at, :searchability], name: 'index_statuses_for_get_following_accounts_to_search', where: 'deleted_at IS NULL AND reblog_of_id IS NULL AND searchability IN (0, 10, 1)' + end + end +end diff --git a/db/migrate/20231009235215_add_delivery_local_to_friend_domains.rb b/db/migrate/20231009235215_add_delivery_local_to_friend_domains.rb new file mode 100644 index 0000000000..a42b016663 --- /dev/null +++ b/db/migrate/20231009235215_add_delivery_local_to_friend_domains.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require Rails.root.join('lib', 'mastodon', 'migration_helpers') + +class AddDeliveryLocalToFriendDomains < ActiveRecord::Migration[7.0] + include Mastodon::MigrationHelpers + + disable_ddl_transaction! + + def up + safety_assured do + add_column_with_default :friend_domains, :delivery_local, :boolean, default: true, allow_null: false + remove_column :friend_domains, :unlocked + end + end + + def down + safety_assured do + remove_column :friend_domains, :delivery_local + add_column_with_default :friend_domains, :unlocked, :boolean, default: false, allow_null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb index e17320cdaa..7a4ee11608 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_09_23_103430) do +ActiveRecord::Schema[7.0].define(version: 2023_10_09_235215) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -536,6 +536,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_09_23_103430) do t.integer "action", default: 0, null: false t.boolean "exclude_follows", default: false, null: false t.boolean "exclude_localusers", default: false, null: false + t.boolean "with_quote", default: true, null: false t.index ["account_id"], name: "index_custom_filters_on_account_id" end @@ -583,6 +584,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_09_23_103430) do t.boolean "hidden_anonymous", default: false, null: false t.boolean "detect_invalid_subscription", default: false, null: false t.boolean "reject_reply_exclude_followers", default: false, null: false + t.boolean "reject_friend", default: false, null: false t.index ["domain"], name: "index_domain_blocks_on_domain", unique: true end @@ -675,6 +677,23 @@ ActiveRecord::Schema[7.0].define(version: 2023_09_23_103430) do t.index ["target_account_id"], name: "index_follows_on_target_account_id" end + create_table "friend_domains", force: :cascade do |t| + t.string "domain", default: "", null: false + t.string "inbox_url", default: "", null: false + t.integer "active_state", default: 0, null: false + t.integer "passive_state", default: 0, null: false + t.string "active_follow_activity_id" + t.string "passive_follow_activity_id" + t.boolean "available", default: true, null: false + t.boolean "pseudo_relay", default: false, null: false + t.boolean "allow_all_posts", default: true, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.boolean "delivery_local", default: true, null: false + t.index ["domain"], name: "index_friend_domains_on_domain", unique: true + t.index ["inbox_url"], name: "index_friend_domains_on_inbox_url", unique: true + end + create_table "identities", force: :cascade do |t| t.string "provider", default: "", null: false t.string "uid", default: "", null: false @@ -1143,6 +1162,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_09_23_103430) do t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false t.string "attribute_type" + t.boolean "quote", default: false, null: false t.index ["status_id"], name: "index_status_references_on_status_id" t.index ["target_status_id"], name: "index_status_references_on_target_status_id" end @@ -1199,7 +1219,9 @@ ActiveRecord::Schema[7.0].define(version: 2023_09_23_103430) do t.integer "searchability" t.boolean "markdown", default: false t.integer "limited_scope" + t.bigint "quote_of_id" t.index ["account_id", "id", "visibility", "updated_at"], name: "index_statuses_20190820", order: { id: :desc }, where: "(deleted_at IS NULL)" + t.index ["account_id", "reblog_of_id", "deleted_at", "searchability"], name: "index_statuses_for_get_following_accounts_to_search", where: "((deleted_at IS NULL) AND (reblog_of_id IS NULL) AND (searchability = ANY (ARRAY[0, 10, 1])))" t.index ["account_id"], name: "index_statuses_on_account_id" t.index ["deleted_at"], name: "index_statuses_on_deleted_at", where: "(deleted_at IS NOT NULL)" t.index ["id", "account_id"], name: "index_statuses_local_20190824", order: { id: :desc }, where: "((local OR (uri IS NULL)) AND (deleted_at IS NULL) AND (visibility = 0) AND (reblog_of_id IS NULL) AND ((NOT reply) OR (in_reply_to_account_id = account_id)))" diff --git a/lib/mastodon/cli/upgrade.rb b/lib/mastodon/cli/upgrade.rb index 52b5540c40..cf83986844 100644 --- a/lib/mastodon/cli/upgrade.rb +++ b/lib/mastodon/cli/upgrade.rb @@ -125,27 +125,12 @@ module Mastodon::CLI progress.log("Moving #{previous_path} to #{upgraded_path}") if options[:verbose] begin - unless dry_run? - FileUtils.mkdir_p(File.dirname(upgraded_path)) - FileUtils.mv(previous_path, upgraded_path) - - begin - FileUtils.rmdir(File.dirname(previous_path), parents: true) - rescue Errno::ENOTEMPTY - # OK - end - end + move_previous_to_upgraded rescue => e progress.log(pastel.red("Error processing #{previous_path}: #{e}")) success = false - unless dry_run? - begin - FileUtils.rmdir(File.dirname(upgraded_path), parents: true) - rescue Errno::ENOTEMPTY - # OK - end - end + remove_directory end end @@ -155,5 +140,28 @@ module Mastodon::CLI attachment.instance_write(:storage_schema_version, previous_storage_schema_version) success end + + def move_previous_to_upgraded(previous_path, upgraded_path) + return if dry_run? + + FileUtils.mkdir_p(File.dirname(upgraded_path)) + FileUtils.mv(previous_path, upgraded_path) + + begin + FileUtils.rmdir(File.dirname(previous_path), parents: true) + rescue Errno::ENOTEMPTY + # OK + end + end + + def remove_directory(path) + return if dry_run? + + begin + FileUtils.rmdir(File.dirname(path), parents: true) + rescue Errno::ENOTEMPTY + # OK + end + end end end diff --git a/lib/mastodon/migration_helpers.rb b/lib/mastodon/migration_helpers.rb index c382b5fbd5..a92a8767ce 100644 --- a/lib/mastodon/migration_helpers.rb +++ b/lib/mastodon/migration_helpers.rb @@ -37,7 +37,6 @@ # This is bad form, but there are enough differences that it's impractical to do # otherwise: -# rubocop:disable all module Mastodon module MigrationHelpers @@ -989,5 +988,3 @@ into similar problems in the future (e.g. when new tables are created). end end end - -# rubocop:enable all diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 183a9ae556..e1f1ed87dc 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -5,11 +5,11 @@ module Mastodon module_function def kmyblue_major - 6 + 7 end def kmyblue_minor - 1 + 0 end def kmyblue_flag @@ -25,7 +25,7 @@ module Mastodon end def patch - 0 + 1 end def default_prerelease diff --git a/lib/tasks/tests.rake b/lib/tasks/tests.rake index 7f8e72dd8f..980823731e 100644 --- a/lib/tasks/tests.rake +++ b/lib/tasks/tests.rake @@ -136,7 +136,7 @@ namespace :tests do INSERT INTO "settings" (id, thing_type, thing_id, var, value, created_at, updated_at) VALUES - (3, 'User', 1, 'notification_emails', E'--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nfollow: false\nreblog: true\nfavourite: true\nmention: false\nfollow_request: true\ndigest: true\nreport: true\npending_account: false\ntrending_tag: true\nappeal: true\n', now(), now()), + (3, 'User', 1, 'notification_emails', E'--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nfollow: false\nreblog: true\nfavourite: true\nmention: false\nfollow_request: true\ndigest: true\nreport: true\npending_account: false\npending_friend_server: true\ntrending_tag: true\nappeal: true\n', now(), now()), (4, 'User', 1, 'trends', E'--- false\n', now(), now()); INSERT INTO "accounts" diff --git a/package.json b/package.json index 5de05c53b6..902dd8e878 100644 --- a/package.json +++ b/package.json @@ -173,7 +173,7 @@ "@types/react-dom": "^18.2.4", "@types/react-helmet": "^6.1.6", "@types/react-immutable-proptypes": "^2.1.0", - "@types/react-motion": "^0.0.34", + "@types/react-motion": "^0.0.35", "@types/react-overlays": "^3.1.0", "@types/react-router-dom": "^5.3.3", "@types/react-select": "^5.0.1", diff --git a/spec/fabricators/emoji_reaction_fabricator.rb b/spec/fabricators/emoji_reaction_fabricator.rb new file mode 100644 index 0000000000..2605a9f232 --- /dev/null +++ b/spec/fabricators/emoji_reaction_fabricator.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +Fabricator(:emoji_reaction) do + account { Fabricate.build(:account) } + status { Fabricate.build(:status) } + name '😀' +end diff --git a/spec/fabricators/friend_domain_fabricator.rb b/spec/fabricators/friend_domain_fabricator.rb new file mode 100644 index 0000000000..840f79ea3e --- /dev/null +++ b/spec/fabricators/friend_domain_fabricator.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +Fabricator(:friend_domain) do + domain 'example.com' + inbox_url 'https://example.com/inbox' + active_state :idle + passive_state :idle + available true + before_create { |friend_domain, _| friend_domain.inbox_url = "https://#{friend_domain.domain}/inbox" if friend_domain.inbox_url.blank? } +end diff --git a/spec/fabricators/status_reference_fabricator.rb b/spec/fabricators/status_reference_fabricator.rb new file mode 100644 index 0000000000..0eff89c14b --- /dev/null +++ b/spec/fabricators/status_reference_fabricator.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +Fabricator(:status_reference) do + status { Fabricate.build(:status) } + target_status { Fabricate.build(:status) } + attribute_type 'BT' + quote false +end diff --git a/spec/helpers/languages_helper_spec.rb b/spec/helpers/languages_helper_spec.rb index 98c8064a33..99461b293b 100644 --- a/spec/helpers/languages_helper_spec.rb +++ b/spec/helpers/languages_helper_spec.rb @@ -60,4 +60,30 @@ describe LanguagesHelper do end end end + + describe 'sorted_locales' do + context 'when sorting with native name' do + it 'returns Suomi after Gàidhlig' do + expect(described_class.sorted_locale_keys(%w(fi gd))).to eq(%w(gd fi)) + end + end + + context 'when sorting with diacritics' do + it 'returns Íslensk before Suomi' do + expect(described_class.sorted_locale_keys(%w(fi is))).to eq(%w(is fi)) + end + end + + context 'when sorting with non-Latin' do + it 'returns Suomi before Amharic' do + expect(described_class.sorted_locale_keys(%w(am fi))).to eq(%w(fi am)) + end + end + + context 'when sorting with local variants' do + it 'returns variant in-line' do + expect(described_class.sorted_locale_keys(%w(en eo en-GB))).to eq(%w(en en-GB eo)) + end + end + end end diff --git a/spec/lib/activitypub/activity/accept_spec.rb b/spec/lib/activitypub/activity/accept_spec.rb index d6b6071279..24dbcbff7c 100644 --- a/spec/lib/activitypub/activity/accept_spec.rb +++ b/spec/lib/activitypub/activity/accept_spec.rb @@ -43,6 +43,35 @@ RSpec.describe ActivityPub::Activity::Accept do end end + context 'when sender is from friend server' do + subject { described_class.new(json, sender) } + + let(:sender) { Fabricate(:account, domain: 'abc.com', url: 'https://abc.com/#actor') } + let!(:friend) { Fabricate(:friend_domain, domain: 'abc.com', active_state: :pending, active_follow_activity_id: 'https://abc-123/456') } + + before do + allow(RemoteAccountRefreshWorker).to receive(:perform_async) + Fabricate(:follow_request, account: recipient, target_account: sender) + subject.perform + end + + it 'creates a follow relationship' do + expect(recipient.following?(sender)).to be true + end + + it 'removes the follow request' do + expect(recipient.requested?(sender)).to be false + end + + it 'queues a refresh' do + expect(RemoteAccountRefreshWorker).to have_received(:perform_async).with(sender.id) + end + + it 'friend server is not changed' do + expect(friend.reload.i_am_pending?).to be true + end + end + context 'when given a relay' do subject { described_class.new(json, sender) } @@ -68,4 +97,47 @@ RSpec.describe ActivityPub::Activity::Accept do expect(relay.reload.accepted?).to be true end end + + context 'when given a friend server' do + subject { described_class.new(json, sender) } + + let(:sender) { Fabricate(:account, domain: 'abc.com', url: 'https://abc.com/#actor') } + let!(:friend) { Fabricate(:friend_domain, domain: 'abc.com', active_state: :pending, active_follow_activity_id: 'https://abc-123/456') } + + let(:json) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'foo', + type: 'Accept', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: 'https://abc-123/456', + }.with_indifferent_access + end + + it 'marks the friend as accepted' do + subject.perform + expect(friend.reload.i_am_accepted?).to be true + end + + it 'when the friend server is pending' do + friend.update(passive_state: :pending) + subject.perform + expect(friend.reload.they_are_idle?).to be true + expect(friend.i_am_accepted?).to be true + end + + it 'when the friend server is accepted' do + friend.update(passive_state: :accepted) + subject.perform + expect(friend.reload.they_are_idle?).to be true + expect(friend.i_am_accepted?).to be true + end + + it 'when my server is not pending' do + friend.update(active_state: :idle) + subject.perform + expect(friend.reload.i_am_idle?).to be true + expect(friend.they_are_idle?).to be true + end + end end diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb index c199fcd038..7659ed82a0 100644 --- a/spec/lib/activitypub/activity/create_spec.rb +++ b/spec/lib/activitypub/activity/create_spec.rb @@ -30,9 +30,11 @@ RSpec.describe ActivityPub::Activity::Create do let(:sender_software) { 'mastodon' } let(:custom_before) { false } + let(:active_friend) { false } before do Fabricate(:instance_info, domain: 'example.com', software: sender_software) + Fabricate(:friend_domain, domain: 'example.com', active_state: :accepted) if active_friend subject.perform unless custom_before end @@ -234,6 +236,45 @@ RSpec.describe ActivityPub::Activity::Create do end end + context 'when public_unlisted with kmyblue:LocalPublic' do + let(:object_json) do + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Note', + content: 'Lorem ipsum', + to: ['http://example.com/followers', 'kmyblue:LocalPublic'], + cc: 'https://www.w3.org/ns/activitystreams#Public', + } + end + + it 'creates status' do + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.visibility).to eq 'unlisted' + end + end + + context 'when public_unlisted with kmyblue:LocalPublic from friend-server' do + let(:object_json) do + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Note', + content: 'Lorem ipsum', + to: ['http://example.com/followers', 'kmyblue:LocalPublic'], + cc: 'https://www.w3.org/ns/activitystreams#Public', + } + end + let(:active_friend) { true } + + it 'creates status' do + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.visibility).to eq 'public_unlisted' + end + end + context 'when private' do let(:object_json) do { @@ -411,6 +452,29 @@ RSpec.describe ActivityPub::Activity::Create do end end + context 'with public_unlisted with kmyblue:LocalPublic' do + let(:searchable_by) { ['http://example.com/followers', 'kmyblue:LocalPublic'] } + + it 'create status' do + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.searchability).to eq 'private' + end + end + + context 'with public_unlisted with kmyblue:LocalPublic from friend-server' do + let(:searchable_by) { ['http://example.com/followers', 'kmyblue:LocalPublic'] } + let(:active_friend) { true } + + it 'create status' do + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.searchability).to eq 'public_unlisted' + end + end + context 'with private' do let(:searchable_by) { 'http://example.com/followers' } @@ -445,6 +509,17 @@ RSpec.describe ActivityPub::Activity::Create do end context 'with limited' do + let(:searchable_by) { 'kmyblue:Limited' } + + it 'create status' do + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.searchability).to eq 'limited' + end + end + + context 'with limited old spec' do let(:searchable_by) { 'as:Limited' } it 'create status' do @@ -570,7 +645,7 @@ RSpec.describe ActivityPub::Activity::Create do context 'with misskey' do let(:sender_software) { 'misskey' } - let(:searchable_by) { 'as:Limited' } + let(:searchable_by) { 'kmyblue:Limited' } it 'create status' do status = sender.statuses.first @@ -1089,6 +1164,97 @@ RSpec.describe ActivityPub::Activity::Create do end end + context 'with references' do + let(:recipient) { Fabricate(:account) } + let!(:target_status) { Fabricate(:status, account: Fabricate(:account, domain: nil)) } + + let(:object_json) do + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Note', + content: 'Lorem ipsum', + references: { + id: 'target_status', + type: 'Collection', + first: { + type: 'CollectionPage', + next: nil, + partOf: 'target_status', + items: [ + ActivityPub::TagManager.instance.uri_for(target_status), + ], + }, + }, + } + end + + it 'creates status' do + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.quote).to be_nil + expect(status.references.pluck(:id)).to eq [target_status.id] + end + end + + context 'with quote' do + let(:recipient) { Fabricate(:account) } + let!(:target_status) { Fabricate(:status, account: Fabricate(:account, domain: nil)) } + + let(:object_json) do + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Note', + content: 'Lorem ipsum', + quote: ActivityPub::TagManager.instance.uri_for(target_status), + } + end + + it 'creates status' do + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.references.pluck(:id)).to eq [target_status.id] + expect(status.quote).to_not be_nil + expect(status.quote.id).to eq target_status.id + end + end + + context 'with references and quote' do + let(:recipient) { Fabricate(:account) } + let!(:target_status) { Fabricate(:status, account: Fabricate(:account, domain: nil)) } + + let(:object_json) do + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Note', + content: 'Lorem ipsum', + quote: ActivityPub::TagManager.instance.uri_for(target_status), + references: { + id: 'target_status', + type: 'Collection', + first: { + type: 'CollectionPage', + next: nil, + partOf: 'target_status', + items: [ + ActivityPub::TagManager.instance.uri_for(target_status), + ], + }, + }, + } + end + + it 'creates status' do + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.references.pluck(:id)).to eq [target_status.id] + expect(status.quote).to_not be_nil + expect(status.quote.id).to eq target_status.id + end + end + context 'with language' do let(:to) { 'https://www.w3.org/ns/activitystreams#Public' } let(:object_json) do @@ -1274,6 +1440,53 @@ RSpec.describe ActivityPub::Activity::Create do end end + context 'when sender quotes to local status' do + subject { described_class.new(json, sender, delivery: true) } + + let!(:local_status) { Fabricate(:status) } + let(:object_json) do + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Note', + content: 'Lorem ipsum', + quote: ActivityPub::TagManager.instance.uri_for(local_status), + } + end + + before do + subject.perform + end + + it 'creates status' do + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.text).to eq 'Lorem ipsum' + end + end + + context 'when sender quotes to non-local status' do + subject { described_class.new(json, sender, delivery: true) } + + let!(:remote_status) { Fabricate(:status, uri: 'https://foo.bar/among', account: Fabricate(:account, domain: 'foo.bar', uri: 'https://foo.bar/account')) } + let(:object_json) do + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Note', + content: 'Lorem ipsum', + quote: ActivityPub::TagManager.instance.uri_for(remote_status), + } + end + + before do + subject.perform + end + + it 'creates status' do + expect(sender.statuses.count).to eq 0 + end + end + context 'when sender targets a local user' do subject { described_class.new(json, sender, delivery: true) } @@ -1324,6 +1537,35 @@ RSpec.describe ActivityPub::Activity::Create do end end + context 'when sender is in friend server' do + subject { described_class.new(json, sender, delivery: true) } + + let!(:friend) { Fabricate(:friend_domain, domain: sender.domain, active_state: :accepted) } + let(:object_json) do + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Note', + content: 'Lorem ipsum', + } + end + + it 'creates status' do + subject.perform + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.text).to eq 'Lorem ipsum' + end + + it 'whey no-relay not creates status' do + friend.update(allow_all_posts: false) + subject.perform + status = sender.statuses.first + + expect(status).to be_nil + end + end + context 'when the sender has no relevance to local activity' do subject { described_class.new(json, sender, delivery: true) } diff --git a/spec/lib/activitypub/activity/delete_spec.rb b/spec/lib/activitypub/activity/delete_spec.rb index 3a73b3726c..f0c957c8a1 100644 --- a/spec/lib/activitypub/activity/delete_spec.rb +++ b/spec/lib/activitypub/activity/delete_spec.rb @@ -73,4 +73,30 @@ RSpec.describe ActivityPub::Activity::Delete do end end end + + context 'when given a friend server' do + subject { described_class.new(json, sender) } + + before do + Fabricate(:friend_domain, domain: 'abc.com', inbox_url: 'https://abc.com/inbox', passive_state: :accepted) + stub_request(:post, 'https://abc.com/inbox') + end + + let(:sender) { Fabricate(:account, domain: 'abc.com', url: 'https://abc.com/#actor') } + + let(:json) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'foo', + type: 'Delete', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: 'https://www.w3.org/ns/activitystreams#Public', + }.with_indifferent_access + end + + it 'marks the friend as deleted' do + subject.perform + expect(FriendDomain.find_by(domain: 'abc.com')).to be_nil + end + end end diff --git a/spec/lib/activitypub/activity/follow_spec.rb b/spec/lib/activitypub/activity/follow_spec.rb index 890ebe2750..c27696ebaf 100644 --- a/spec/lib/activitypub/activity/follow_spec.rb +++ b/spec/lib/activitypub/activity/follow_spec.rb @@ -4,7 +4,8 @@ require 'rails_helper' RSpec.describe ActivityPub::Activity::Follow do let(:actor_type) { 'Person' } - let(:sender) { Fabricate(:account, domain: 'example.com', inbox_url: 'https://example.com/inbox', actor_type: actor_type) } + let(:display_name) { '' } + let(:sender) { Fabricate(:account, domain: 'example.com', inbox_url: 'https://example.com/inbox', actor_type: actor_type, display_name: display_name) } let(:recipient) { Fabricate(:account) } let(:json) do @@ -36,6 +37,23 @@ RSpec.describe ActivityPub::Activity::Follow do end end + context 'with an unlocked account from friend server' do + let!(:friend) { Fabricate(:friend_domain, domain: sender.domain, passive_state: :idle) } + + before do + subject.perform + end + + it 'creates a follow from sender to recipient' do + expect(sender.following?(recipient)).to be true + expect(sender.active_relationships.find_by(target_account: recipient).uri).to eq 'foo' + end + + it 'does not change friend server passive status' do + expect(friend.they_are_idle?).to be true + end + end + context 'when silenced account following an unlocked account' do before do sender.touch(:silenced_at) @@ -103,6 +121,54 @@ RSpec.describe ActivityPub::Activity::Follow do end end + context 'when unlocked misskey proxy account but locked from bot' do + let(:display_name) { 'i am proxy.' } + + before do + Fabricate(:instance_info, domain: 'example.com', software: 'misskey') + recipient.user.settings['lock_follow_from_bot'] = true + recipient.user.save! + subject.perform + end + + it 'does not create a follow from sender to recipient' do + expect(sender.following?(recipient)).to be false + end + + it 'creates a follow request' do + expect(sender.requested?(recipient)).to be true + expect(sender.follow_requests.find_by(target_account: recipient).uri).to eq 'foo' + end + end + + context 'when unlocked mastodon proxy account but locked from bot' do + let(:display_name) { 'i am proxy.' } + + before do + Fabricate(:instance_info, domain: 'example.com', software: 'mastodon') + recipient.user.settings['lock_follow_from_bot'] = true + recipient.user.save! + subject.perform + end + + it 'does not create a follow from sender to recipient' do + expect(sender.following?(recipient)).to be true + end + end + + context 'when unlocked misskey normal account but locked from bot' do + before do + Fabricate(:instance_info, domain: 'example.com', software: 'misskey') + recipient.user.settings['lock_follow_from_bot'] = true + recipient.user.save! + subject.perform + end + + it 'does not create a follow from sender to recipient' do + expect(sender.following?(recipient)).to be true + end + end + context 'when domain block reject_straight_follow' do before do Fabricate(:domain_block, domain: 'example.com', reject_straight_follow: true) @@ -236,4 +302,159 @@ RSpec.describe ActivityPub::Activity::Follow do end end end + + context 'when given a friend server' do + subject { described_class.new(json, sender) } + + let(:sender) { Fabricate(:account, domain: 'abc.com', url: 'https://abc.com/#actor') } + let!(:friend) { Fabricate(:friend_domain, domain: 'abc.com', passive_state: :idle) } + let!(:owner_user) { Fabricate(:user, role: UserRole.find_by(name: 'Owner')) } + let!(:patch_user) { Fabricate(:user, role: Fabricate(:user_role, name: 'OhagiOps', permissions: UserRole::FLAGS[:manage_federation])) } + + let(:json) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'foo', + type: 'Follow', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: 'https://www.w3.org/ns/activitystreams#Public', + }.with_indifferent_access + end + + it 'marks the friend as pending' do + subject.perform + expect(friend.reload.they_are_pending?).to be true + expect(friend.passive_follow_activity_id).to eq 'foo' + end + + context 'when no record' do + before do + friend.update(domain: 'def.com') + end + + it 'marks the friend as pending' do + subject.perform + + friend = FriendDomain.find_by(domain: 'abc.com') + expect(friend).to_not be_nil + expect(friend.they_are_pending?).to be true + expect(friend.passive_follow_activity_id).to eq 'foo' + expect(friend.inbox_url).to eq 'https://abc.com/inbox' + end + end + + context 'when my server is pending' do + before do + friend.update(active_state: :pending) + end + + it 'marks me as idle' do + subject.perform + expect(friend.reload.they_are_pending?).to be true + expect(friend.i_am_idle?).to be true + end + end + + context 'when my server is already accepted' do + before do + friend.update(active_state: :accepted) + stub_request(:post, 'https://example.com/inbox') + end + + it 'marks me as idle and the friend as accepted' do + subject.perform + expect(friend.reload.they_are_accepted?).to be true + expect(friend.i_am_idle?).to be true + expect(a_request(:post, 'https://example.com/inbox').with(body: hash_including({ + id: 'foo#accepts/friends', + type: 'Accept', + object: 'foo', + }))).to have_been_made.once + end + end + + context 'with sending email' do + around do |example| + queue_adapter = ActiveJob::Base.queue_adapter + ActiveJob::Base.queue_adapter = :test + + example.run + + ActiveJob::Base.queue_adapter = queue_adapter + end + + it 'perform' do + expect { subject.perform }.to have_enqueued_mail(AdminMailer, :new_pending_friend_server) + .with(hash_including(params: { recipient: owner_user.account })).once + .and(have_enqueued_mail(AdminMailer, :new_pending_friend_server).with(hash_including(params: { recipient: patch_user.account })).once) + .and(have_enqueued_mail.at_most(2)) + end + end + + context 'when after rejected' do + before do + friend.update(passive_state: :rejected) + end + + it 'marks the friend as pending' do + subject.perform + expect(friend.reload.they_are_pending?).to be true + expect(friend.passive_follow_activity_id).to eq 'foo' + end + end + + context 'when unlocked on admin settings' do + before do + Form::AdminSettings.new(unlocked_friend: '1').save + stub_request(:post, 'https://example.com/inbox') + end + + it 'marks the friend as accepted' do + subject.perform + + friend = FriendDomain.find_by(domain: 'abc.com') + expect(friend).to_not be_nil + expect(friend.they_are_accepted?).to be true + expect(a_request(:post, 'https://example.com/inbox').with(body: hash_including({ + id: 'foo#accepts/friends', + type: 'Accept', + object: 'foo', + }))).to have_been_made.once + end + end + + context 'when already accepted' do + before do + friend.update(passive_state: :accepted) + stub_request(:post, 'https://example.com/inbox') + end + + it 'marks the friend as accepted' do + subject.perform + + friend = FriendDomain.find_by(domain: 'abc.com') + expect(friend).to_not be_nil + expect(friend.they_are_accepted?).to be true + expect(a_request(:post, 'https://example.com/inbox').with(body: hash_including({ + id: 'foo#accepts/friends', + type: 'Accept', + object: 'foo', + }))).to have_been_made.once + end + end + + context 'when domain blocked' do + before do + friend.update(domain: 'def.com') + end + + it 'marks the friend rejected' do + Fabricate(:domain_block, domain: 'abc.com', reject_friend: true) + subject.perform + + friend = FriendDomain.find_by(domain: 'abc.com') + expect(friend).to be_nil + end + end + end end diff --git a/spec/lib/activitypub/activity/like_spec.rb b/spec/lib/activitypub/activity/like_spec.rb index 51493f7bc8..9991ba37dc 100644 --- a/spec/lib/activitypub/activity/like_spec.rb +++ b/spec/lib/activitypub/activity/like_spec.rb @@ -29,6 +29,254 @@ RSpec.describe ActivityPub::Activity::Like do end end + describe '#perform when receive emoji reaction' do + subject do + described_class.new(json, sender).perform + EmojiReaction.where(status: status) + end + + before do + stub_request(:get, 'http://example.com/emoji.png').to_return(body: attachment_fixture('emojo.png')) + end + + let(:json) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'foo', + type: 'Like', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: ActivityPub::TagManager.instance.uri_for(status), + content: content, + tag: tag, + }.with_indifferent_access + end + let(:content) { nil } + let(:tag) { nil } + + context 'with unicode emoji' do + let(:content) { '😀' } + + it 'create emoji reaction' do + expect(subject.count).to eq 1 + expect(subject.first.name).to eq '😀' + expect(subject.first.account).to eq sender + expect(sender.favourited?(status)).to be false + end + end + + context 'with custom emoji' do + let(:content) { ':tinking:' } + let(:tag) do + { + id: 'https://example.com/aaa', + type: 'Emoji', + icon: { + url: 'http://example.com/emoji.png', + }, + name: 'tinking', + license: 'Everyone but Ohagi', + } + end + + it 'create emoji reaction' do + expect(subject.count).to eq 1 + expect(subject.first.name).to eq 'tinking' + expect(subject.first.account).to eq sender + expect(subject.first.custom_emoji).to_not be_nil + expect(subject.first.custom_emoji.shortcode).to eq 'tinking' + expect(subject.first.custom_emoji.domain).to eq 'example.com' + expect(sender.favourited?(status)).to be false + end + + it 'custom emoji license is saved' do + expect(subject.first.custom_emoji.license).to eq 'Everyone but Ohagi' + end + end + + context 'with custom emoji but that is existing on local server' do + let(:content) { ':tinking:' } + let(:tag) do + { + id: 'https://example.com/aaa', + type: 'Emoji', + icon: { + url: 'http://example.com/emoji.png', + }, + name: 'tinking', + license: 'Everyone but Ohagi', + } + end + + before do + Fabricate(:custom_emoji, domain: 'example.com', uri: 'https://example.com/aaa', image_remote_url: 'http://example.com/emoji.png', shortcode: 'tinking', license: 'Everyone but Ohagi') + end + + it 'create emoji reaction' do + expect(subject.count).to eq 1 + expect(subject.first.name).to eq 'tinking' + expect(subject.first.account).to eq sender + expect(subject.first.custom_emoji).to_not be_nil + expect(subject.first.custom_emoji.shortcode).to eq 'tinking' + expect(subject.first.custom_emoji.domain).to eq 'example.com' + expect(sender.favourited?(status)).to be false + end + end + + context 'with custom emoji and custom domain' do + let(:content) { ':tinking:' } + let(:tag) do + { + id: 'https://example.com/aaa', + type: 'Emoji', + domain: 'post.kmycode.net', + icon: { + url: 'http://example.com/emoji.png', + }, + name: 'tinking', + } + end + + it 'create emoji reaction' do + expect(subject.count).to eq 1 + expect(subject.first.name).to eq 'tinking' + expect(subject.first.account).to eq sender + expect(subject.first.custom_emoji).to_not be_nil + expect(subject.first.custom_emoji.shortcode).to eq 'tinking' + expect(subject.first.custom_emoji.domain).to eq 'post.kmycode.net' + expect(sender.favourited?(status)).to be false + end + end + + context 'with custom emoji but invalid id' do + let(:content) { ':tinking:' } + let(:tag) do + { + id: 'aaa', + type: 'Emoji', + icon: { + url: 'http://example.com/emoji.png', + }, + name: 'tinking', + } + end + + it 'create emoji reaction' do + expect(subject.count).to eq 1 + expect(subject.first.name).to eq 'tinking' + expect(subject.first.account).to eq sender + expect(subject.first.custom_emoji).to_not be_nil + expect(subject.first.custom_emoji.shortcode).to eq 'tinking' + expect(subject.first.custom_emoji.domain).to eq 'example.com' + expect(sender.favourited?(status)).to be false + end + end + + context 'with custom emoji but local domain' do + let(:content) { ':tinking:' } + let(:tag) do + { + id: 'aaa', + type: 'Emoji', + domain: Rails.configuration.x.local_domain, + icon: { + url: 'http://example.com/emoji.png', + }, + name: 'tinking', + } + end + + it 'create emoji reaction' do + expect(subject.count).to eq 1 + expect(subject.first.name).to eq 'tinking' + expect(subject.first.account).to eq sender + expect(subject.first.custom_emoji).to_not be_nil + expect(subject.first.custom_emoji.shortcode).to eq 'tinking' + expect(subject.first.custom_emoji.domain).to be_nil + expect(sender.favourited?(status)).to be false + end + end + + context 'with unicode emoji and reject_media enabled' do + let(:content) { '😀' } + + before do + Fabricate(:domain_block, domain: 'example.com', severity: :noop, reject_media: true) + end + + it 'create emoji reaction' do + expect(subject.count).to eq 1 + expect(subject.first.name).to eq '😀' + expect(subject.first.account).to eq sender + expect(sender.favourited?(status)).to be false + end + end + + context 'with custom emoji and reject_media enabled' do + let(:content) { ':tinking:' } + let(:tag) do + { + id: 'https://example.com/aaa', + type: 'Emoji', + icon: { + url: 'http://example.com/emoji.png', + }, + name: 'tinking', + } + end + + before do + Fabricate(:domain_block, domain: 'example.com', severity: :noop, reject_media: true) + end + + it 'create emoji reaction' do + expect(subject.count).to eq 0 + expect(sender.favourited?(status)).to be false + end + end + + context 'when emoji reaction is disabled' do + let(:content) { '😀' } + + before do + Form::AdminSettings.new(enable_emoji_reaction: false).save + end + + it 'create emoji reaction' do + expect(subject.count).to eq 0 + expect(sender.favourited?(status)).to be true + end + end + + context 'when emoji reaction between other servers is disabled' do + let(:recipient) { Fabricate(:account, domain: 'narrow.com', uri: 'https://narrow.com/') } + let(:content) { '😀' } + + before do + Form::AdminSettings.new(receive_other_servers_emoji_reaction: false).save + end + + it 'create emoji reaction' do + expect(subject.count).to eq 0 + expect(sender.favourited?(status)).to be false + end + end + + context 'when emoji reaction between other servers is disabled but that status is local' do + let(:content) { '😀' } + + before do + Form::AdminSettings.new(receive_other_servers_emoji_reaction: false).save + end + + it 'create emoji reaction' do + expect(subject.count).to eq 1 + expect(subject.first.name).to eq '😀' + expect(subject.first.account).to eq sender + expect(sender.favourited?(status)).to be false + end + end + end + describe '#perform when domain_block' do subject { described_class.new(json, sender) } @@ -50,7 +298,7 @@ RSpec.describe ActivityPub::Activity::Like do subject.perform end - it 'does not create a favourite from sender to status', pending: 'considering spec' do + it 'does not create a favourite from sender to status' do expect(sender.favourited?(status)).to be false end end diff --git a/spec/lib/activitypub/activity/reject_spec.rb b/spec/lib/activitypub/activity/reject_spec.rb index 0a4243cd16..9ebbded42e 100644 --- a/spec/lib/activitypub/activity/reject_spec.rb +++ b/spec/lib/activitypub/activity/reject_spec.rb @@ -122,6 +122,30 @@ RSpec.describe ActivityPub::Activity::Reject do end end + context 'when sender is from friend server' do + subject { described_class.new(json, sender) } + + let(:sender) { Fabricate(:account, domain: 'abc.com', url: 'https://abc.com/#actor') } + let!(:friend) { Fabricate(:friend_domain, domain: 'abc.com', active_state: :pending, active_follow_activity_id: 'https://abc-123/456') } + + before do + Fabricate(:follow_request, account: recipient, target_account: sender) + subject.perform + end + + it 'does not create a follow relationship' do + expect(recipient.following?(sender)).to be false + end + + it 'removes the follow request' do + expect(recipient.requested?(sender)).to be false + end + + it 'friend server is not changed' do + expect(friend.reload.i_am_pending?).to be true + end + end + context 'when given a relay' do subject { described_class.new(json, sender) } @@ -147,4 +171,40 @@ RSpec.describe ActivityPub::Activity::Reject do expect(relay.reload.rejected?).to be true end end + + context 'when given a friend' do + subject { described_class.new(json, sender) } + + let(:sender) { Fabricate(:account, domain: 'abc.com', url: 'https://abc.com/#actor') } + let!(:friend) { Fabricate(:friend_domain, domain: 'abc.com', active_state: :pending, active_follow_activity_id: 'https://abc-123/456') } + + let(:json) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'foo', + type: 'Reject', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: 'https://abc-123/456', + }.with_indifferent_access + end + + it 'marks the friend as rejected' do + subject.perform + expect(friend.reload.i_am_rejected?).to be true + end + + it 'when the friend server is pending' do + friend.update(passive_state: :pending) + subject.perform + expect(friend.reload.they_are_idle?).to be true + expect(friend.i_am_rejected?).to be true + end + + it 'when the friend server is accepted' do + friend.update(passive_state: :accepted) + subject.perform + expect(friend.reload.they_are_idle?).to be true + expect(friend.i_am_rejected?).to be true + end + end end diff --git a/spec/lib/activitypub/activity/undo_spec.rb b/spec/lib/activitypub/activity/undo_spec.rb index 58e71fc4e8..feda725654 100644 --- a/spec/lib/activitypub/activity/undo_spec.rb +++ b/spec/lib/activitypub/activity/undo_spec.rb @@ -145,6 +145,13 @@ RSpec.describe ActivityPub::Activity::Undo do expect(sender.following?(recipient)).to be false end + it 'deletes follow from sender to recipient when has friend' do + friend = Fabricate(:friend_domain, domain: sender.domain, passive_state: :accepted) + subject.perform + expect(sender.following?(recipient)).to be false + expect(friend.reload.they_are_accepted?).to be true + end + context 'with only object uri' do let(:object_json) { 'bar' } @@ -153,6 +160,36 @@ RSpec.describe ActivityPub::Activity::Undo do expect(sender.following?(recipient)).to be false end end + + context 'when for a friend' do + let(:sender) { Fabricate(:account, domain: 'abc.com', url: 'https://abc.com/#actor') } + let!(:friend) { Fabricate(:friend_domain, domain: 'abc.com', passive_state: :accepted, passive_follow_activity_id: 'bar') } + let(:object_json) do + { + id: 'bar', + type: 'Follow', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: 'https://www.w3.org/ns/activitystreams#Public', + } + end + + it 'deletes follow from this server to friend' do + subject.perform + expect(FriendDomain.exists?(domain: 'abc.com')).to be false + end + + it 'when my server is pending' do + friend.update(active_state: :pending) + subject.perform + expect(FriendDomain.exists?(domain: 'abc.com')).to be false + end + + it 'when my server is accepted' do + friend.update(active_state: :accepted) + subject.perform + expect(FriendDomain.exists?(domain: 'abc.com')).to be false + end + end end context 'with Like' do @@ -176,5 +213,32 @@ RSpec.describe ActivityPub::Activity::Undo do expect(sender.favourited?(status)).to be false end end + + context 'with EmojiReact' do + let(:status) { Fabricate(:status) } + + let(:content) { '😀' } + let(:object_json) do + { + id: 'bar', + type: 'Like', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: ActivityPub::TagManager.instance.uri_for(status), + content: content, + } + end + + before do + Fabricate(:favourite, account: sender, status: status) + Fabricate(:emoji_reaction, account: sender, status: status, name: content) + end + + it 'delete emoji reaction' do + subject.perform + reaction = EmojiReaction.find_by(account: sender, status: status) + expect(reaction).to be_nil + expect(sender.favourited?(status)).to be true + end + end end end diff --git a/spec/lib/activitypub/tag_manager_spec.rb b/spec/lib/activitypub/tag_manager_spec.rb index 2bff125a6a..9878952f05 100644 --- a/spec/lib/activitypub/tag_manager_spec.rb +++ b/spec/lib/activitypub/tag_manager_spec.rb @@ -27,6 +27,11 @@ RSpec.describe ActivityPub::TagManager do expect(subject.to(status)).to eq ['https://www.w3.org/ns/activitystreams#Public'] end + it 'returns followers collection for public_unlisted status' do + status = Fabricate(:status, visibility: :public_unlisted) + expect(subject.to(status)).to eq [account_followers_url(status.account)] + end + it 'returns followers collection for unlisted status' do status = Fabricate(:status, visibility: :unlisted) expect(subject.to(status)).to eq [account_followers_url(status.account)] @@ -69,12 +74,34 @@ RSpec.describe ActivityPub::TagManager do end end + describe '#to_for_friend' do + it 'returns followers collection for public_unlisted status' do + status = Fabricate(:status, visibility: :public_unlisted) + expect(subject.to_for_friend(status)).to eq [account_followers_url(status.account), 'kmyblue:LocalPublic'] + end + + it 'returns followers collection for unlisted status' do + status = Fabricate(:status, visibility: :unlisted) + expect(subject.to_for_friend(status)).to eq [account_followers_url(status.account)] + end + + it 'returns followers collection for private status' do + status = Fabricate(:status, visibility: :private) + expect(subject.to_for_friend(status)).to eq [account_followers_url(status.account)] + end + end + describe '#cc' do it 'returns followers collection for public status' do status = Fabricate(:status, visibility: :public) expect(subject.cc(status)).to eq [account_followers_url(status.account)] end + it 'returns public collection for public_unlisted status' do + status = Fabricate(:status, visibility: :public_unlisted) + expect(subject.cc(status)).to eq ['https://www.w3.org/ns/activitystreams#Public'] + end + it 'returns public collection for unlisted status' do status = Fabricate(:status, visibility: :unlisted) expect(subject.cc(status)).to eq ['https://www.w3.org/ns/activitystreams#Public'] @@ -114,6 +141,74 @@ RSpec.describe ActivityPub::TagManager do end end + describe '#cc_for_misskey' do + let(:user) { Fabricate(:user) } + + before do + user.settings.update(reject_unlisted_subscription: true, reject_public_unlisted_subscription: true) + user.save + end + + it 'returns public collection for public status' do + status = Fabricate(:status, visibility: :public) + expect(subject.cc_for_misskey(status)).to eq [account_followers_url(status.account)] + end + + it 'returns empty array for public_unlisted status' do + status = Fabricate(:status, account: user.account, visibility: :public_unlisted) + expect(subject.cc_for_misskey(status)).to eq [] + end + + it 'returns empty array for unlisted status' do + status = Fabricate(:status, account: user.account, visibility: :unlisted) + expect(subject.cc_for_misskey(status)).to eq [] + end + end + + describe '#searchable_by' do + it 'returns public collection for public status' do + status = Fabricate(:status, searchability: :public) + expect(subject.searchable_by(status)).to eq ['https://www.w3.org/ns/activitystreams#Public'] + end + + it 'returns followers collection for public_unlisted status' do + status = Fabricate(:status, searchability: :public_unlisted) + expect(subject.searchable_by(status)).to eq [account_followers_url(status.account)] + end + + it 'returns followers collection for private status' do + status = Fabricate(:status, searchability: :private) + expect(subject.searchable_by(status)).to eq [account_followers_url(status.account)] + end + + it 'returns empty array for direct status' do + status = Fabricate(:status, searchability: :direct) + expect(subject.searchable_by(status)).to eq [] + end + + it 'returns as:Limited array for limited status' do + status = Fabricate(:status, searchability: :limited) + expect(subject.searchable_by(status)).to eq ['as:Limited', 'kmyblue:Limited'] + end + end + + describe '#searchable_by_for_friend' do + it 'returns public collection for public status' do + status = Fabricate(:status, account: Fabricate(:account, searchability: :public), searchability: :public) + expect(subject.searchable_by_for_friend(status)).to eq ['https://www.w3.org/ns/activitystreams#Public'] + end + + it 'returns public collection for public_unlisted status' do + status = Fabricate(:status, account: Fabricate(:account, searchability: :public), searchability: :public_unlisted) + expect(subject.searchable_by_for_friend(status)).to eq [account_followers_url(status.account), 'kmyblue:LocalPublic'] + end + + it 'returns followers collection for private status' do + status = Fabricate(:status, account: Fabricate(:account, searchability: :public), searchability: :private) + expect(subject.searchable_by_for_friend(status)).to eq [account_followers_url(status.account)] + end + end + describe '#local_uri?' do it 'returns false for non-local URI' do expect(subject.local_uri?('http://example.com/123')).to be false diff --git a/spec/lib/cache_buster_spec.rb b/spec/lib/cache_buster_spec.rb index 84085608e8..3dc62a8154 100644 --- a/spec/lib/cache_buster_spec.rb +++ b/spec/lib/cache_buster_spec.rb @@ -28,6 +28,14 @@ describe CacheBuster do end context 'when using default options' do + around do |example| + # Disables the CacheBuster.new deprecation warning about default arguments. + # Remove this `silence` block when default arg support is removed from CacheBuster + ActiveSupport::Deprecation.silence do + example.run + end + end + include_examples 'makes_request' end diff --git a/spec/lib/status_reach_finder_spec.rb b/spec/lib/status_reach_finder_spec.rb index 4292f12bc6..2d8e075d5b 100644 --- a/spec/lib/status_reach_finder_spec.rb +++ b/spec/lib/status_reach_finder_spec.rb @@ -8,10 +8,11 @@ describe StatusReachFinder do subject { described_class.new(status) } let(:parent_status) { nil } + let(:quoted_status) { nil } let(:visibility) { :public } let(:searchability) { :public } let(:alice) { Fabricate(:account, username: 'alice') } - let(:status) { Fabricate(:status, account: alice, thread: parent_status, visibility: visibility, searchability: searchability) } + let(:status) { Fabricate(:status, account: alice, thread: parent_status, quote_of_id: quoted_status&.id, visibility: visibility, searchability: searchability) } context 'with a simple case' do let(:bob) { Fabricate(:account, username: 'bob', domain: 'foo.bar', protocol: :activitypub, inbox_url: 'https://foo.bar/inbox') } @@ -23,12 +24,14 @@ describe StatusReachFinder do it 'send status' do expect(subject.inboxes).to include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to_not include 'https://foo.bar/inbox' end end context 'with non-follower' do it 'send status' do expect(subject.inboxes).to_not include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to_not include 'https://foo.bar/inbox' end end end @@ -66,6 +69,22 @@ describe StatusReachFinder do end end + context 'when misskey with public_unlisted searchability' do + let(:sender_software) { 'misskey' } + let(:searchability) { :public_unlisted } + + it 'send status without setting' do + expect(subject.inboxes).to include 'https://foo.bar/inbox' + expect(subject.inboxes_for_misskey).to_not include 'https://foo.bar/inbox' + end + + it 'send status with setting' do + alice.user.settings.update(reject_unlisted_subscription: 'true') + expect(subject.inboxes).to_not include 'https://foo.bar/inbox' + expect(subject.inboxes_for_misskey).to include 'https://foo.bar/inbox' + end + end + context 'when misskey with public searchability' do let(:sender_software) { 'misskey' } @@ -75,6 +94,163 @@ describe StatusReachFinder do expect(subject.inboxes_for_misskey).to_not include 'https://foo.bar/inbox' end end + + context 'when has distributable friend server' do + let(:sender_software) { 'misskey' } + let(:searchability) { :public } + + before { Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', available: true, active_state: :accepted, pseudo_relay: true) } + + it 'send status without friend server' do + expect(subject.inboxes).to_not include 'https://foo.bar/inbox' + expect(subject.inboxes_for_misskey).to_not include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to include 'https://foo.bar/inbox' + end + end + end + + context 'when this server has a friend' do + let(:bob) { Fabricate(:account, username: 'bob', domain: 'foo.bar', protocol: :activitypub, inbox_url: 'https://foo.bar/inbox') } + + context 'with follower' do + before do + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :accepted) + bob.follow!(alice) + end + + it 'send status' do + expect(subject.inboxes).to_not include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to include 'https://foo.bar/inbox' + end + end + + context 'with follower but not local-distributable' do + before do + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :accepted, delivery_local: false) + bob.follow!(alice) + end + + it 'send status' do + expect(subject.inboxes).to include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to_not include 'https://foo.bar/inbox' + end + end + + context 'with non-follower and non-relay' do + before do + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :accepted) + end + + it 'send status' do + expect(subject.inboxes).to_not include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to_not include 'https://foo.bar/inbox' + end + end + + context 'with pending' do + before do + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :pending) + bob.follow!(alice) + end + + it 'send status' do + expect(subject.inboxes).to include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to_not include 'https://foo.bar/inbox' + end + end + + context 'with unidirection from them' do + before do + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :idle, passive_state: :accepted) + bob.follow!(alice) + end + + it 'send status' do + expect(subject.inboxes).to_not include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to include 'https://foo.bar/inbox' + end + end + + context 'when unavailable' do + before do + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :accepted, available: false) + bob.follow!(alice) + end + + it 'send status' do + expect(subject.inboxes).to include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to_not include 'https://foo.bar/inbox' + end + end + + context 'when distributable' do + before do + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', passive_state: :accepted, pseudo_relay: true) + end + + it 'send status' do + expect(subject.inboxes).to_not include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to include 'https://foo.bar/inbox' + end + end + + context 'when distributable and following' do + before do + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', passive_state: :accepted, pseudo_relay: true) + bob.follow!(alice) + end + + it 'send status' do + expect(subject.inboxes).to_not include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to include 'https://foo.bar/inbox' + end + end + + context 'when distributable reverse' do + before do + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :accepted, pseudo_relay: true) + end + + it 'send status' do + expect(subject.inboxes).to_not include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to include 'https://foo.bar/inbox' + end + end + + context 'when distributable but not local distributable' do + before do + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', passive_state: :accepted, pseudo_relay: true, delivery_local: false) + end + + it 'send status' do + expect(subject.inboxes).to include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to_not include 'https://foo.bar/inbox' + end + end + + context 'when distributable and following but not local distributable' do + before do + Fabricate(:friend_domain, domain: 'foo.bar', passive_state: :accepted, pseudo_relay: true, delivery_local: false) + bob.follow!(alice) + end + + it 'send status' do + expect(subject.inboxes).to include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to_not include 'https://foo.bar/inbox' + end + end + end + + context 'when it contains distributable friend server' do + before do + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', passive_state: :accepted, pseudo_relay: true) + end + + it 'includes the inbox of the mentioned account' do + expect(subject.inboxes).to_not include 'https://foo.bar/inbox' + expect(subject.inboxes_for_misskey).to_not include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to include 'https://foo.bar/inbox' + end end context 'when it contains mentions of remote accounts' do @@ -165,6 +341,15 @@ describe StatusReachFinder do end end end + + context 'when it is a quote to a remote account' do + let(:bob) { Fabricate(:account, username: 'bob', domain: 'foo.bar', protocol: :activitypub, inbox_url: 'https://foo.bar/inbox') } + let(:quoted_status) { Fabricate(:status, account: bob) } + + it 'includes the inbox of the quoted-to account' do + expect(subject.inboxes).to include 'https://foo.bar/inbox' + end + end end context 'with extended domain block' do @@ -229,4 +414,106 @@ describe StatusReachFinder do end end end + + describe '#inboxes_for_friend and distributables' do + subject { described_class.new(status).inboxes_for_friend } + + let(:visibility) { :public } + let(:searchability) { :public } + let(:alice) { Fabricate(:account, username: 'alice') } + let(:status) { Fabricate(:status, account: alice, visibility: visibility, searchability: searchability) } + + context 'when a simple case' do + before do + Fabricate(:friend_domain, domain: 'abc.com', inbox_url: 'https://abc.com/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: true, available: true) + Fabricate(:friend_domain, domain: 'def.com', inbox_url: 'https://def.com/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: true, available: true) + Fabricate(:friend_domain, domain: 'ghi.com', inbox_url: 'https://ghi.com/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: true, available: false) + Fabricate(:friend_domain, domain: 'jkl.com', inbox_url: 'https://jkl.com/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: false, available: true) + Fabricate(:friend_domain, domain: 'mno.com', inbox_url: 'https://mno.com/inbox', active_state: :accepted, passive_state: :idle, pseudo_relay: true, available: true) + Fabricate(:friend_domain, domain: 'pqr.com', inbox_url: 'https://pqr.com/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: true, available: true) + Fabricate(:unavailable_domain, domain: 'pqr.com') + Fabricate(:friend_domain, domain: 'stu.com', inbox_url: 'https://stu.com/inbox', active_state: :idle, passive_state: :accepted, pseudo_relay: true, available: true) + Fabricate(:friend_domain, domain: 'vwx.com', inbox_url: 'https://vwx.com/inbox', active_state: :idle, passive_state: :accepted, pseudo_relay: true, available: true, delivery_local: false) + end + + it 'returns friend servers' do + expect(subject).to include 'https://abc.com/inbox' + expect(subject).to include 'https://def.com/inbox' + end + + it 'not contains unavailable friends' do + expect(subject).to_not include 'https://ghi.com/inbox' + end + + it 'not contains no-relay friends' do + expect(subject).to_not include 'https://jkl.com/inbox' + end + + it 'contains no-mutual friends' do + expect(subject).to include 'https://mno.com/inbox' + expect(subject).to include 'https://stu.com/inbox' + end + + it 'not contains un local distable' do + expect(subject).to_not include 'https://vwx.com/inbox' + end + + it 'not contains unavailable domain friends' do + expect(subject).to_not include 'https://pqr.com/inbox' + end + + context 'when public visibility' do + let(:visibility) { :public } + let(:searchability) { :direct } + + it 'returns friend servers' do + expect(subject).to_not eq [] + end + end + + context 'when public_unlsited visibility' do + let(:visibility) { :public_unlisted } + let(:searchability) { :direct } + + it 'returns friend servers' do + expect(subject).to_not eq [] + end + end + + context 'when unlsited visibility with public searchability' do + let(:visibility) { :unlisted } + let(:searchability) { :public } + + it 'returns friend servers' do + expect(subject).to_not eq [] + end + end + + context 'when unlsited visibility with public_unlisted searchability' do + let(:visibility) { :unlisted } + let(:searchability) { :public_unlisted } + + it 'returns friend servers' do + expect(subject).to_not eq [] + end + end + + context 'when unlsited visibility with private searchability' do + let(:visibility) { :unlisted } + let(:searchability) { :private } + + it 'returns empty servers' do + expect(subject).to eq [] + end + end + + context 'when private visibility' do + let(:visibility) { :private } + + it 'returns friend servers' do + expect(subject).to eq [] + end + end + end + end end diff --git a/spec/mailers/admin_mailer_spec.rb b/spec/mailers/admin_mailer_spec.rb index 423dce88ab..23b99a68cc 100644 --- a/spec/mailers/admin_mailer_spec.rb +++ b/spec/mailers/admin_mailer_spec.rb @@ -64,6 +64,26 @@ RSpec.describe AdminMailer do end end + describe '.new_pending_friend_server' do + let(:recipient) { Fabricate(:account, username: 'Barklums') } + let(:friend) { Fabricate(:friend_domain, passive_state: :pending, domain: 'abc.com') } + let(:mail) { described_class.with(recipient: recipient).new_pending_friend_server(friend) } + + before do + recipient.user.update(locale: :en) + end + + it 'renders the headers' do + expect(mail.subject).to eq('New friend server up for review on cb6e6126.ngrok.io (abc.com)') + expect(mail.to).to eq [recipient.user_email] + expect(mail.from).to eq ['notifications@localhost'] + end + + it 'renders the body' do + expect(mail.body.encoded).to match 'The new friend server abc.com is waiting for your review. You can approve or reject this application.' + end + end + describe '.new_trends' do let(:recipient) { Fabricate(:account, username: 'Snurf') } let(:links) { [] } diff --git a/spec/mailers/previews/admin_mailer_preview.rb b/spec/mailers/previews/admin_mailer_preview.rb index bc8f0193b9..7ba6f08239 100644 --- a/spec/mailers/previews/admin_mailer_preview.rb +++ b/spec/mailers/previews/admin_mailer_preview.rb @@ -8,6 +8,11 @@ class AdminMailerPreview < ActionMailer::Preview AdminMailer.with(recipient: Account.first).new_pending_account(User.pending.first) end + # Preview this email at http://localhost:3000/rails/mailers/admin_mailer/new_pending_friend_server + def new_pending_friend_server + AdminMailer.with(recipient: Account.first).new_pending_friend_server(User.pending.first) + end + # Preview this email at http://localhost:3000/rails/mailers/admin_mailer/new_trends def new_trends AdminMailer.with(recipient: Account.first).new_trends(PreviewCard.joins(:trend).limit(3), Tag.limit(3), Status.joins(:trend).where(reblog_of_id: nil).limit(3)) diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index c9d12baf8e..c13d57c761 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -700,7 +700,7 @@ RSpec.describe Account do expect(subject.match('Check this out https://medium.com/@alice/some-article#.abcdef123')).to be_nil end - xit 'does not match URL query string' do + it 'does not match URL query string' do expect(subject.match('https://example.com/?x=@alice')).to be_nil end end @@ -845,7 +845,7 @@ RSpec.describe Account do match = Fabricate(:account, display_name: 'pattern and suffix') account = Fabricate(:account, display_name: 'prefix and pattern') - expect(described_class.matches_display_name('pattern')).to eq [match, account] + expect(described_class.matches_display_name('pattern')).to contain_exactly(match, account) end end @@ -854,7 +854,7 @@ RSpec.describe Account do match = Fabricate(:account, username: 'pattern_and_suffix') account = Fabricate(:account, username: 'prefix_and_pattern') - expect(described_class.matches_username('pattern')).to eq [match, account] + expect(described_class.matches_username('pattern')).to contain_exactly(match, account) end end diff --git a/spec/models/concerns/status_threading_concern_spec.rb b/spec/models/concerns/status_threading_concern_spec.rb index 2eac1ca6e5..d0f5179550 100644 --- a/spec/models/concerns/status_threading_concern_spec.rb +++ b/spec/models/concerns/status_threading_concern_spec.rb @@ -129,4 +129,56 @@ describe StatusThreadingConcern do expect(a.descendants(20)).to eq [c, d, e, f] end end + + describe '#readable_references' do + subject { status.readable_references(account).pluck(:id) } + + let(:visibility) { :public } + let(:alice) { Fabricate(:account) } + let(:referred_account) { Fabricate(:account) } + let(:status) { Fabricate(:status, account: account) } + let(:referred_status) { Fabricate(:status, account: referred_account, visibility: visibility) } + let(:referred_follower) { Fabricate(:account) } + let(:follower) { Fabricate(:account) } + let(:third_account) { Fabricate(:account) } + let(:account) { third_account } + + before do + referred_follower.follow!(referred_account) + follower.follow!(alice) + Fabricate(:status_reference, status: status, target_status: referred_status) + end + + it 'with a simple case' do + expect(subject).to include referred_status.id + end + + context 'when private post' do + let(:visibility) { :private } + + context 'with referred post follower' do + let(:account) { referred_follower } + + it 'can show' do + expect(subject).to include referred_status.id + end + end + + context 'with original post follower' do + let(:account) { follower } + + it 'can show' do + expect(subject).to_not include referred_status.id + end + end + + context 'with other account' do + let(:account) { third_account } + + it 'can show' do + expect(subject).to_not include referred_status.id + end + end + end + end end diff --git a/spec/models/friend_domain_spec.rb b/spec/models/friend_domain_spec.rb new file mode 100644 index 0000000000..647c39e5a8 --- /dev/null +++ b/spec/models/friend_domain_spec.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe FriendDomain do + let(:friend) { Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox') } + + before do + stub_request(:post, 'https://foo.bar/inbox') + end + + describe '#follow!' do + it 'call inbox' do + friend.update(active_state: :accepted, passive_state: :accepted) + friend.follow! + expect(friend.active_follow_activity_id).to_not be_nil + expect(friend.i_am_pending?).to be true + expect(friend.they_are_idle?).to be true + expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({ + id: friend.active_follow_activity_id, + type: 'Follow', + actor: 'https://cb6e6126.ngrok.io/actor', + object: 'https://www.w3.org/ns/activitystreams#Public', + }))).to have_been_made.once + end + end + + describe '#unfollow!' do + it 'call inbox' do + friend.update(active_follow_activity_id: 'ohagi', active_state: :accepted, passive_state: :accepted) + friend.unfollow! + expect(friend.active_follow_activity_id).to be_nil + expect(friend.i_am_idle?).to be true + expect(friend.they_are_idle?).to be true + expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({ + type: 'Undo', + object: { + id: 'ohagi', + type: 'Follow', + actor: 'https://cb6e6126.ngrok.io/actor', + object: 'https://www.w3.org/ns/activitystreams#Public', + }, + }))).to have_been_made.once + end + end + + describe '#accept!' do + it 'call inbox' do + friend.update(passive_follow_activity_id: 'ohagi', active_state: :accepted, passive_state: :pending) + friend.accept! + expect(friend.they_are_accepted?).to be true + expect(friend.i_am_idle?).to be true + expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({ + id: 'ohagi#accepts/friends', + type: 'Accept', + actor: 'https://cb6e6126.ngrok.io/actor', + object: 'ohagi', + }))).to have_been_made.once + end + end + + describe '#reject!' do + it 'call inbox' do + friend.update(passive_follow_activity_id: 'ohagi', active_state: :accepted, passive_state: :pending) + friend.reject! + expect(friend.they_are_rejected?).to be true + expect(friend.i_am_idle?).to be true + expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({ + id: 'ohagi#rejects/friends', + type: 'Reject', + actor: 'https://cb6e6126.ngrok.io/actor', + object: 'ohagi', + }))).to have_been_made.once + end + end + + describe '#delete!' do + it 'call inbox' do + friend.update(active_state: :pending) + friend.destroy + expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({ + type: 'Delete', + actor: 'https://cb6e6126.ngrok.io/actor', + object: 'https://www.w3.org/ns/activitystreams#Public', + }))).to have_been_made.once + end + end +end diff --git a/spec/models/public_feed_spec.rb b/spec/models/public_feed_spec.rb index 965e293c5e..3d13783a50 100644 --- a/spec/models/public_feed_spec.rb +++ b/spec/models/public_feed_spec.rb @@ -89,7 +89,7 @@ RSpec.describe PublicFeed do end it 'excludes public_unlisted statuses' do - expect(subject).to_not include(public_unlisted_status.id) + expect(subject).to include(public_unlisted_status.id) end end @@ -105,7 +105,7 @@ RSpec.describe PublicFeed do end it 'excludes public_unlisted statuses' do - expect(subject).to_not include(public_unlisted_status.id) + expect(subject).to include(public_unlisted_status.id) end end end diff --git a/spec/models/relationship_filter_spec.rb b/spec/models/relationship_filter_spec.rb index ac31885774..fccd42aaad 100644 --- a/spec/models/relationship_filter_spec.rb +++ b/spec/models/relationship_filter_spec.rb @@ -6,32 +6,60 @@ describe RelationshipFilter do let(:account) { Fabricate(:account) } describe '#results' do - context 'when default params are used' do - subject do - described_class.new(account, 'order' => 'active').results + let(:account_of_7_months) { Fabricate(:account_stat, statuses_count: 1, last_status_at: 7.months.ago).account } + let(:account_of_1_day) { Fabricate(:account_stat, statuses_count: 1, last_status_at: 1.day.ago).account } + let(:account_of_3_days) { Fabricate(:account_stat, statuses_count: 1, last_status_at: 3.days.ago).account } + let(:silent_account) { Fabricate(:account_stat, statuses_count: 0, last_status_at: nil).account } + + before do + account.follow!(account_of_7_months) + account.follow!(account_of_1_day) + account.follow!(account_of_3_days) + account.follow!(silent_account) + end + + context 'when ordering by last activity' do + context 'when not filtering' do + subject do + described_class.new(account, 'order' => 'active').results + end + + it 'returns followings ordered by last activity' do + expect(subject).to eq [account_of_1_day, account_of_3_days, account_of_7_months, silent_account] + end end - before do - add_following_account_with(last_status_at: 7.days.ago) - add_following_account_with(last_status_at: 1.day.ago) - add_following_account_with(last_status_at: 3.days.ago) + context 'when filtering for dormant accounts' do + subject do + described_class.new(account, 'order' => 'active', 'activity' => 'dormant').results + end + + it 'returns dormant followings ordered by last activity' do + expect(subject).to eq [account_of_7_months, silent_account] + end + end + end + + context 'when ordering by account creation' do + context 'when not filtering' do + subject do + described_class.new(account, 'order' => 'recent').results + end + + it 'returns followings ordered by last account creation' do + expect(subject).to eq [silent_account, account_of_3_days, account_of_1_day, account_of_7_months] + end end - it 'returns followings ordered by last activity' do - expected_result = account.following.eager_load(:account_stat).reorder(nil).by_recent_status + context 'when filtering for dormant accounts' do + subject do + described_class.new(account, 'order' => 'recent', 'activity' => 'dormant').results + end - expect(subject).to eq expected_result + it 'returns dormant followings ordered by last activity' do + expect(subject).to eq [silent_account, account_of_7_months] + end end end end - - def add_following_account_with(last_status_at:) - following_account = Fabricate(:account) - Fabricate(:account_stat, account: following_account, - last_status_at: last_status_at, - statuses_count: 1, - following_count: 0, - followers_count: 0) - Fabricate(:follow, account: account, target_account: following_account).account - end end diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb index 6462307594..136b301933 100644 --- a/spec/models/status_spec.rb +++ b/spec/models/status_spec.rb @@ -114,7 +114,7 @@ RSpec.describe Status do end end - describe '#searchability' do + describe '#compute_searchability' do subject { Fabricate(:status, account: account, searchability: status_searchability) } let(:account_searchability) { :public } @@ -137,6 +137,27 @@ RSpec.describe Status do end end + context 'when public-public_unlisted but silenced' do + let(:silenced_at) { Time.now.utc } + let(:status_searchability) { :public_unlisted } + + it 'returns private' do + expect(subject.compute_searchability).to eq 'private' + end + end + + context 'when public-public_unlisted' do + let(:status_searchability) { :public_unlisted } + + it 'returns public' do + expect(subject.compute_searchability).to eq 'public' + end + + it 'returns public_unlisted for local' do + expect(subject.compute_searchability_local).to eq 'public_unlisted' + end + end + context 'when public-private' do let(:status_searchability) { :private } @@ -215,6 +236,57 @@ RSpec.describe Status do expect(subject.compute_searchability).to eq 'public' end end + + context 'when public-public_unlisted of local account' do + let(:account_searchability) { :public } + let(:account_domain) { nil } + let(:status_searchability) { :public_unlisted } + + it 'returns public' do + expect(subject.compute_searchability).to eq 'public' + end + + it 'returns public_unlisted for local' do + expect(subject.compute_searchability_local).to eq 'public_unlisted' + end + + it 'returns private for activitypub' do + expect(subject.compute_searchability_activitypub).to eq 'private' + end + end + end + + describe '#quote' do + let(:target_status) { Fabricate(:status) } + let(:quote) { true } + + before do + Fabricate(:status_reference, status: subject, target_status: target_status, quote: quote) + end + + context 'when quoting single' do + it 'get quote' do + expect(subject.quote).to_not be_nil + expect(subject.quote.id).to eq target_status.id + end + end + + context 'when multiple quotes' do + it 'get quote' do + target2 = Fabricate(:status) + Fabricate(:status_reference, status: subject, quote: quote) + expect(subject.quote).to_not be_nil + expect([target_status.id, target2.id].include?(subject.quote.id)).to be true + end + end + + context 'when no quote but reference' do + let(:quote) { false } + + it 'get quote' do + expect(subject.quote).to be_nil + end + end end describe '#content' do @@ -324,6 +396,38 @@ RSpec.describe Status do end end + describe '.blocks_map' do + subject { described_class.blocks_map([status.account.id], account) } + + let(:status) { Fabricate(:status) } + let(:account) { Fabricate(:account) } + + it 'returns a hash' do + expect(subject).to be_a Hash + end + + it 'contains true value' do + account.block!(status.account) + expect(subject[status.account.id]).to be true + end + end + + describe '.domain_blocks_map' do + subject { described_class.domain_blocks_map([status.account.domain], account) } + + let(:status) { Fabricate(:status, account: Fabricate(:account, domain: 'foo.bar', uri: 'https://foo.bar/status')) } + let(:account) { Fabricate(:account) } + + it 'returns a hash' do + expect(subject).to be_a Hash + end + + it 'contains true value' do + account.block_domain!(status.account.domain) + expect(subject[status.account.domain]).to be true + end + end + describe '.favourites_map' do subject { described_class.favourites_map([status], account) } diff --git a/spec/models/tag_feed_spec.rb b/spec/models/tag_feed_spec.rb index 270797ccd8..5206e7cede 100644 --- a/spec/models/tag_feed_spec.rb +++ b/spec/models/tag_feed_spec.rb @@ -91,18 +91,36 @@ describe TagFeed, type: :service do expect(results).to include status_tagged_with_cats end + it 'unlisted/public_unlisted_searchability post returns' do + status_tagged_with_cats.update(visibility: :unlisted, searchability: :public_unlisted) + results = described_class.new(tag_cats, nil).get(20) + expect(results).to include status_tagged_with_cats + end + it 'unlisted/public_searchability post returns with account' do status_tagged_with_cats.update(visibility: :unlisted, searchability: :public) results = described_class.new(tag_cats, account).get(20) expect(results).to include status_tagged_with_cats end + it 'unlisted/public_unlisted_searchability post returns with account' do + status_tagged_with_cats.update(visibility: :unlisted, searchability: :public_unlisted) + results = described_class.new(tag_cats, account).get(20) + expect(results).to include status_tagged_with_cats + end + it 'private post not returns' do status_tagged_with_cats.update(visibility: :private, searchability: :public) results = described_class.new(tag_cats, nil).get(20) expect(results).to_not include status_tagged_with_cats end + it 'private, public_unlisted post not returns' do + status_tagged_with_cats.update(visibility: :private, searchability: :public_unlisted) + results = described_class.new(tag_cats, nil).get(20) + expect(results).to_not include status_tagged_with_cats + end + it 'private post not returns with account' do status_tagged_with_cats.update(visibility: :private, searchability: :public) results = described_class.new(tag_cats, account).get(20) diff --git a/spec/policies/status_policy_spec.rb b/spec/policies/status_policy_spec.rb index 271c70804b..3bdc2084d8 100644 --- a/spec/policies/status_policy_spec.rb +++ b/spec/policies/status_policy_spec.rb @@ -167,6 +167,48 @@ RSpec.describe StatusPolicy, type: :model do end end + context 'with the permission of emoji_reaction?' do + permissions :emoji_reaction? do + it 'grants access when viewer is not blocked' do + follow = Fabricate(:follow) + status.account = follow.target_account + + expect(subject).to permit(follow.account, status) + end + + it 'denies when viewer is blocked' do + block = Fabricate(:block) + status.account = block.target_account + + expect(subject).to_not permit(block.account, status) + end + end + end + + context 'with the permission of quote?' do + permissions :quote? do + it 'grants access when viewer is not blocked' do + follow = Fabricate(:follow) + status.account = follow.target_account + + expect(subject).to permit(follow.account, status) + end + + it 'denies when viewer is blocked' do + block = Fabricate(:block) + status.account = block.target_account + + expect(subject).to_not permit(block.account, status) + end + + it 'denies when private visibility' do + status.visibility = :private + + expect(subject).to_not permit(Fabricate(:account), status) + end + end + end + context 'with the permission of update?' do permissions :update? do it 'grants access if owner' do diff --git a/spec/requests/cache_spec.rb b/spec/requests/cache_spec.rb index 178d19ed0d..c391c8b3da 100644 --- a/spec/requests/cache_spec.rb +++ b/spec/requests/cache_spec.rb @@ -30,6 +30,7 @@ module TestEndpoints /directory /@alice /@alice/110224538612341312 + /deck/home ).freeze # Endpoints that should be cachable when accessed anonymously but have a Vary diff --git a/spec/search/services/statuses_search_service_spec.rb b/spec/search/services/statuses_search_service_spec.rb index c0d4116ae0..51245f7354 100644 --- a/spec/search/services/statuses_search_service_spec.rb +++ b/spec/search/services/statuses_search_service_spec.rb @@ -63,6 +63,45 @@ describe StatusesSearchService do end end + context 'when public_unlisted searchability' do + let(:searchability) { :public_unlisted } + let(:account) { other } + + context 'with other account' do + it 'search status' do + expect(subject.count).to eq 1 + expect(subject).to include status.id + end + end + + context 'with follower' do + let(:account) { following } + + it 'search status' do + expect(subject.count).to eq 1 + expect(subject).to include status.id + end + end + + context 'with reacted user' do + let(:account) { reacted } + + it 'search status' do + expect(subject.count).to eq 1 + expect(subject).to include status.id + end + end + + context 'with self' do + let(:account) { alice } + + it 'search status' do + expect(subject.count).to eq 1 + expect(subject).to include status.id + end + end + end + context 'when private searchability' do let(:searchability) { :private } let(:account) { other } diff --git a/spec/serializers/activitypub/note_serializer_spec.rb b/spec/serializers/activitypub/note_serializer_spec.rb index 0425e2e66b..0c8c78e7a9 100644 --- a/spec/serializers/activitypub/note_serializer_spec.rb +++ b/spec/serializers/activitypub/note_serializer_spec.rb @@ -5,9 +5,11 @@ require 'rails_helper' describe ActivityPub::NoteSerializer do subject { JSON.parse(@serialization.to_json) } + let(:visibility) { :public } + let(:searchability) { :public } let!(:account) { Fabricate(:account) } let!(:other) { Fabricate(:account) } - let!(:parent) { Fabricate(:status, account: account, visibility: :public) } + let!(:parent) { Fabricate(:status, account: account, visibility: visibility, searchability: searchability, language: 'zh-TW') } let!(:reply_by_account_first) { Fabricate(:status, account: account, thread: parent, visibility: :public) } let!(:reply_by_account_next) { Fabricate(:status, account: account, thread: parent, visibility: :public) } let!(:reply_by_other_first) { Fabricate(:status, account: other, thread: parent, visibility: :public) } @@ -15,17 +17,22 @@ describe ActivityPub::NoteSerializer do let!(:reply_by_account_visibility_direct) { Fabricate(:status, account: account, thread: parent, visibility: :direct) } let!(:referred) { nil } let!(:referred2) { nil } - let(:convert_to_quote) { false } before(:each) do parent.references << referred if referred.present? parent.references << referred2 if referred2.present? - account.user&.settings&.[]=('single_ref_to_quote', true) if convert_to_quote @serialization = ActiveModelSerializers::SerializableResource.new(parent, serializer: described_class, adapter: ActivityPub::Adapter) end - it 'has a Note type' do - expect(subject['type']).to eql('Note') + it 'has the expected shape' do + expect(subject).to include({ + '@context' => include('https://www.w3.org/ns/activitystreams'), + 'type' => 'Note', + 'attributedTo' => ActivityPub::TagManager.instance.uri_for(account), + 'contentMap' => include({ + 'zh-TW' => a_kind_of(String), + }), + }) end it 'has a replies collection' do @@ -48,6 +55,30 @@ describe ActivityPub::NoteSerializer do expect(subject['replies']['first']['items']).to_not include(reply_by_account_visibility_direct.uri) end + it 'send as public visibility' do + expect(subject['to']).to include 'https://www.w3.org/ns/activitystreams#Public' + end + + context 'when public_unlisted visibility' do + let(:visibility) { :public_unlisted } + + it 'send as unlisted visibility' do + expect(subject['to']).to_not include 'https://www.w3.org/ns/activitystreams#Public' + end + end + + it 'send as public searchability' do + expect(subject['searchableBy']).to include 'https://www.w3.org/ns/activitystreams#Public' + end + + context 'when public_unlisted searchability' do + let(:searchability) { :public_unlisted } + + it 'send as private searchability' do + expect(subject['searchableBy']).to_not include 'https://www.w3.org/ns/activitystreams#Public' + end + end + context 'when has quote but no_convert setting' do let(:referred) { Fabricate(:status) } @@ -64,28 +95,4 @@ describe ActivityPub::NoteSerializer do expect(subject['references']['first']['items']).to include referred.uri end end - - context 'when has quote and convert setting' do - let(:referred) { Fabricate(:status) } - let(:convert_to_quote) { true } - - it 'has as quote' do - expect(subject['quoteUri']).to_not be_nil - expect(subject['quoteUri']).to eq referred.uri - expect(subject['_misskey_quote']).to eq referred.uri - expect(subject['references']['first']['items']).to include referred.uri - end - end - - context 'when has multiple references and convert setting' do - let(:referred) { Fabricate(:status) } - let(:referred2) { Fabricate(:status) } - let(:convert_to_quote) { true } - - it 'has as quote' do - expect(subject['quoteUri']).to be_nil - expect(subject['references']['first']['items']).to include referred.uri - expect(subject['references']['first']['items']).to include referred2.uri - end - end end diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb index b23aa1cea3..8955084879 100644 --- a/spec/services/activitypub/process_account_service_spec.rb +++ b/spec/services/activitypub/process_account_service_spec.rb @@ -21,6 +21,7 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do searchableBy: searchable_by, indexable: indexable, summary: sender_bio, + actor_type: 'Person', }.with_indifferent_access end @@ -53,6 +54,14 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do end context 'when limited' do + let(:searchable_by) { 'kmyblue:Limited' } + + it 'searchability is limited' do + expect(subject.searchability).to eq 'limited' + end + end + + context 'when limited old spec' do let(:searchable_by) { 'as:Limited' } it 'searchability is limited' do diff --git a/spec/services/block_domain_service_spec.rb b/spec/services/block_domain_service_spec.rb index 36dce9d196..30fb760487 100644 --- a/spec/services/block_domain_service_spec.rb +++ b/spec/services/block_domain_service_spec.rb @@ -10,9 +10,13 @@ RSpec.describe BlockDomainService, type: :service do let!(:bad_status_with_attachment) { Fabricate(:status, account: bad_account, text: 'Hahaha') } let!(:bad_attachment) { Fabricate(:media_attachment, account: bad_account, status: bad_status_with_attachment, file: attachment_fixture('attachment.jpg')) } let!(:already_banned_account) { Fabricate(:account, username: 'badguy', domain: 'evil.org', suspended: true, silenced: true) } + let!(:bad_friend) { Fabricate(:friend_domain, domain: 'evil.org', inbox_url: 'https://evil.org/inbox', active_state: :accepted, passive_state: :accepted) } describe 'for a suspension' do before do + stub_request(:post, 'https://evil.org/inbox').with(body: hash_including({ + type: 'Delete', + })) subject.call(DomainBlock.create!(domain: 'evil.org', severity: :suspend)) end @@ -41,6 +45,21 @@ RSpec.describe BlockDomainService, type: :service do expect { bad_status_with_attachment.reload }.to raise_exception ActiveRecord::RecordNotFound expect { bad_attachment.reload }.to raise_exception ActiveRecord::RecordNotFound end + + it 'removes remote friend from that domain' do + expect(FriendDomain.find_by(domain: 'evil.org')).to be_nil + end + end + + describe 'for rejecting friend only' do + before do + stub_request(:post, 'https://evil.org/inbox') + subject.call(DomainBlock.create!(domain: 'evil.org', severity: :noop, reject_friend: true)) + end + + it 'removes remote friend from that domain' do + expect(FriendDomain.find_by(domain: 'evil.org')).to be_nil + end end describe 'for a silence with reject media' do diff --git a/spec/services/delivery_antenna_service_spec.rb b/spec/services/delivery_antenna_service_spec.rb index 224d448b1a..684a5da9dd 100644 --- a/spec/services/delivery_antenna_service_spec.rb +++ b/spec/services/delivery_antenna_service_spec.rb @@ -295,6 +295,18 @@ RSpec.describe DeliveryAntennaService, type: :service do end end + context 'when public_unlisted searchability' do + let(:searchability) { :public_unlisted } + + it 'detecting antenna' do + expect(antenna_feed_of(antenna)).to include status.id + end + + it 'not detecting antenna' do + expect(antenna_feed_of(empty_antenna)).to_not include status.id + end + end + context 'when private searchability' do let(:searchability) { :private } @@ -317,6 +329,15 @@ RSpec.describe DeliveryAntennaService, type: :service do end end + context 'when public_unlisted searchability' do + let(:searchability) { :public_unlisted } + + it 'detecting antenna' do + expect(antenna_feed_of(antenna)).to include status.id + expect(antenna_feed_of(empty_antenna)).to include status.id + end + end + context 'when private searchability' do let(:searchability) { :private } diff --git a/spec/services/fan_out_on_write_service_spec.rb b/spec/services/fan_out_on_write_service_spec.rb index 65db23214a..17d9f91252 100644 --- a/spec/services/fan_out_on_write_service_spec.rb +++ b/spec/services/fan_out_on_write_service_spec.rb @@ -278,7 +278,7 @@ RSpec.describe FanOutOnWriteService, type: :service do it 'is broadcast publicly' do expect(redis).to have_received(:publish).with('timeline:hashtag:hoge', anything) expect(redis).to have_received(:publish).with('timeline:public:local', anything) - expect(redis).to_not have_received(:publish).with('timeline:public', anything) + expect(redis).to have_received(:publish).with('timeline:public', anything) end context 'with list' do @@ -363,6 +363,15 @@ RSpec.describe FanOutOnWriteService, type: :service do expect(redis).to_not have_received(:publish).with('timeline:public', anything) end + context 'with searchability public_unlisted' do + let(:searchability) { 'public_unlisted' } + + it 'is not broadcast to the hashtag stream' do + expect(redis).to have_received(:publish).with('timeline:hashtag:hoge', anything) + expect(redis).to have_received(:publish).with('timeline:hashtag:hoge:local', anything) + end + end + context 'with searchability private' do let(:searchability) { 'private' } diff --git a/spec/services/post_status_service_spec.rb b/spec/services/post_status_service_spec.rb index 5e2a54c264..529ab09969 100644 --- a/spec/services/post_status_service_spec.rb +++ b/spec/services/post_status_service_spec.rb @@ -127,6 +127,13 @@ RSpec.describe PostStatusService, type: :service do expect(status.searchability).to eq 'private' end + it 'creates a status with limited searchability for silenced users with public_unlisted searchability' do + status = subject.call(Fabricate(:account, silenced: true), text: 'test', searchability: :public_unlisted, visibility: :public) + + expect(status).to be_persisted + expect(status.searchability).to eq 'private' + end + it 'creates a status with the given searchability=public / visibility=unlisted' do status = create_status_with_options(searchability: :public, visibility: :unlisted) @@ -134,6 +141,13 @@ RSpec.describe PostStatusService, type: :service do expect(status.searchability).to eq 'public' end + it 'creates a status with the given searchability=public_unlisted / visibility=unlisted' do + status = create_status_with_options(searchability: :public_unlisted, visibility: :unlisted) + + expect(status).to be_persisted + expect(status.searchability).to eq 'public_unlisted' + end + it 'creates a status with the given searchability=public / visibility=private' do status = create_status_with_options(searchability: :public, visibility: :private) @@ -141,6 +155,13 @@ RSpec.describe PostStatusService, type: :service do expect(status.searchability).to eq 'private' end + it 'creates a status with the given searchability=public_unlisted / visibility=private' do + status = create_status_with_options(searchability: :public_unlisted, visibility: :private) + + expect(status).to be_persisted + expect(status.searchability).to eq 'private' + end + it 'creates a status for the given application' do application = Fabricate(:application) diff --git a/spec/services/process_references_service_spec.rb b/spec/services/process_references_service_spec.rb index c41144a2aa..37818b2d45 100644 --- a/spec/services/process_references_service_spec.rb +++ b/spec/services/process_references_service_spec.rb @@ -10,6 +10,8 @@ RSpec.describe ProcessReferencesService, type: :service do let(:status) { Fabricate(:status, account: account, text: text, visibility: visibility) } let(:target_status) { Fabricate(:status, account: Fabricate(:user).account, visibility: target_status_visibility) } let(:target_status_uri) { ActivityPub::TagManager.instance.uri_for(target_status) } + let(:quote_urls) { nil } + let(:allow_quote) { true } def notify?(target_status_id = nil) target_status_id ||= target_status.id @@ -18,7 +20,10 @@ RSpec.describe ProcessReferencesService, type: :service do describe 'posting new status' do subject do - described_class.new.call(status, reference_parameters, urls: urls, fetch_remote: fetch_remote) + target_status.account.user.settings['allow_quote'] = false unless allow_quote + target_status.account.user&.save + + described_class.new.call(status, reference_parameters, urls: urls, fetch_remote: fetch_remote, quote_urls: quote_urls) status.reference_objects.pluck(:target_status_id, :attribute_type) end @@ -35,6 +40,10 @@ RSpec.describe ProcessReferencesService, type: :service do expect(subject.pluck(1)).to include 'RT' expect(notify?).to be true end + + it 'not quote' do + expect(status.quote).to be_nil + end end context 'when multiple references' do @@ -86,6 +95,61 @@ RSpec.describe ProcessReferencesService, type: :service do end end + context 'with quote as parameter only' do + let(:text) { 'Hello' } + let(:quote_urls) { [ActivityPub::TagManager.instance.uri_for(target_status)] } + + it 'post status' do + expect(subject.size).to eq 1 + expect(subject.pluck(0)).to include target_status.id + expect(subject.pluck(1)).to include 'QT' + expect(status.quote).to_not be_nil + expect(status.quote.id).to eq target_status.id + expect(notify?).to be true + end + end + + context 'with quote as parameter and embed' do + let(:text) { "Hello QT #{target_status_uri}" } + let(:quote_urls) { [ActivityPub::TagManager.instance.uri_for(target_status)] } + + it 'post status' do + expect(subject.size).to eq 1 + expect(subject.pluck(0)).to include target_status.id + expect(subject.pluck(1)).to include 'QT' + expect(status.quote).to_not be_nil + expect(status.quote.id).to eq target_status.id + expect(notify?).to be true + end + end + + context 'with quote as parameter but embed is not quote' do + let(:text) { "Hello RE #{target_status_uri}" } + let(:quote_urls) { [ActivityPub::TagManager.instance.uri_for(target_status)] } + + it 'post status' do + expect(subject.size).to eq 1 + expect(subject.pluck(0)).to include target_status.id + expect(subject.pluck(1)).to include 'QT' + expect(status.quote).to_not be_nil + expect(status.quote.id).to eq target_status.id + expect(notify?).to be true + end + end + + context 'when quote is rejected' do + let(:text) { "Hello QT #{target_status_uri}" } + let(:allow_quote) { false } + + it 'post status' do + expect(subject.size).to eq 1 + expect(subject.pluck(0)).to include target_status.id + expect(subject.pluck(1)).to include 'BT' + expect(status.quote).to be_nil + expect(notify?).to be true + end + end + context 'with quote and reference' do let(:target_status2) { Fabricate(:status) } let(:target_status2_uri) { ActivityPub::TagManager.instance.uri_for(target_status2) } @@ -240,6 +304,17 @@ RSpec.describe ProcessReferencesService, type: :service do end end + context 'when remove quote' do + let(:text) { "QT #{target_status_uri}" } + let(:new_text) { 'Hello' } + + it 'post status' do + expect(subject.size).to eq 0 + expect(status.quote).to be_nil + expect(notify?).to be false + end + end + context 'when change reference' do let(:text) { "BT #{target_status_uri}" } let(:new_text) { "BT #{target_status2_uri}" } @@ -250,5 +325,43 @@ RSpec.describe ProcessReferencesService, type: :service do expect(notify?(target_status2.id)).to be true end end + + context 'when change quote' do + let(:text) { "QT #{target_status_uri}" } + let(:new_text) { "QT #{target_status2_uri}" } + + it 'post status' do + expect(subject.size).to eq 1 + expect(subject).to include target_status2.id + expect(status.quote).to_not be_nil + expect(status.quote.id).to eq target_status2.id + expect(notify?(target_status2.id)).to be true + end + end + + context 'when change quote to reference', pending: 'Will fix later' do + let(:text) { "QT #{target_status_uri}" } + let(:new_text) { "RT #{target_status_uri}" } + + it 'post status' do + expect(subject.size).to eq 1 + expect(subject).to include target_status.id + expect(status.quote).to be_nil + expect(notify?(target_status.id)).to be true + end + end + + context 'when change reference to quote', pending: 'Will fix later' do + let(:text) { "RT #{target_status_uri}" } + let(:new_text) { "QT #{target_status_uri}" } + + it 'post status' do + expect(subject.size).to eq 1 + expect(subject).to include target_status.id + expect(status.quote).to_not be_nil + expect(status.quote.id).to eq target_status.id + expect(notify?(target_status.id)).to be true + end + end end end diff --git a/spec/services/report_service_spec.rb b/spec/services/report_service_spec.rb index 616368bf48..d3bcd5d31c 100644 --- a/spec/services/report_service_spec.rb +++ b/spec/services/report_service_spec.rb @@ -36,7 +36,7 @@ RSpec.describe ReportService, type: :service do expect(report.uri).to_not be_nil end - context 'when reporting a reply' do + context 'when reporting a reply on a different remote server' do let(:remote_thread_account) { Fabricate(:account, domain: 'foo.com', protocol: :activitypub, inbox_url: 'http://foo.com/inbox') } let(:reported_status) { Fabricate(:status, account: remote_account, thread: Fabricate(:status, account: remote_thread_account)) } @@ -67,6 +67,25 @@ RSpec.describe ReportService, type: :service do end end end + + context 'when reporting a reply on the same remote server as the person being replied-to' do + let(:remote_thread_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') } + let(:reported_status) { Fabricate(:status, account: remote_account, thread: Fabricate(:status, account: remote_thread_account)) } + + context 'when forward_to_domains includes both the replied-to domain and the origin domain' do + it 'sends ActivityPub payload only once' do + subject.call(source_account, remote_account, status_ids: [reported_status.id], forward: forward, forward_to_domains: [remote_account.domain]) + expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once + end + end + + context 'when forward_to_domains does not include the replied-to domain' do + it 'sends ActivityPub payload only once' do + subject.call(source_account, remote_account, status_ids: [reported_status.id], forward: forward) + expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once + end + end + end end context 'when forward is false' do diff --git a/streaming/index.js b/streaming/index.js index e164298149..c1569b1fb7 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -864,7 +864,7 @@ const startServer = async () => { } if (!payload.filtered && !req.cachedFilters) { - queries.push(client.query('SELECT filter.id AS id, filter.phrase AS title, filter.context AS context, filter.expires_at AS expires_at, filter.action AS filter_action, keyword.keyword AS keyword, keyword.whole_word AS whole_word, filter.exclude_follows AS exclude_follows, filter.exclude_localusers AS exclude_localusers FROM custom_filter_keywords keyword JOIN custom_filters filter ON keyword.custom_filter_id = filter.id WHERE filter.account_id = $1 AND (filter.expires_at IS NULL OR filter.expires_at > NOW())', [req.accountId])); + queries.push(client.query('SELECT filter.id AS id, filter.phrase AS title, filter.context AS context, filter.expires_at AS expires_at, filter.action AS filter_action, filter.with_quote AS with_quote, keyword.keyword AS keyword, keyword.whole_word AS whole_word, filter.exclude_follows AS exclude_follows, filter.exclude_localusers AS exclude_localusers FROM custom_filter_keywords keyword JOIN custom_filters filter ON keyword.custom_filter_id = filter.id WHERE filter.account_id = $1 AND (filter.expires_at IS NULL OR filter.expires_at > NOW())', [req.accountId])); } if (!payload.filtered) { queries.push(client.query(`SELECT 1 @@ -913,7 +913,8 @@ const startServer = async () => { // representing a value in an enum defined by Ruby on Rails: // // enum { warn: 0, hide: 1 } - filter_action: ['warn', 'hide'][filter.filter_action], + filter_action: ['warn', 'hide', 'half_warn'][filter.filter_action], + with_quote: filter.with_quote, excludeFollows: filter.exclude_follows, excludeLocalusers: filter.exclude_localusers, }, diff --git a/yarn.lock b/yarn.lock index 7f72a6d0cd..80f1d7b76e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1096,13 +1096,20 @@ dependencies: regenerator-runtime "^0.12.0" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.22.3", "@babel/runtime@^7.22.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.22.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.15.tgz#38f46494ccf6cf020bd4eed7124b425e83e523b8" integrity sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA== dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.12.1", "@babel/runtime@^7.22.5", "@babel/runtime@^7.9.2": + version "7.23.1" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.1.tgz#72741dc4d413338a91dcb044a86f3c0bc402646d" + integrity sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.22.15", "@babel/template@^7.22.5": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" @@ -1298,7 +1305,12 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": +"@eslint-community/regexpp@^4.5.1": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.9.0.tgz#7ccb5f58703fa61ffdcbf39e2c604a109e781162" + integrity sha512-zJmuCWj2VLBt4c25CfBIbMZLGLyhkvs7LznyVX5HfpzeocThgIj5XQK4L+g3U36mMcx8bPMhGyPpwCATamC4jQ== + +"@eslint-community/regexpp@^4.6.1": version "4.8.1" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.8.1.tgz#8c4bb756cc2aa7eaf13cfa5e69c83afb3260c20c" integrity sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ== @@ -1752,9 +1764,9 @@ "@jridgewell/sourcemap-codec" "^1.4.14" "@material-design-icons/svg@^0.14.10": - version "0.14.12" - resolved "https://registry.yarnpkg.com/@material-design-icons/svg/-/svg-0.14.12.tgz#b3dd27b4c2a93e0310f51acfb311846b0212f987" - integrity sha512-hVEMICFvG26SKDXatPmz+vY5BAqLPCDiyXnw+KN46FXOtY4PcpeAfzFZvwt6D9ywNnVJd4EvmLdlWgLmtOWxbA== + version "0.14.13" + resolved "https://registry.yarnpkg.com/@material-design-icons/svg/-/svg-0.14.13.tgz#de5a79038cf8b281f4b47d79c07399b2b92fcfb7" + integrity sha512-nCExGZOtoLoFeeqShEOM4XA9DkkLzLlQdk/ZxHxps0//dz6e1Lw3fvQbZ2X/+0Dz2O+udiEukfZ4Nd4KpHg8aA== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1826,9 +1838,9 @@ integrity sha512-tOQQBVH8LsUpGXqDnk+kaOGVsgZ8maHAhEiw3Git3p88q+c0Slgu47HuDnL6sVxeCfz24zbq7dOjsVYDiTpDIA== "@reduxjs/toolkit@^1.9.5": - version "1.9.6" - resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.9.6.tgz#fc968b45fe5b17ff90932c4556960d9c1078365a" - integrity sha512-Gc4ikl90ORF4viIdAkY06JNUnODjKfGxZRwATM30EdHq8hLSVoSrwXne5dd739yenP5bJxAX7tLuOWK5RPGtrw== + version "1.9.7" + resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.9.7.tgz#7fc07c0b0ebec52043f8cb43510cf346405f78a6" + integrity sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ== dependencies: immer "^9.0.21" redux "^4.2.1" @@ -2318,9 +2330,9 @@ integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== "@types/node@*": - version "20.6.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.6.5.tgz#4c6a79adf59a8e8193ac87a0e522605b16587258" - integrity sha512-2qGq5LAOTh9izcc0+F+dToFigBWiK1phKPt7rNhOqJSr35y8rlIBjDwGtFSgAI6MGIhjwOVNSQZVdJsZJ2uR1w== + version "20.7.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.7.1.tgz#06d732ead0bd5ad978ef0ea9cbdeb24dc8717514" + integrity sha512-LT+OIXpp2kj4E2S/p91BMe+VgGX2+lfO+XTpfXhh+bCk2LkQtHZSub8ewFBMGP5ClysPjTDFa4sMI8Q3n4T0wg== "@types/node@14 || 16 || 17": version "17.0.45" @@ -2362,9 +2374,9 @@ integrity sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g== "@types/prop-types@*", "@types/prop-types@^15.7.5": - version "15.7.7" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.7.tgz#f9361f7b87fd5d8188b2c998db0a1f47e9fb391a" - integrity sha512-FbtmBWCcSa2J4zL781Zf1p5YUBXQomPEcep9QZCfRfQgTxz3pJWiDFLebohZ9fFntX5ibzOkSsrJ0TEew8cAog== + version "15.7.8" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.8.tgz#805eae6e8f41bd19e88917d2ea200dc992f405d3" + integrity sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ== "@types/punycode@^2.1.0": version "2.1.0" @@ -2382,36 +2394,36 @@ integrity sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg== "@types/range-parser@*": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" - integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + version "1.2.5" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.5.tgz#38bd1733ae299620771bd414837ade2e57757498" + integrity sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA== "@types/react-dom@^18.0.0", "@types/react-dom@^18.2.4": - version "18.2.7" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.7.tgz#67222a08c0a6ae0a0da33c3532348277c70abb63" - integrity sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA== + version "18.2.8" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.8.tgz#338f1b0a646c9f10e0a97208c1d26b9f473dffd6" + integrity sha512-bAIvO5lN/U8sPGvs1Xm61rlRHHaq5rp5N3kp9C+NJ/Q41P8iqjkXSu0+/qu8POsjH9pNWb0OYabFez7taP7omw== dependencies: "@types/react" "*" "@types/react-helmet@^6.1.6": - version "6.1.6" - resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-6.1.6.tgz#7d1afd8cbf099616894e8240e9ef70e3c6d7506d" - integrity sha512-ZKcoOdW/Tg+kiUbkFCBtvDw0k3nD4HJ/h/B9yWxN4uDO8OkRksWTO+EL+z/Qu3aHTeTll3Ro0Cc/8UhwBCMG5A== + version "6.1.7" + resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-6.1.7.tgz#4cecc03165084727408d29d92d8fdd4a7e267403" + integrity sha512-mUFOrdR3AIvHE8BEaqzfPEnR62xq5PHQJehhgNtj78x0d5NOxUCQ0j+r9OZ4RvB+prNZx9wvQnVW8ApFBX+fig== dependencies: "@types/react" "*" "@types/react-immutable-proptypes@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@types/react-immutable-proptypes/-/react-immutable-proptypes-2.1.0.tgz#c045fb48ba28c34c9d759abc3a51a04b5321b77a" - integrity sha512-NRH4W4mgymzyM2gnAG+i2VoOdWIBOQlJlSyAgnFiBTdJ0l8IVeyCtdWP8g6Lra59sUBj2XUO/+DkfmrRAxj6UA== + version "2.1.1" + resolved "https://registry.yarnpkg.com/@types/react-immutable-proptypes/-/react-immutable-proptypes-2.1.1.tgz#9327e09b07ea61c95a8c99869a5e9f0fc1690cc4" + integrity sha512-PiVos0qCotGqI+J0kOeFhbeg7zK8StPMTSDGaTtgYkX6UPjuVVS7lJMedyt4kAMFbM/2QE0bbP5jh22WqtspQg== dependencies: "@types/prop-types" "*" immutable "^3.8.2" -"@types/react-motion@^0.0.34": - version "0.0.34" - resolved "https://registry.yarnpkg.com/@types/react-motion/-/react-motion-0.0.34.tgz#789ff2063e2f7fbb6085d291135c442e8b35291a" - integrity sha512-/rFI22Vg4Xzb47hXtS06WkzUGRu+Vb3yDleuxiqzGj0JbXYXQUCgwSa2ZU12K7ubKi4C8xsdIN3xt4Z4fjSdPw== +"@types/react-motion@^0.0.35": + version "0.0.35" + resolved "https://registry.yarnpkg.com/@types/react-motion/-/react-motion-0.0.35.tgz#63002d85791dc9c7c212044c7b89dfff99cd0b51" + integrity sha512-7e0rlyG4wgvL1F5G8FMpgEYgPF54i7tmOy3b9KSp6kPjU2hEAV6BiRrl3qMPGrJXZ9soH/OlUYx1Ae5C9AJLGA== dependencies: "@types/react" "*" @@ -2447,23 +2459,23 @@ react-select "*" "@types/react-sparklines@^1.7.2": - version "1.7.2" - resolved "https://registry.yarnpkg.com/@types/react-sparklines/-/react-sparklines-1.7.2.tgz#c14e80623abd3669a10f18d13f6fb9fbdc322f70" - integrity sha512-N1GwO7Ri5C5fE8+CxhiDntuSw1qYdGytBuedKrCxWpaojXm4WnfygbdBdc5sXGX7feMxDXBy9MNhxoUTwrMl4A== + version "1.7.3" + resolved "https://registry.yarnpkg.com/@types/react-sparklines/-/react-sparklines-1.7.3.tgz#cdcbeea734bae191011d8f42ef20d794e55b9064" + integrity sha512-fU88CytKUp/aT8CsaWCNOgcvto5OoINj717z9l0a6OiLiz4EhojRW3c6dSwawM9VjKOIJZMXobSRbbJt7MCtsw== dependencies: "@types/react" "*" "@types/react-swipeable-views@^0.13.1": - version "0.13.2" - resolved "https://registry.yarnpkg.com/@types/react-swipeable-views/-/react-swipeable-views-0.13.2.tgz#c37cc8978ae60ab0dff209ef3eb1f77185aef330" - integrity sha512-FiszBm9M0JicAgzO/IwDqpfHQRUEjPZA88UexYsVD6qHJBf5LrbGjR5Mw4+yZbf8ZxJneNqOsZbe4WGjOYG7iQ== + version "0.13.3" + resolved "https://registry.yarnpkg.com/@types/react-swipeable-views/-/react-swipeable-views-0.13.3.tgz#a4c545b7b722c2622806e5853a96ec5826b0bafb" + integrity sha512-gVAQb5AxZTSLVTrJ/Fxwsk0axdBqGzXC8NxAD8MNwEf+qZynsb+15KL9TpNCaGGk4SCE2iyU/JNi6nGNB61AyA== dependencies: "@types/react" "*" "@types/react-test-renderer@^18.0.0": - version "18.0.2" - resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.2.tgz#44243977eec18ab8cda88d8977437f47a0d3fdbe" - integrity sha512-tJzMn+9GHDrdrLe0O4rwJELDfTrmdJbCn/UdYyzjlnPiXYXDl5FBNzdw4PVk2R3hJvSHKFjZcRgvZc12lV0p5Q== + version "18.0.3" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.3.tgz#67922bf5e5f0096581b1efd67dcdeabdd400cfea" + integrity sha512-4wcNLnY6nIT+L6g94CpzL4CXX2P18JvKPU9CDlaHr3DnbP3GiaQLhDotJqjWlVqOcE4UhLRjp0MtxqwuNKONnA== dependencies: "@types/react" "*" @@ -2489,18 +2501,18 @@ "@types/react" "*" "@types/react@*", "@types/react@16 || 17 || 18", "@types/react@>=16.9.11", "@types/react@^18.0.26", "@types/react@^18.2.7": - version "18.2.22" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.22.tgz#abe778a1c95a07fa70df40a52d7300a40b949ccb" - integrity sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA== + version "18.2.24" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.24.tgz#3c7d68c02e0205a472f04abe4a0c1df35d995c05" + integrity sha512-Ee0Jt4sbJxMu1iDcetZEIKQr99J1Zfb6D4F3qfUWoR1JpInkY1Wdg4WwCyBjL257D0+jGqSl1twBjV8iCaC0Aw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" csstype "^3.0.2" "@types/redux-immutable@^4.0.3": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@types/redux-immutable/-/redux-immutable-4.0.3.tgz#db92a281aa9a55a7b63bc1f20a233790305a1f06" - integrity sha512-wXUApt9ib9MGUqoHUMbQmQhqCkvykMHBW3z/P7DISMigFGpGRQ0kkbv7we0XNiv5sYEtEiZzNCEDm+W6ei04DA== + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/redux-immutable/-/redux-immutable-4.0.4.tgz#091641dea366ceed4f8b228a08a0de5f380b9bfd" + integrity sha512-qPFra/qd6HX7+bxayxwM9bsGdMoF7QhTGW/ZJFiaoBZVDZEnQEQCrDhsbSW8Xpuihe6xJ0TRHG3/WTvQpHLGaQ== dependencies: immutable "^4.0.0-rc.1" redux "^4.0.0" @@ -2518,14 +2530,14 @@ "@types/node" "*" "@types/scheduler@*": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5" - integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ== + version "0.16.4" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.4.tgz#fedc3e5b15c26dc18faae96bf1317487cb3658cf" + integrity sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ== "@types/semver@^7.5.0": - version "7.5.2" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.2.tgz#31f6eec1ed7ec23f4f05608d3a2d381df041f564" - integrity sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw== + version "7.5.3" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.3.tgz#9a726e116beb26c24f1ccd6850201e1246122e04" + integrity sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw== "@types/send@*": version "0.17.1" @@ -2545,9 +2557,9 @@ "@types/node" "*" "@types/source-list-map@*": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" - integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== + version "0.1.3" + resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.3.tgz#077e15c87fe06520e30396a533bd9848e735ce9b" + integrity sha512-I9R/7fUjzUOyDy6AFkehCK711wWoAXEaBi80AfjZt1lIkbe6AcXKd3ckQc3liMvQExWvfOeh/8CtKzrfUFN5gA== "@types/stack-utils@^2.0.0": version "2.0.1" @@ -2555,9 +2567,9 @@ integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== "@types/tapable@^1": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310" - integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== + version "1.0.9" + resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.9.tgz#1481a4236267dd2d0ca2a637adb90f0ccb3d69c4" + integrity sha512-fOHIwZua0sRltqWzODGUM6b4ffZrf/vzGUmNXdR+4DzuJP42PMbM5dLKcdzlYvv8bMJ3GALOzkk1q7cDm2zPyA== "@types/tough-cookie@*": version "4.0.2" @@ -2570,9 +2582,9 @@ integrity sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g== "@types/uglify-js@*": - version "3.17.1" - resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.17.1.tgz#e0ffcef756476410e5bce2cb01384ed878a195b5" - integrity sha512-GkewRA4i5oXacU/n4MA9+bLgt5/L3F1mKrYvFGm7r2ouLXhRKjuWwo9XHNnbx6WF3vlGW21S3fCvgqxvxXXc5g== + version "3.17.2" + resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.17.2.tgz#a2ba86fd524f6281a7655463338c546f845b29c3" + integrity sha512-9SjrHO54LINgC/6Ehr81NjAxAYvwEZqjUHLjJYvC4Nmr9jbLQCIZbWSvl4vXQkkmR1UAuaKDycau3O1kWGFyXQ== dependencies: source-map "^0.6.1" @@ -2592,18 +2604,18 @@ integrity sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA== "@types/webpack-sources@*": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-3.2.0.tgz#16d759ba096c289034b26553d2df1bf45248d38b" - integrity sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg== + version "3.2.1" + resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-3.2.1.tgz#98670b35fa799c44ac235910f3fda9bfdcdbc2c6" + integrity sha512-iLC3Fsx62ejm3ST3PQ8vBMC54Rb3EoCprZjeJGI5q+9QjfDLGt9jeg/k245qz1G9AQnORGk0vqPicJFPT1QODQ== dependencies: "@types/node" "*" "@types/source-list-map" "*" source-map "^0.7.3" "@types/webpack@^4.41.33": - version "4.41.33" - resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.33.tgz#16164845a5be6a306bcbe554a8e67f9cac215ffc" - integrity sha512-PPajH64Ft2vWevkerISMtnZ8rTs4YmRbs+23c402J0INmxDKCrhZNvwZYtzx96gY2wAtXdrK1BS2fiC8MlLr3g== + version "4.41.34" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.34.tgz#8cf616be84b39c8fb26f9459d4470a5514df2477" + integrity sha512-CN2aOGrR3zbMc2v+cKqzaClYP1ldkpPOgtdNvgX+RmlWCSWxHxpzz6WSCVQZRkF8D60ROlkRzAoEpgjWQ+bd2g== dependencies: "@types/node" "*" "@types/tapable" "^1" @@ -2618,22 +2630,22 @@ integrity sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ== "@types/yargs@^17.0.24", "@types/yargs@^17.0.8": - version "17.0.25" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.25.tgz#3edd102803c97356fb4c805b2bbaf7dfc9ab6abc" - integrity sha512-gy7iPgwnzNvxgAEi2bXOHWCVOG6f7xsprVJH4MjlAWeBmJ7vh/Y1kwMtUrs64ztf24zVIRCpr3n/z6gm9QIkgg== + version "17.0.26" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.26.tgz#388e5002a8b284ad7b4599ba89920a6d74d8d79a" + integrity sha512-Y3vDy2X6zw/ZCumcwLpdhM5L7jmyGpmBCTYMHDLqT2IKVMYRRLdv6ZakA+wxhra6Z/3bwhNbNl9bDGXaFU+6rw== dependencies: "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^6.0.0": - version "6.7.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.2.tgz#f18cc75c9cceac8080a9dc2e7d166008c5207b9f" - integrity sha512-ooaHxlmSgZTM6CHYAFRlifqh1OAr3PAQEwi7lhYhaegbnXrnh7CDcHmc3+ihhbQC7H0i4JF0psI5ehzkF6Yl6Q== + version "6.7.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.3.tgz#d98046e9f7102d49a93d944d413c6055c47fafd7" + integrity sha512-vntq452UHNltxsaaN+L9WyuMch8bMd9CqJ3zhzTPXXidwbf5mqqKCVXEuvRZUqLJSTLeWE65lQwyXsRGnXkCTA== dependencies: "@eslint-community/regexpp" "^4.5.1" - "@typescript-eslint/scope-manager" "6.7.2" - "@typescript-eslint/type-utils" "6.7.2" - "@typescript-eslint/utils" "6.7.2" - "@typescript-eslint/visitor-keys" "6.7.2" + "@typescript-eslint/scope-manager" "6.7.3" + "@typescript-eslint/type-utils" "6.7.3" + "@typescript-eslint/utils" "6.7.3" + "@typescript-eslint/visitor-keys" "6.7.3" debug "^4.3.4" graphemer "^1.4.0" ignore "^5.2.4" @@ -2642,31 +2654,31 @@ ts-api-utils "^1.0.1" "@typescript-eslint/parser@^6.0.0": - version "6.7.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.7.2.tgz#e0ae93771441b9518e67d0660c79e3a105497af4" - integrity sha512-KA3E4ox0ws+SPyxQf9iSI25R6b4Ne78ORhNHeVKrPQnoYsb9UhieoiRoJgrzgEeKGOXhcY1i8YtOeCHHTDa6Fw== + version "6.7.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.7.3.tgz#aaf40092a32877439e5957e18f2d6a91c82cc2fd" + integrity sha512-TlutE+iep2o7R8Lf+yoer3zU6/0EAUc8QIBB3GYBc1KGz4c4TRm83xwXUZVPlZ6YCLss4r77jbu6j3sendJoiQ== dependencies: - "@typescript-eslint/scope-manager" "6.7.2" - "@typescript-eslint/types" "6.7.2" - "@typescript-eslint/typescript-estree" "6.7.2" - "@typescript-eslint/visitor-keys" "6.7.2" + "@typescript-eslint/scope-manager" "6.7.3" + "@typescript-eslint/types" "6.7.3" + "@typescript-eslint/typescript-estree" "6.7.3" + "@typescript-eslint/visitor-keys" "6.7.3" debug "^4.3.4" -"@typescript-eslint/scope-manager@6.7.2": - version "6.7.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.7.2.tgz#cf59a2095d2f894770c94be489648ad1c78dc689" - integrity sha512-bgi6plgyZjEqapr7u2mhxGR6E8WCzKNUFWNh6fkpVe9+yzRZeYtDTbsIBzKbcxI+r1qVWt6VIoMSNZ4r2A+6Yw== +"@typescript-eslint/scope-manager@6.7.3": + version "6.7.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.7.3.tgz#07e5709c9bdae3eaf216947433ef97b3b8b7d755" + integrity sha512-wOlo0QnEou9cHO2TdkJmzF7DFGvAKEnB82PuPNHpT8ZKKaZu6Bm63ugOTn9fXNJtvuDPanBc78lGUGGytJoVzQ== dependencies: - "@typescript-eslint/types" "6.7.2" - "@typescript-eslint/visitor-keys" "6.7.2" + "@typescript-eslint/types" "6.7.3" + "@typescript-eslint/visitor-keys" "6.7.3" -"@typescript-eslint/type-utils@6.7.2": - version "6.7.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.7.2.tgz#ed921c9db87d72fa2939fee242d700561454f367" - integrity sha512-36F4fOYIROYRl0qj95dYKx6kybddLtsbmPIYNK0OBeXv2j9L5nZ17j9jmfy+bIDHKQgn2EZX+cofsqi8NPATBQ== +"@typescript-eslint/type-utils@6.7.3": + version "6.7.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.7.3.tgz#c2c165c135dda68a5e70074ade183f5ad68f3400" + integrity sha512-Fc68K0aTDrKIBvLnKTZ5Pf3MXK495YErrbHb1R6aTpfK5OdSFj0rVN7ib6Tx6ePrZ2gsjLqr0s98NG7l96KSQw== dependencies: - "@typescript-eslint/typescript-estree" "6.7.2" - "@typescript-eslint/utils" "6.7.2" + "@typescript-eslint/typescript-estree" "6.7.3" + "@typescript-eslint/utils" "6.7.3" debug "^4.3.4" ts-api-utils "^1.0.1" @@ -2675,10 +2687,10 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== -"@typescript-eslint/types@6.7.2": - version "6.7.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.7.2.tgz#75a615a6dbeca09cafd102fe7f465da1d8a3c066" - integrity sha512-flJYwMYgnUNDAN9/GAI3l8+wTmvTYdv64fcH8aoJK76Y+1FCZ08RtI5zDerM/FYT5DMkAc+19E4aLmd5KqdFyg== +"@typescript-eslint/types@6.7.3": + version "6.7.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.7.3.tgz#0402b5628a63f24f2dc9d4a678e9a92cc50ea3e9" + integrity sha512-4g+de6roB2NFcfkZb439tigpAMnvEIg3rIjWQ+EM7IBaYt/CdJt6em9BJ4h4UpdgaBWdmx2iWsafHTrqmgIPNw== "@typescript-eslint/typescript-estree@5.62.0": version "5.62.0" @@ -2693,30 +2705,30 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@6.7.2": - version "6.7.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.2.tgz#ce5883c23b581a5caf878af641e49dd0349238c7" - integrity sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ== +"@typescript-eslint/typescript-estree@6.7.3": + version "6.7.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.3.tgz#ec5bb7ab4d3566818abaf0e4a8fa1958561b7279" + integrity sha512-YLQ3tJoS4VxLFYHTw21oe1/vIZPRqAO91z6Uv0Ss2BKm/Ag7/RVQBcXTGcXhgJMdA4U+HrKuY5gWlJlvoaKZ5g== dependencies: - "@typescript-eslint/types" "6.7.2" - "@typescript-eslint/visitor-keys" "6.7.2" + "@typescript-eslint/types" "6.7.3" + "@typescript-eslint/visitor-keys" "6.7.3" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/utils@6.7.2": - version "6.7.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.7.2.tgz#b9ef0da6f04932167a9222cb4ac59cb187165ebf" - integrity sha512-ZCcBJug/TS6fXRTsoTkgnsvyWSiXwMNiPzBUani7hDidBdj1779qwM1FIAmpH4lvlOZNF3EScsxxuGifjpLSWQ== +"@typescript-eslint/utils@6.7.3": + version "6.7.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.7.3.tgz#96c655816c373135b07282d67407cb577f62e143" + integrity sha512-vzLkVder21GpWRrmSR9JxGZ5+ibIUSudXlW52qeKpzUEQhRSmyZiVDDj3crAth7+5tmN1ulvgKaCU2f/bPRCzg== dependencies: "@eslint-community/eslint-utils" "^4.4.0" "@types/json-schema" "^7.0.12" "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "6.7.2" - "@typescript-eslint/types" "6.7.2" - "@typescript-eslint/typescript-estree" "6.7.2" + "@typescript-eslint/scope-manager" "6.7.3" + "@typescript-eslint/types" "6.7.3" + "@typescript-eslint/typescript-estree" "6.7.3" semver "^7.5.4" "@typescript-eslint/visitor-keys@5.62.0": @@ -2727,12 +2739,12 @@ "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" -"@typescript-eslint/visitor-keys@6.7.2": - version "6.7.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.2.tgz#4cb2bd786f1f459731b0ad1584c9f73e1c7a4d5c" - integrity sha512-uVw9VIMFBUTz8rIeaUT3fFe8xIUx8r4ywAdlQv1ifH+6acn/XF8Y6rwJ7XNmkNMDrTW+7+vxFFPIF40nJCVsMQ== +"@typescript-eslint/visitor-keys@6.7.3": + version "6.7.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.3.tgz#83809631ca12909bd2083558d2f93f5747deebb2" + integrity sha512-HEVXkU9IB+nk9o63CeICMHxFWbHWr3E1mpilIQBe9+7L/lH97rleFLVtYsfnWB+JVMaiFnEaxvknvmIzX+CqVg== dependencies: - "@typescript-eslint/types" "6.7.2" + "@typescript-eslint/types" "6.7.3" eslint-visitor-keys "^3.4.1" "@webassemblyjs/ast@1.9.0": @@ -3364,9 +3376,9 @@ axe-core@^4.6.2: integrity sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g== axios@^1.4.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.5.0.tgz#f02e4af823e2e46a9768cfc74691fdd0517ea267" - integrity sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ== + version "1.5.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.5.1.tgz#11fbaa11fc35f431193a9564109c88c1f27b585f" + integrity sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" @@ -4351,9 +4363,9 @@ core-js@^2.5.0: integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== core-js@^3.30.2: - version "3.32.2" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.32.2.tgz#172fb5949ef468f93b4be7841af6ab1f21992db7" - integrity sha512-pxXSw1mYZPDGvTQqEc5vgIb83jGQKFGYWY76z4a7weZXUolw3G+OvpZqSRcfYOoOVUQJYEPsWeQK8pKEnUtWxQ== + version "3.33.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.33.0.tgz#70366dbf737134761edb017990cf5ce6c6369c40" + integrity sha512-HoZr92+ZjFEKar5HS6MC776gYslNOKHt75mEBKWKnPeFDpZ6nH5OeF3S6HFT1mUAUZKrzkez05VboaX8myjSuw== core-util-is@~1.0.0: version "1.0.3" @@ -6022,11 +6034,16 @@ flatted@^3.2.7: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== -follow-redirects@^1.0.0, follow-redirects@^1.15.0: +follow-redirects@^1.0.0: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== +follow-redirects@^1.15.0: + version "1.15.3" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" + integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== + font-awesome@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" @@ -6247,9 +6264,9 @@ glob-parent@^6.0.2: is-glob "^4.0.3" glob@^10.2.5, glob@^10.2.6: - version "10.3.9" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.9.tgz#181ae87640ecce9b2fc5b96e4e2d70b7c3629ab8" - integrity sha512-2tU/LKevAQvDVuVJ9pg9Yv9xcbSh+TqHuTaXTNbQwf+0kDl9Fm6bMovi4Nm5c8TVvfxo2LLcqCGtmO9KoJaGWg== + version "10.3.10" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b" + integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== dependencies: foreground-child "^3.1.0" jackspeak "^2.3.5" @@ -9738,9 +9755,9 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss@^8.2.15, postcss@^8.4.24, postcss@^8.4.25: - version "8.4.30" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.30.tgz#0e0648d551a606ef2192a26da4cabafcc09c1aa7" - integrity sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g== + version "8.4.31" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" + integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== dependencies: nanoid "^3.3.6" picocolors "^1.0.0" @@ -10152,9 +10169,9 @@ react-redux-loading-bar@^5.0.4: react-lifecycles-compat "^3.0.4" react-redux@^8.0.4, react-redux@^8.1.1: - version "8.1.2" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.1.2.tgz#9076bbc6b60f746659ad6d51cb05de9c5e1e9188" - integrity sha512-xJKYI189VwfsFc4CJvHqHlDrzyFTY/3vZACbE+rr/zQ34Xx1wQfB4OTOSeOSNrF6BDVe8OOdxIrAnMGXA3ggfw== + version "8.1.3" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.1.3.tgz#4fdc0462d0acb59af29a13c27ffef6f49ab4df46" + integrity sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw== dependencies: "@babel/runtime" "^7.12.1" "@types/hoist-non-react-statics" "^3.3.1" @@ -11486,6 +11503,7 @@ stringz@^2.1.0: char-regex "^1.0.2" "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + name strip-ansi-cjs version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==