From 0d14fcebae0b9a0e5da73049c960064338dfee8e Mon Sep 17 00:00:00 2001 From: Eugen Rochko <eugen@zeonfederated.com> Date: Mon, 13 Nov 2023 10:58:28 +0100 Subject: [PATCH 01/63] Change link previews to keep original URL from the status (#27312) --- app/chewy/public_statuses_index.rb | 2 +- app/chewy/statuses_index.rb | 2 +- .../api/v1/conversations_controller.rb | 2 +- app/models/admin/status_batch_action.rb | 2 +- app/models/concerns/status_search_concern.rb | 2 +- app/models/preview_card.rb | 7 ++++++- app/models/preview_cards_status.rb | 18 ++++++++++++++++++ app/models/status.rb | 15 ++++++++++----- app/models/trends/links.rb | 4 +--- .../rest/preview_card_serializer.rb | 4 ++++ .../process_status_update_service.rb | 2 +- app/services/fetch_link_card_service.rb | 6 +++--- app/services/update_status_service.rb | 2 +- ...183200_add_url_to_preview_cards_statuses.rb | 7 +++++++ db/schema.rb | 3 ++- lib/tasks/tests.rake | 2 +- spec/helpers/media_component_helper_spec.rb | 4 +++- spec/services/fetch_link_card_service_spec.rb | 8 ++++---- spec/services/update_status_service_spec.rb | 8 ++++---- 19 files changed, 70 insertions(+), 30 deletions(-) create mode 100644 app/models/preview_cards_status.rb create mode 100644 db/migrate/20231006183200_add_url_to_preview_cards_statuses.rb diff --git a/app/chewy/public_statuses_index.rb b/app/chewy/public_statuses_index.rb index 4be204d4a..b5f0be5e5 100644 --- a/app/chewy/public_statuses_index.rb +++ b/app/chewy/public_statuses_index.rb @@ -53,7 +53,7 @@ class PublicStatusesIndex < Chewy::Index index_scope ::Status.unscoped .kept .indexable - .includes(:media_attachments, :preloadable_poll, :preview_cards, :tags) + .includes(:media_attachments, :preloadable_poll, :tags, preview_cards_status: :preview_card) root date_detection: false do field(:id, type: 'long') diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index 6b25dc9df..e315a2030 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -50,7 +50,7 @@ class StatusesIndex < Chewy::Index }, } - index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preview_cards, :local_mentioned, :local_favorited, :local_reblogged, :local_bookmarked, :tags, preloadable_poll: :local_voters), delete_if: ->(status) { status.searchable_by.empty? } + index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :local_mentioned, :local_favorited, :local_reblogged, :local_bookmarked, :tags, preview_cards_status: :preview_card, preloadable_poll: :local_voters), delete_if: ->(status) { status.searchable_by.empty? } root date_detection: false do field(:id, type: 'long') diff --git a/app/controllers/api/v1/conversations_controller.rb b/app/controllers/api/v1/conversations_controller.rb index b3ca2f790..6a3567e62 100644 --- a/app/controllers/api/v1/conversations_controller.rb +++ b/app/controllers/api/v1/conversations_controller.rb @@ -41,10 +41,10 @@ class Api::V1::ConversationsController < Api::BaseController account: :account_stat, last_status: [ :media_attachments, - :preview_cards, :status_stat, :tags, { + preview_cards_status: :preview_card, active_mentions: [account: :account_stat], account: :account_stat, }, diff --git a/app/models/admin/status_batch_action.rb b/app/models/admin/status_batch_action.rb index 24c3979aa..8a8e2fa37 100644 --- a/app/models/admin/status_batch_action.rb +++ b/app/models/admin/status_batch_action.rb @@ -74,7 +74,7 @@ class Admin::StatusBatchAction # Can't use a transaction here because UpdateStatusService queues # Sidekiq jobs - statuses.includes(:media_attachments, :preview_cards).find_each do |status| + statuses.includes(:media_attachments, preview_cards_status: :preview_card).find_each do |status| next if status.discarded? || !(status.with_media? || status.with_preview_card?) authorize([:admin, status], :update?) diff --git a/app/models/concerns/status_search_concern.rb b/app/models/concerns/status_search_concern.rb index 3ef45754a..7252fde73 100644 --- a/app/models/concerns/status_search_concern.rb +++ b/app/models/concerns/status_search_concern.rb @@ -40,7 +40,7 @@ module StatusSearchConcern properties << 'media' if with_media? properties << 'poll' if with_poll? properties << 'link' if with_preview_card? - properties << 'embed' if preview_cards.any?(&:video?) + properties << 'embed' if preview_card&.video? properties << 'sensitive' if sensitive? properties << 'reply' if reply? end diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb index a1751c426..837592743 100644 --- a/app/models/preview_card.rb +++ b/app/models/preview_card.rb @@ -50,7 +50,9 @@ class PreviewCard < ApplicationRecord enum type: { link: 0, photo: 1, video: 2, rich: 3 } enum link_type: { unknown: 0, article: 1 } - has_and_belongs_to_many :statuses + has_many :preview_cards_statuses, dependent: :delete_all, inverse_of: :preview_card + has_many :statuses, through: :preview_cards_statuses + has_one :trend, class_name: 'PreviewCardTrend', inverse_of: :preview_card, dependent: :destroy has_attached_file :image, processors: [:thumbnail, :blurhash_transcoder], styles: ->(f) { image_styles(f) }, convert_options: { all: '-quality 90 +profile "!icc,*" +set date:modify +set date:create +set date:timestamp' }, validate_media_type: false @@ -64,6 +66,9 @@ class PreviewCard < ApplicationRecord before_save :extract_dimensions, if: :link? + # This can be set by the status when retrieving the preview card using the join model + attr_accessor :original_url + def appropriate_for_trends? link? && article? && title.present? && description.present? && image.present? && provider_name.present? end diff --git a/app/models/preview_cards_status.rb b/app/models/preview_cards_status.rb new file mode 100644 index 000000000..341771e4d --- /dev/null +++ b/app/models/preview_cards_status.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: preview_cards_statuses +# +# preview_card_id :bigint(8) not null +# status_id :bigint(8) not null +# url :string +# +class PreviewCardsStatus < ApplicationRecord + # Composite primary keys are not properly supported in Rails. However, + # we shouldn't need this anyway... + self.primary_key = nil + + belongs_to :preview_card + belongs_to :status +end diff --git a/app/models/status.rb b/app/models/status.rb index 1c41ef1d5..41c895029 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -79,8 +79,8 @@ class Status < ApplicationRecord has_many :local_bookmarked, -> { merge(Account.local) }, through: :bookmarks, source: :account has_and_belongs_to_many :tags - has_and_belongs_to_many :preview_cards + has_one :preview_cards_status, inverse_of: :status # Because of a composite primary key, the dependent option cannot be used has_one :notification, as: :activity, dependent: :destroy has_one :status_stat, inverse_of: :status has_one :poll, inverse_of: :status, dependent: :destroy @@ -142,24 +142,25 @@ class Status < ApplicationRecord # The `prepend: true` option below ensures this runs before # the `dependent: destroy` callbacks remove relevant records before_destroy :unlink_from_conversations!, prepend: true + before_destroy :reset_preview_card! cache_associated :application, :media_attachments, :conversation, :status_stat, :tags, - :preview_cards, :preloadable_poll, + preview_cards_status: [:preview_card], account: [:account_stat, user: :role], active_mentions: { account: :account_stat }, reblog: [ :application, :tags, - :preview_cards, :media_attachments, :conversation, :status_stat, :preloadable_poll, + preview_cards_status: [:preview_card], account: [:account_stat, user: :role], active_mentions: { account: :account_stat }, ], @@ -226,7 +227,11 @@ class Status < ApplicationRecord end def preview_card - preview_cards.first + preview_cards_status&.preview_card&.tap { |x| x.original_url = preview_cards_status.url } + end + + def reset_preview_card! + PreviewCardsStatus.where(status_id: id).delete_all end def hidden? @@ -244,7 +249,7 @@ class Status < ApplicationRecord end def with_preview_card? - preview_cards.any? + preview_cards_status.present? end def with_poll? diff --git a/app/models/trends/links.rb b/app/models/trends/links.rb index fcbdb1a5f..b4eae9f70 100644 --- a/app/models/trends/links.rb +++ b/app/models/trends/links.rb @@ -54,9 +54,7 @@ class Trends::Links < Trends::Base !(original_status.account.silenced? || status.account.silenced?) && !(original_status.spoiler_text? || original_status.sensitive?) - original_status.preview_cards.each do |preview_card| - add(preview_card, status.account_id, at_time) if preview_card.appropriate_for_trends? - end + add(original_status.preview_card, status.account_id, at_time) if original_status.preview_card&.appropriate_for_trends? end def add(preview_card, account_id, at_time = Time.now.utc) diff --git a/app/serializers/rest/preview_card_serializer.rb b/app/serializers/rest/preview_card_serializer.rb index 3e1c4bde3..039262cd5 100644 --- a/app/serializers/rest/preview_card_serializer.rb +++ b/app/serializers/rest/preview_card_serializer.rb @@ -8,6 +8,10 @@ class REST::PreviewCardSerializer < ActiveModel::Serializer :provider_url, :html, :width, :height, :image, :image_description, :embed_url, :blurhash, :published_at + def url + object.original_url.presence || object.url + end + def image object.image? ? full_asset_url(object.image.url(:original)) : nil end diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb index 4ff92da01..2db0e80e7 100644 --- a/app/services/activitypub/process_status_update_service.rb +++ b/app/services/activitypub/process_status_update_service.rb @@ -280,7 +280,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService end def reset_preview_card! - @status.preview_cards.clear + @status.reset_preview_card! LinkCrawlWorker.perform_in(rand(1..59).seconds, @status.id) end diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb index 13775e63c..c6b600dd7 100644 --- a/app/services/fetch_link_card_service.rb +++ b/app/services/fetch_link_card_service.rb @@ -19,7 +19,7 @@ class FetchLinkCardService < BaseService @status = status @original_url = parse_urls - return if @original_url.nil? || @status.preview_cards.any? + return if @original_url.nil? || @status.with_preview_card? @url = @original_url.to_s @@ -62,9 +62,9 @@ class FetchLinkCardService < BaseService def attach_card with_redis_lock("attach_card:#{@status.id}") do - return if @status.preview_cards.any? + return if @status.with_preview_card? - @status.preview_cards << @card + PreviewCardsStatus.create(status: @status, preview_card: @card, url: @original_url) Rails.cache.delete(@status) Trends.links.register(@status) end diff --git a/app/services/update_status_service.rb b/app/services/update_status_service.rb index d1c2b990f..cdfe28365 100644 --- a/app/services/update_status_service.rb +++ b/app/services/update_status_service.rb @@ -123,7 +123,7 @@ class UpdateStatusService < BaseService def reset_preview_card! return unless @status.text_previously_changed? - @status.preview_cards.clear + @status.reset_preview_card! LinkCrawlWorker.perform_async(@status.id) end diff --git a/db/migrate/20231006183200_add_url_to_preview_cards_statuses.rb b/db/migrate/20231006183200_add_url_to_preview_cards_statuses.rb new file mode 100644 index 000000000..f7c6de462 --- /dev/null +++ b/db/migrate/20231006183200_add_url_to_preview_cards_statuses.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddURLToPreviewCardsStatuses < ActiveRecord::Migration[7.0] + def change + add_column :preview_cards_statuses, :url, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 37020c2d7..a0062c8ce 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_07_150100) do +ActiveRecord::Schema[7.0].define(version: 2023_10_06_183200) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -811,6 +811,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_09_07_150100) do create_table "preview_cards_statuses", primary_key: ["status_id", "preview_card_id"], force: :cascade do |t| t.bigint "preview_card_id", null: false t.bigint "status_id", null: false + t.string "url" end create_table "relays", force: :cascade do |t| diff --git a/lib/tasks/tests.rake b/lib/tasks/tests.rake index 7f8e72dd8..209a73efa 100644 --- a/lib/tasks/tests.rake +++ b/lib/tasks/tests.rake @@ -69,7 +69,7 @@ namespace :tests do exit(1) end - unless Status.find(12).preview_cards.pluck(:url) == ['https://joinmastodon.org/'] + unless PreviewCard.where(id: PreviewCardsStatus.where(status_id: 12).select(:preview_card_id)).pluck(:url) == ['https://joinmastodon.org/'] puts 'Preview cards not deduplicated as expected' exit(1) end diff --git a/spec/helpers/media_component_helper_spec.rb b/spec/helpers/media_component_helper_spec.rb index 71a9af6f3..149f6a83a 100644 --- a/spec/helpers/media_component_helper_spec.rb +++ b/spec/helpers/media_component_helper_spec.rb @@ -49,10 +49,12 @@ describe MediaComponentHelper do end describe 'render_card_component' do - let(:status) { Fabricate(:status, preview_cards: [Fabricate(:preview_card)]) } + let(:status) { Fabricate(:status) } let(:result) { helper.render_card_component(status) } before do + PreviewCardsStatus.create(status: status, preview_card: Fabricate(:preview_card)) + without_partial_double_verification do allow(helper).to receive(:current_account).and_return(status.account) end diff --git a/spec/services/fetch_link_card_service_spec.rb b/spec/services/fetch_link_card_service_spec.rb index f44cbb750..d8ca310b2 100644 --- a/spec/services/fetch_link_card_service_spec.rb +++ b/spec/services/fetch_link_card_service_spec.rb @@ -120,7 +120,7 @@ RSpec.describe FetchLinkCardService, type: :service do let(:status) { Fabricate(:status, text: 'Check out http://example.com/sjis') } it 'decodes the HTML' do - expect(status.preview_cards.first.title).to eq('SJISのページ') + expect(status.preview_card.title).to eq('SJISのページ') end end @@ -128,7 +128,7 @@ RSpec.describe FetchLinkCardService, type: :service do let(:status) { Fabricate(:status, text: 'Check out http://example.com/sjis_with_wrong_charset') } it 'decodes the HTML despite the wrong charset header' do - expect(status.preview_cards.first.title).to eq('SJISのページ') + expect(status.preview_card.title).to eq('SJISのページ') end end @@ -136,7 +136,7 @@ RSpec.describe FetchLinkCardService, type: :service do let(:status) { Fabricate(:status, text: 'Check out http://example.com/koi8-r') } it 'decodes the HTML' do - expect(status.preview_cards.first.title).to eq('Московя начинаетъ только въ XVI ст. привлекать внимане иностранцевъ.') + expect(status.preview_card.title).to eq('Московя начинаетъ только въ XVI ст. привлекать внимане иностранцевъ.') end end @@ -144,7 +144,7 @@ RSpec.describe FetchLinkCardService, type: :service do let(:status) { Fabricate(:status, text: 'Check out http://example.com/windows-1251') } it 'decodes the HTML' do - expect(status.preview_cards.first.title).to eq('сэмпл текст') + expect(status.preview_card.title).to eq('сэмпл текст') end end diff --git a/spec/services/update_status_service_spec.rb b/spec/services/update_status_service_spec.rb index 9c53ebb2f..eb38230b0 100644 --- a/spec/services/update_status_service_spec.rb +++ b/spec/services/update_status_service_spec.rb @@ -23,11 +23,11 @@ RSpec.describe UpdateStatusService, type: :service do end context 'when text changes' do - let!(:status) { Fabricate(:status, text: 'Foo') } + let(:status) { Fabricate(:status, text: 'Foo') } let(:preview_card) { Fabricate(:preview_card) } before do - status.preview_cards << preview_card + PreviewCardsStatus.create(status: status, preview_card: preview_card) subject.call(status, status.account_id, text: 'Bar') end @@ -45,11 +45,11 @@ RSpec.describe UpdateStatusService, type: :service do end context 'when content warning changes' do - let!(:status) { Fabricate(:status, text: 'Foo', spoiler_text: '') } + let(:status) { Fabricate(:status, text: 'Foo', spoiler_text: '') } let(:preview_card) { Fabricate(:preview_card) } before do - status.preview_cards << preview_card + PreviewCardsStatus.create(status: status, preview_card: preview_card) subject.call(status, status.account_id, text: 'Foo', spoiler_text: 'Bar') end From da4f37020b6ef15235fd5bcaf3ed7cd2fd15437f Mon Sep 17 00:00:00 2001 From: Renaud Chaput <renchap@gmail.com> Date: Mon, 13 Nov 2023 13:19:41 +0100 Subject: [PATCH 02/63] Fix Jest config (#27834) --- .watchmanconfig | 3 +++ jest.config.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 .watchmanconfig diff --git a/.watchmanconfig b/.watchmanconfig new file mode 100644 index 000000000..29e4f231e --- /dev/null +++ b/.watchmanconfig @@ -0,0 +1,3 @@ +{ + "ignore_dirs": ["node_modules/", "public/"] +} diff --git a/jest.config.js b/jest.config.js index 83eae46cb..b4a34a5ab 100644 --- a/jest.config.js +++ b/jest.config.js @@ -19,7 +19,7 @@ const config = { // Those packages are ESM, so we need them to be processed by Babel transformIgnorePatterns: ['/node_modules/(?!(redent|strip-indent)/)'], coverageDirectory: '<rootDir>/coverage', - moduleDirectories: ['<rootDir>/node_modules', '<rootDir>/app/javascript'], + moduleDirectories: ['node_modules', '<rootDir>/app/javascript'], moduleNameMapper: { '\\.svg$': '<rootDir>/app/javascript/__mocks__/svg.js', }, From 3b989e4d644d2c5e1b191e2740cd700cf9dc291d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 14:13:53 +0100 Subject: [PATCH 03/63] Update dependency rails to v7.1.2 (#27812) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 121 ++++++++++++++++++++++++++------------------------- 1 file changed, 61 insertions(+), 60 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 83693812f..84ad19b80 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -39,50 +39,51 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (7.1.1) - actionpack (= 7.1.1) - activesupport (= 7.1.1) + actioncable (7.1.2) + actionpack (= 7.1.2) + activesupport (= 7.1.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.1.1) - actionpack (= 7.1.1) - activejob (= 7.1.1) - activerecord (= 7.1.1) - activestorage (= 7.1.1) - activesupport (= 7.1.1) + actionmailbox (7.1.2) + actionpack (= 7.1.2) + activejob (= 7.1.2) + activerecord (= 7.1.2) + activestorage (= 7.1.2) + activesupport (= 7.1.2) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.1.1) - actionpack (= 7.1.1) - actionview (= 7.1.1) - activejob (= 7.1.1) - activesupport (= 7.1.1) + actionmailer (7.1.2) + actionpack (= 7.1.2) + actionview (= 7.1.2) + activejob (= 7.1.2) + activesupport (= 7.1.2) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.2) - actionpack (7.1.1) - actionview (= 7.1.1) - activesupport (= 7.1.1) + actionpack (7.1.2) + actionview (= 7.1.2) + activesupport (= 7.1.2) nokogiri (>= 1.8.5) + racc rack (>= 2.2.4) rack-session (>= 1.0.1) rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - actiontext (7.1.1) - actionpack (= 7.1.1) - activerecord (= 7.1.1) - activestorage (= 7.1.1) - activesupport (= 7.1.1) + actiontext (7.1.2) + actionpack (= 7.1.2) + activerecord (= 7.1.2) + activestorage (= 7.1.2) + activesupport (= 7.1.2) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.1.1) - activesupport (= 7.1.1) + actionview (7.1.2) + activesupport (= 7.1.2) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) @@ -92,22 +93,22 @@ GEM activemodel (>= 4.1) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) - activejob (7.1.1) - activesupport (= 7.1.1) + activejob (7.1.2) + activesupport (= 7.1.2) globalid (>= 0.3.6) - activemodel (7.1.1) - activesupport (= 7.1.1) - activerecord (7.1.1) - activemodel (= 7.1.1) - activesupport (= 7.1.1) + activemodel (7.1.2) + activesupport (= 7.1.2) + activerecord (7.1.2) + activemodel (= 7.1.2) + activesupport (= 7.1.2) timeout (>= 0.4.0) - activestorage (7.1.1) - actionpack (= 7.1.1) - activejob (= 7.1.1) - activerecord (= 7.1.1) - activesupport (= 7.1.1) + activestorage (7.1.2) + actionpack (= 7.1.2) + activejob (= 7.1.2) + activerecord (= 7.1.2) + activesupport (= 7.1.2) marcel (~> 1.0) - activesupport (7.1.1) + activesupport (7.1.2) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) @@ -218,7 +219,7 @@ GEM activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) - date (3.3.3) + date (3.3.4) debug_inspector (1.1.0) devise (4.9.3) bcrypt (~> 3.0) @@ -369,7 +370,7 @@ GEM terminal-table (>= 1.5.1) idn-ruby (0.1.5) io-console (0.6.0) - irb (1.8.1) + irb (1.8.3) rdoc reline (>= 0.3.8) jmespath (1.6.2) @@ -462,13 +463,13 @@ GEM uri net-http-persistent (4.0.2) connection_pool (~> 2.2) - net-imap (0.4.1) + net-imap (0.4.4) date net-protocol net-ldap (0.18.0) net-pop (0.1.2) net-protocol - net-protocol (0.2.1) + net-protocol (0.2.2) timeout net-smtp (0.4.0) net-protocol @@ -526,7 +527,7 @@ GEM net-smtp premailer (~> 1.7, >= 1.7.9) private_address_check (0.5.0) - psych (5.1.1) + psych (5.1.1.1) stringio public_suffix (5.0.3) puma (6.4.0) @@ -557,20 +558,20 @@ GEM rackup (1.0.0) rack (< 3) webrick - rails (7.1.1) - actioncable (= 7.1.1) - actionmailbox (= 7.1.1) - actionmailer (= 7.1.1) - actionpack (= 7.1.1) - actiontext (= 7.1.1) - actionview (= 7.1.1) - activejob (= 7.1.1) - activemodel (= 7.1.1) - activerecord (= 7.1.1) - activestorage (= 7.1.1) - activesupport (= 7.1.1) + rails (7.1.2) + actioncable (= 7.1.2) + actionmailbox (= 7.1.2) + actionmailer (= 7.1.2) + actionpack (= 7.1.2) + actiontext (= 7.1.2) + actionview (= 7.1.2) + activejob (= 7.1.2) + activemodel (= 7.1.2) + activerecord (= 7.1.2) + activestorage (= 7.1.2) + activesupport (= 7.1.2) bundler (>= 1.15.0) - railties (= 7.1.1) + railties (= 7.1.2) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -585,9 +586,9 @@ GEM rails-i18n (7.0.8) i18n (>= 0.7, < 2) railties (>= 6.0.0, < 8) - railties (7.1.1) - actionpack (= 7.1.1) - activesupport (= 7.1.1) + railties (7.1.2) + actionpack (= 7.1.2) + activesupport (= 7.1.2) irb rackup (>= 1.0.0) rake (>= 12.2) @@ -737,7 +738,7 @@ GEM statsd-ruby (1.5.0) stoplight (3.0.2) redlock (~> 1.0) - stringio (3.0.8) + stringio (3.0.9) strong_migrations (1.6.4) activerecord (>= 5.2) swd (1.3.0) @@ -753,7 +754,7 @@ GEM test-prof (1.2.3) thor (1.3.0) tilt (2.3.0) - timeout (0.4.0) + timeout (0.4.1) tpm-key_attestation (0.12.0) bindata (~> 2.4) openssl (> 2.0) From a7117bbef635735d4f01fc2fb6eaa085568862fa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 14:13:57 +0100 Subject: [PATCH 04/63] Update dependency @rails/ujs to v7.1.2 (#27811) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4c70859d0..0058a6837 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2661,9 +2661,9 @@ __metadata: linkType: hard "@rails/ujs@npm:^7.1.1": - version: 7.1.1 - resolution: "@rails/ujs@npm:7.1.1" - checksum: 79aa50400097d9254e194979cc011aaa92b456631fd0087a7bfc6b74ff47821c005b34a0fbb421361741c68133ac9bb35f1bb8f97de1c501144dad4e2c7440f3 + version: 7.1.2 + resolution: "@rails/ujs@npm:7.1.2" + checksum: 072962733c371fa0fff5e88a0aecd8e91c892f9dc2d31723b7586b45c723206d6b82ac71b0d7db26ea0a5ce60e0832430b061e4669b8f2aa813c3ea975aac98a languageName: node linkType: hard From e5a7b73ef4ca8934bb967d15d17c8ee1bd1c2cf0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 14:21:02 +0100 Subject: [PATCH 05/63] New Crowdin Translations (automated) (#27815) Co-authored-by: GitHub Actions <noreply@github.com> --- app/javascript/mastodon/locales/be.json | 1 + app/javascript/mastodon/locales/et.json | 1 + app/javascript/mastodon/locales/fa.json | 5 +- app/javascript/mastodon/locales/fi.json | 1 + app/javascript/mastodon/locales/fo.json | 1 + app/javascript/mastodon/locales/ig.json | 18 +++ app/javascript/mastodon/locales/ja.json | 4 +- app/javascript/mastodon/locales/ko.json | 1 + app/javascript/mastodon/locales/lt.json | 13 ++- app/javascript/mastodon/locales/no.json | 1 + app/javascript/mastodon/locales/pt-BR.json | 1 + app/javascript/mastodon/locales/pt-PT.json | 1 + app/javascript/mastodon/locales/vi.json | 1 + config/locales/doorkeeper.lt.yml | 128 ++++++++++++++++++++- config/locales/fa.yml | 8 +- config/locales/ig.yml | 4 + config/locales/ja.yml | 2 +- config/locales/ko.yml | 4 +- config/locales/nn.yml | 8 ++ config/locales/no.yml | 17 +++ config/locales/simple_form.he.yml | 2 +- config/locales/simple_form.no.yml | 1 + 22 files changed, 210 insertions(+), 13 deletions(-) diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json index 6262d24e2..7c2d652b6 100644 --- a/app/javascript/mastodon/locales/be.json +++ b/app/javascript/mastodon/locales/be.json @@ -222,6 +222,7 @@ "emoji_button.search_results": "Вынікі пошуку", "emoji_button.symbols": "Сімвалы", "emoji_button.travel": "Падарожжы і месцы", + "empty_column.account_hides_collections": "Гэты карыстальнік вырашыў схаваць гэтую інфармацыю", "empty_column.account_suspended": "Уліковы запіс прыпынены", "empty_column.account_timeline": "Тут няма допісаў!", "empty_column.account_unavailable": "Профіль недаступны", diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json index 0b0a8110d..c4182a073 100644 --- a/app/javascript/mastodon/locales/et.json +++ b/app/javascript/mastodon/locales/et.json @@ -222,6 +222,7 @@ "emoji_button.search_results": "Otsitulemused", "emoji_button.symbols": "Sümbolid", "emoji_button.travel": "Reisimine & kohad", + "empty_column.account_hides_collections": "See kasutaja otsustas mitte teha seda infot saadavaks", "empty_column.account_suspended": "Konto kustutatud", "empty_column.account_timeline": "Siin postitusi ei ole!", "empty_column.account_unavailable": "Profiil pole saadaval", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index f4a2e09e7..246f21899 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -222,6 +222,7 @@ "emoji_button.search_results": "نتایج جستوجو", "emoji_button.symbols": "نمادها", "emoji_button.travel": "سفر و مکان", + "empty_column.account_hides_collections": "کاربر خواسته که این اطّلاعات در دسترس نباشند", "empty_column.account_suspended": "حساب معلق شد", "empty_column.account_timeline": "هیچ فرستهای اینجا نیست!", "empty_column.account_unavailable": "نمایهٔ موجود نیست", @@ -358,13 +359,13 @@ "keyboard_shortcuts.profile": "گشودن نمایهٔ نویسنده", "keyboard_shortcuts.reply": "پاسخ به فرسته", "keyboard_shortcuts.requests": "گشودن سیاههٔ درخواستهای پیگیری", - "keyboard_shortcuts.search": "تمرکز روی جستوجو", + "keyboard_shortcuts.search": "تمرکز روی نوار جستوجو", "keyboard_shortcuts.spoilers": "نمایش/نهفتن زمینهٔ هشدار محتوا", "keyboard_shortcuts.start": "گشودن ستون «آغاز کنید»", "keyboard_shortcuts.toggle_hidden": "نمایش/نهفتن نوشتهٔ پشت هشدار محتوا", "keyboard_shortcuts.toggle_sensitivity": "نمایش/نهفتن رسانه", "keyboard_shortcuts.toot": "شروع یک فرستهٔ جدید", - "keyboard_shortcuts.unfocus": "برداشتن تمرکز از نوشتن/جستوجو", + "keyboard_shortcuts.unfocus": "برداشتن تمرکز از ناحیهٔ نوشتن یا جستوجو", "keyboard_shortcuts.up": "بالا بردن در سیاهه", "lightbox.close": "بستن", "lightbox.compress": "فشردهسازی جعبهٔ نمایش تصویر", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index cc4a9391a..9aa2e7355 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -222,6 +222,7 @@ "emoji_button.search_results": "Hakutulokset", "emoji_button.symbols": "Symbolit", "emoji_button.travel": "Matkailu ja paikat", + "empty_column.account_hides_collections": "Käyttäjä on päättänyt olla julkaisematta näitä tietoja", "empty_column.account_suspended": "Tili jäädytetty", "empty_column.account_timeline": "Ei viestejä täällä.", "empty_column.account_unavailable": "Profiilia ei löydy", diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json index d6130deb0..42a1317db 100644 --- a/app/javascript/mastodon/locales/fo.json +++ b/app/javascript/mastodon/locales/fo.json @@ -222,6 +222,7 @@ "emoji_button.search_results": "Leitiúrslit", "emoji_button.symbols": "Ímyndir", "emoji_button.travel": "Ferðing og støð", + "empty_column.account_hides_collections": "Hesin brúkarin hevur valt, at hesar upplýsingarnar ikki skulu vera tøkar", "empty_column.account_suspended": "Kontan gjørd óvirkin", "empty_column.account_timeline": "Einki uppslag her!", "empty_column.account_unavailable": "Vangin er ikki tøkur", diff --git a/app/javascript/mastodon/locales/ig.json b/app/javascript/mastodon/locales/ig.json index 3eec2158b..201bebc05 100644 --- a/app/javascript/mastodon/locales/ig.json +++ b/app/javascript/mastodon/locales/ig.json @@ -1,8 +1,11 @@ { "account.add_or_remove_from_list": "Tinye ma ọ bụ Wepu na ndepụta", "account.badges.bot": "Bot", + "account.badges.group": "Otù", "account.cancel_follow_request": "Withdraw follow request", "account.follow": "Soro", + "account.followers": "Ndị na-eso", + "account.following": "Na-eso", "account.follows_you": "Na-eso gị", "account.mute": "Mee ogbi @{name}", "account.unfollow": "Kwụsị iso", @@ -11,16 +14,20 @@ "audio.hide": "Zoo ụda", "bundle_column_error.retry": "Nwaa ọzọ", "bundle_column_error.routing.title": "404", + "bundle_modal_error.close": "Mechie", "bundle_modal_error.retry": "Nwaa ọzọ", "column.about": "Maka", "column.blocks": "Ojiarụ egbochiri", "column.bookmarks": "Ebenrụtụakā", "column.home": "Be", + "column.lists": "Ndepụta", "column.pins": "Pinned post", + "column_header.pin": "Gbado na profaịlụ gị", "column_subheading.settings": "Mwube", "community.column_settings.media_only": "Media only", "compose.language.change": "Gbanwee asụsụ", "compose.language.search": "Chọọ asụsụ...", + "compose.published.open": "Mepe", "compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.", "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.", "compose_form.placeholder": "What is on your mind?", @@ -32,7 +39,10 @@ "confirmations.delete.message": "Are you sure you want to delete this status?", "confirmations.delete_list.confirm": "Hichapụ", "confirmations.domain_block.confirm": "Hide entire domain", + "confirmations.edit.confirm": "Dezie", + "confirmations.mute.confirm": "Mee ogbi", "confirmations.reply.confirm": "Zaa", + "confirmations.unfollow.confirm": "Kwụsị iso", "conversation.delete": "Hichapụ nkata", "dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.", "dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.", @@ -76,6 +86,7 @@ "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", "lists.delete": "Hichapụ ndepụta", + "lists.edit": "Dezie ndepụta", "lists.subheading": "Ndepụta gị", "loading_indicator.label": "Na-adọnye...", "navigation_bar.about": "Maka", @@ -100,20 +111,27 @@ "privacy.change": "Adjust status privacy", "privacy.direct.short": "Direct", "privacy.private.short": "Followers-only", + "relative_time.full.just_now": "kịta", "relative_time.just_now": "kịta", "relative_time.today": "taa", "reply_indicator.cancel": "Kagbuo", "report.categories.other": "Ọzọ", + "report.categories.spam": "Nzipụ Ozièlètrọniìk Nkeāchọghị", + "report.mute": "Mee ogbi", "report.placeholder": "Type or paste additional comments", "report.submit": "Submit report", "report.target": "Report {target}", "report_notification.attached_statuses": "{count, plural, one {# post} other {# posts}} attached", + "report_notification.categories.other": "Ọzọ", + "search.placeholder": "Chọọ", "server_banner.active_users": "ojiarụ dị ìrè", + "server_banner.learn_more": "Mụtakwuo", "sign_in_banner.sign_in": "Sign in", "status.admin_status": "Open this status in the moderation interface", "status.bookmark": "Kee ebenrụtụakā", "status.copy": "Copy link to status", "status.delete": "Hichapụ", + "status.edit": "Dezie", "status.edited_x_times": "Edited {count, plural, one {# time} other {# times}}", "status.open": "Expand this status", "status.remove_bookmark": "Wepu ebenrụtụakā", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 72a57c8e3..4cd7228c8 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -586,8 +586,8 @@ "search.no_recent_searches": "検索履歴はありません", "search.placeholder": "検索", "search.quick_action.account_search": "{x}に該当するプロフィール", - "search.quick_action.go_to_account": "{x}のプロフィールを見る", - "search.quick_action.go_to_hashtag": "{x}に該当するハッシュタグ", + "search.quick_action.go_to_account": "プロフィール {x} を見る", + "search.quick_action.go_to_hashtag": "ハッシュタグ {x} を見る", "search.quick_action.open_url": "MastodonでURLを開く", "search.quick_action.status_search": "{x}に該当する投稿", "search.search_or_paste": "検索またはURLを入力", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 269063b7c..574d8e211 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -222,6 +222,7 @@ "emoji_button.search_results": "검색 결과", "emoji_button.symbols": "기호", "emoji_button.travel": "여행과 장소", + "empty_column.account_hides_collections": "이 사용자는 이 정보를 사용할 수 없도록 설정했습니다", "empty_column.account_suspended": "계정 정지됨", "empty_column.account_timeline": "이곳에는 게시물이 없습니다!", "empty_column.account_unavailable": "프로필 사용 불가", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index a1ab53131..75f4a239e 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -15,7 +15,7 @@ "account.add_or_remove_from_list": "Pridėti arba ištrinti iš sąrašų", "account.badges.bot": "Automatizuotas", "account.badges.group": "Grupė", - "account.block": "Užblokuoti @{name}", + "account.block": "Blokuoti @{name}", "account.block_domain": "Blokuoti domeną {domain}", "account.block_short": "Blokuoti", "account.blocked": "Užblokuota", @@ -25,11 +25,20 @@ "account.disable_notifications": "Nustoti man pranešti, kai @{name} paskelbia", "account.domain_blocked": "Užblokuotas domenas", "account.edit_profile": "Redaguoti profilį", + "account.enable_notifications": "Pranešti man, kai @{name} paskelbia", + "account.featured_tags.last_status_at": "Paskutinį kartą paskelbta {date}", + "account.featured_tags.last_status_never": "Nėra įrašų", "account.follow": "Sekti", - "account.follows_you": "Seka jus", + "account.followers": "Sekėjai", + "account.followers.empty": "Šio naudotojo dar niekas neseka.", + "account.followers_counter": "{count, plural, one {{counter} sekėjas (-a)} few {{counter} sekėjai} many {{counter} sekėjo} other {{counter} sekėjų}}", + "account.following": "Seka", + "account.follows.empty": "Šis naudotojas (-a) dar nieko neseka.", + "account.follows_you": "Seka tave", "account.go_to_profile": "Eiti į profilį", "account.in_memoriam": "Atminimui.", "account.joined_short": "Prisijungė", + "account.languages": "Keisti prenumeruojamas kalbas", "account.locked_info": "Šios paskyros privatumo būsena nustatyta kaip užrakinta. Savininkas (-ė) rankiniu būdu peržiūri, kas gali sekti.", "account.media": "Medija", "account.mute": "Užtildyti @{name}", diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index 449fdfb1a..7421c780f 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -222,6 +222,7 @@ "emoji_button.search_results": "Søkeresultat", "emoji_button.symbols": "Symboler", "emoji_button.travel": "Reise & steder", + "empty_column.account_hides_collections": "Denne brukeren har valgt å ikke gjøre denne informasjonen tilgjengelig", "empty_column.account_suspended": "Kontoen er suspendert", "empty_column.account_timeline": "Ingen innlegg her!", "empty_column.account_unavailable": "Profilen er utilgjengelig", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index dfb464dbb..9c09e2d71 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -222,6 +222,7 @@ "emoji_button.search_results": "Resultado da pesquisa", "emoji_button.symbols": "Símbolos", "emoji_button.travel": "Viagem e Lugares", + "empty_column.account_hides_collections": "A pessoa optou por não disponibilizar esta informação", "empty_column.account_suspended": "Conta suspensa", "empty_column.account_timeline": "Nada aqui.", "empty_column.account_unavailable": "Perfil indisponível", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index f33dd2fb8..988bec9b0 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -222,6 +222,7 @@ "emoji_button.search_results": "Resultados da pesquisa", "emoji_button.symbols": "Símbolos", "emoji_button.travel": "Viagens & Lugares", + "empty_column.account_hides_collections": "Este utilizador escolheu não disponibilizar esta informação", "empty_column.account_suspended": "Conta suspensa", "empty_column.account_timeline": "Sem publicações por aqui!", "empty_column.account_unavailable": "Perfil indisponível", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index 3a7e75b91..aa229eceb 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -222,6 +222,7 @@ "emoji_button.search_results": "Kết quả tìm kiếm", "emoji_button.symbols": "Biểu tượng", "emoji_button.travel": "Du lịch", + "empty_column.account_hides_collections": "Người này đã chọn ẩn thông tin", "empty_column.account_suspended": "Tài khoản vô hiệu hóa", "empty_column.account_timeline": "Chưa có tút nào!", "empty_column.account_unavailable": "Tài khoản bị đình chỉ", diff --git a/config/locales/doorkeeper.lt.yml b/config/locales/doorkeeper.lt.yml index 36f31e39e..1012160f6 100644 --- a/config/locales/doorkeeper.lt.yml +++ b/config/locales/doorkeeper.lt.yml @@ -1,18 +1,143 @@ --- lt: + activerecord: + attributes: + doorkeeper/application: + name: Programėlės pavadinimas + redirect_uri: Peradresavimo URI + scopes: Aprėptys + website: Programėlės svetainė + errors: + models: + doorkeeper/application: + attributes: + redirect_uri: + fragment_present: negali turėti fragmento. + invalid_uri: turi būti tinkamas URI. + relative_uri: turi būti absoliutus URI. + secured_uri: turi būti HTTPS/SSL URI. doorkeeper: + applications: + buttons: + authorize: Įgalinti + cancel: Atšaukti + destroy: Sunaikinti + edit: Redaguoti + submit: Pateikti + confirmations: + destroy: Ar esi įsitikinęs (-usi)? + edit: + title: Redaguoti programėlę + form: + error: Ups! Patikrink, ar formoje nėra galimų klaidų. + help: + native_redirect_uri: Naudoti %{native_redirect_uri} vietiniams bandymams + redirect_uri: Naudoti po vieną eilutę kiekvienam URI + scopes: Atskirk aprėptis tarpais. Palik tuščią, jei nori naudoti numatytąsias aprėtis. + index: + application: Programėlė + callback_url: Atgalinis URL + delete: Ištrinti + empty: Neturi jokių programėlių. + name: Pavadinimas + new: Nauja programėlė + scopes: Aprėptys + show: Rodyti + title: Tavo programėlės + new: + title: Nauja programėlė + show: + actions: Veiksmai + application_id: Kliento raktas + callback_urls: Atgalinių URL adresų + scopes: Aprėptys + secret: Kliento paslaptis + title: 'Programėlė: %{name}' authorizations: + buttons: + authorize: Įgalinti + deny: Atmesti error: title: Įvyko klaida. new: prompt_html: "%{client_name} norėtų gauti leidimą prieigos prie tavo paskyros. Tai trečiosios šalies programėlė. <strong>Jei ja nepasitiki, neturėtum jai leisti.</strong>" + review_permissions: Peržiūrėti leidimus + title: Reikalingas įgaliojimas + show: + title: Nukopijuok šį įgaliojimo kodą ir įklijuok jį į programėlę. authorized_applications: + buttons: + revoke: Naikinti + confirmations: + revoke: Ar esi įsitikinęs (-usi)? index: - title: Tavo leidžiamos programėlės + authorized_at: Įgaliota %{date} + description_html: Tai programėlės, kurios gali pasiekti tavo paskyrą naudojant API. Jei čia yra programėlių, kurių neatpažįsti, arba jei programėlė elgiasi netinkamai, gali panaikinti jos prieigą. + last_used_at: Paskutinį kartą naudota %{date} + never_used: Niekada nenaudotas + scopes: Leidimai + superapp: Vidinis + title: Tavo įgaliotos programėlės + errors: + messages: + access_denied: Išteklių savininkas (-ė) arba įgaliojimų serveris atmetė užklausą. + credential_flow_not_configured: Išteklių savininko slaptažodžio kredencialų srautas nepavyko, nes Doorkeeper.configure.resource_owner_from_credentials nėra nesukonfigūruotas. + invalid_client: Kliento tapatybės nustatymas nepavyko dėl nežinomo kliento, neįtraukto kliento tapatybės nustatymo arba nepalaikomo tapatybės nustatymo metodo. + invalid_grant: Pateiktas įgaliojimas yra netinkamas, pasibaigęs, panaikintas, neatitinka įgaliojimo užklausoje naudoto nukreipimo URI arba buvo išduotas kitam klientui. + invalid_redirect_uri: Nukreipimo uri įtrauktas yra netinkamas. + invalid_request: + missing_param: 'Trūksta privalomo parametro: %{value}.' + request_not_authorized: Užklausą reikia įgalioti. Reikalingo parametro užklausai įgalioti trūksta arba jis netinkamas. + unknown: Užklausoje trūksta privalomo parametro, turi nepalaikomą parametro reikšmę arba yra kitaip netinkamai suformuota. + invalid_resource_owner: Pateikti išteklių savininko įgaliojimai yra netinkami arba išteklių savininko negalima surasti. + invalid_scope: Užklausos aprėptis yra netinkama, nežinoma arba netinkamai suformuota. + invalid_token: + expired: Baigėsi prieigos rakto galiojimas. + revoked: Prieigos raktas buvo panaikintas. + unknown: Prieigos raktas yra netinkamas. + resource_owner_authenticator_not_configured: Išteklių savininko suradimas nepavyko dėl to, kad Doorkeeper.configure.resource_owner_authenticator nėra sukonfigūruotas. + server_error: Įgaliojimų serveris susidūrė su netikėta sąlyga, dėl kurios negalėjo užpildyti užklausos. + temporarily_unavailable: Įgaliojimų serveris šiuo metu negali apdoroti užklausos dėl laikinos serverio perkrovos arba techninės priežiūros. + unauthorized_client: Klientas nėra įgaliotas atlikti šią užklausą šiuo metodu. + unsupported_grant_type: Įgaliojimų suteikimo tipas nepalaikomas įgaliojimų serveryje. + unsupported_response_type: Įgaliojimų serveris nepalaiko šio atsako tipo. + flash: + applications: + create: + notice: Programėlė sukurta. + destroy: + notice: Programėlė ištrinta. + update: + notice: Programėlė atnaujinta. + authorized_applications: + destroy: + notice: Programėlė panaikinta. grouped_scopes: + access: + read: Tik skaitymo prieiga + read/write: Skaitymo ir rašymo prieiga + write: Tik rašymo prieiga title: + accounts: Paskyros + admin/accounts: Paskyrų administravimas + admin/all: Visi administraciniai funkcijos + admin/reports: Ataskaitų administravimas + all: Pilna prieiga prie tavo Mastodon paskyros blocks: Blokavimai + bookmarks: Žymės + conversations: Pokalbiai + crypto: Galo iki galo užšifravimas + favourites: Mėgstami + filters: Filtrai follow: Sekimai, nutildymai ir blokavimai + follows: Sekimai + lists: Sąrašai + media: Medijos priedai + mutes: Užtildymai + notifications: Pranešimai + push: Stumdomieji pranešimai + reports: Ataskaitos + search: Paieška statuses: Įrašai layouts: admin: @@ -37,6 +162,7 @@ lt: admin:write:domain_blocks: atlikti prižiūrėjimo veiksmus su domenų blokavimais admin:write:email_domain_blocks: atlikti prižiūrėjimo veiksmus su el. laiško domenų blokavimais admin:write:ip_blocks: atlikti prižiūrėjimo veiksmus su IP blokavimais + admin:write:reports: atlikti paskyrų prižiūrėjimo veiksmus atsakaitams crypto: naudoti galo iki galo šifravimą follow: modifikuoti paskyros santykius push: gauti tavo stumiamuosius pranešimus diff --git a/config/locales/fa.yml b/config/locales/fa.yml index 8e076878a..8569d2e37 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -131,7 +131,7 @@ fa: reset_password: بازنشانی گذرواژه resubscribe: اشتراک دوباره role: نقش - search: جستجو + search: جستوجو search_same_email_domain: دیگر کاربران با دامنهٔ رایانامهٔ یکسان search_same_ip: دیگر کاربران با IP یکسان security: امنیت @@ -386,6 +386,10 @@ fa: confirm_suspension: cancel: لغو confirm: تعلیق + permanent_action: برگرداندن تعلیق هیچ داده یا ارتباطی را برنخواهد گرداند. + preamble_html: در حال تعلیق <strong>%{domain}</strong> و همهٔ زیردامنههایش هستید. + remove_all_data: این کار همهٔ دادههای نمایه، محتوا و رسانههای حسابهای این دامنه را از کارسازتان برمیدارد. + stop_communication: کارسازتان دیگر با این کارسازها ارتباط برقرار نخواهد کرد. title: تأیید انسداد دامنه برای %{domain} created_msg: مسدودسازی دامنه در حال پردازش است destroyed_msg: انسداد دامنه واگردانده شد @@ -1219,7 +1223,7 @@ fa: followers: این کار همهٔ پیگیران شما را از حساب فعلی به حساب تازه منتقل خواهد کرد only_redirect_html: شما همچنین میتوانید حساب خود را <a href="%{path}">به یک حساب دیگر اشاره دهید</a>. other_data: هیچ دادهٔ دیگری خودبهخود منتقل نخواهد شد - redirect: نمایهٔ حساب فعلی شما به حساب تازه اشاره خواهد کرد و خودش در نتیجهٔ جستجوها ظاهر نخواهد شد + redirect: نمایهٔ حساب کنونیتان به حساب تازه اشاره خواهد کرد و از جستوجوها حذف خواهد شد moderation: title: مدیریت کاربران move_handler: diff --git a/config/locales/ig.yml b/config/locales/ig.yml index 7c264f0d7..9db771fdc 100644 --- a/config/locales/ig.yml +++ b/config/locales/ig.yml @@ -1 +1,5 @@ +--- ig: + filters: + contexts: + home: Ụlọ na ndepụta diff --git a/config/locales/ja.yml b/config/locales/ja.yml index ca438f53d..3318b690a 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -1023,7 +1023,7 @@ ja: hint_html: もう一つだけ!あなたが人間であることを確認する必要があります(スパムを防ぐためです!)。 以下のCAPTCHAを解き、「続ける」をクリックします。 title: セキュリティチェック confirmations: - awaiting_review: メールアドレスは確認済みです。%{domain} のモデレーターによりアカウント登録の審査が完了すると、メールでお知らせします。 + awaiting_review: メールアドレスが確認できました。%{domain} のスタッフが登録審査を行います。承認されたらメールでお知らせします! awaiting_review_title: 登録の審査待ちです clicking_this_link: このリンクを押す login_link: ログイン diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 530cf6a44..72eafc8fb 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -1689,8 +1689,8 @@ ko: keep_polls_hint: 설문을 삭제하지 않았음 keep_self_bookmark: 북마크한 게시물 유지 keep_self_bookmark_hint: 북마크한 본인의 게시물을 삭제하지 않습니다 - keep_self_fav: 마음에 들어한 게시물 유지 - keep_self_fav_hint: 내 스스로 마음에 들어한 본인의 게시물을 삭제하지 않습니다 + keep_self_fav: 내가 좋아요한 게시물 유지 + keep_self_fav_hint: 스스로 좋아요를 누른 본인의 게시물을 삭제하지 않습니다 min_age: '1209600': 2 주 '15778476': 6 개월 diff --git a/config/locales/nn.yml b/config/locales/nn.yml index 551824e80..acd6b206e 100644 --- a/config/locales/nn.yml +++ b/config/locales/nn.yml @@ -1041,6 +1041,14 @@ nn: hint_html: Berre ein ting til! Vi må bekrefte at du er et menneske (så vi kan halde spam ute!). Løys CAPTCHA-en nedanfor og klikk "Fortsett". title: Sikkerheitssjekk confirmations: + awaiting_review: Din e-post adresse er bekreftet! %{domain} ansatte gjennomgår nå registreringen din. Du vil motta en e-post hvis de godkjenner din konto! + awaiting_review_title: Din registrering blir vurdert + clicking_this_link: klikke på denne lenken + login_link: logg inn + proceed_to_login_html: Du kan nå fortsette til %{login_link}. + redirect_to_app_html: Du burde bli omdirigert til <strong>%{app_name}</strong> -appen. Hvis det ikke skjedde, kan du prøve %{clicking_this_link} eller manuelt gå tilbake til appen. + registration_complete: Registreringen på %{domain} er nå fullført! + welcome_title: Velkommen, %{name}! wrong_email_hint: Viss epostadressa er feil, kan du endra ho i kontoinnstillingane. delete_account: Slett konto delete_account_html: Om du vil sletta kontoen din, kan du <a href="%{path}">gå hit</a>. Du vert spurd etter stadfesting. diff --git a/config/locales/no.yml b/config/locales/no.yml index 1abfbdb97..75085fa5a 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -772,6 +772,11 @@ approved: Godkjenning kreves for påmelding none: Ingen kan melde seg inn open: Hvem som helst kan melde seg inn + security: + authorized_fetch: Krev autentisering fra fødererte servere + authorized_fetch_hint: Krav om godkjenning fra fødererte servere muliggjør strengere håndhevelse av blokker på både brukernivå og servernivå. Dette går imidlertid på bekostning av en ytelsesstraff, reduserer rekkevidden til svarene dine og kan introdusere kompatibilitetsproblemer med enkelte fødererte tjenester. I tillegg vil dette ikke hindre dedikerte aktører i å hente dine offentlige innlegg og kontoer. + authorized_fetch_overridden_hint: Du kan for øyeblikket ikke endre denne innstillingen fordi den overstyres av en miljøvariabel. + federation_authentication: Håndheving av føderasjonsautentisering title: Serverinnstillinger site_uploads: delete: Slett den opplastede filen @@ -1036,6 +1041,14 @@ hint_html: Bare en ting til! Vi må bekrefte at du er et menneske (dette er slik at vi kan holde spam ute!). Løs CAPTCHA nedenfor og klikk "Fortsett". title: Sikkerhetskontroll confirmations: + awaiting_review: Din e-post adresse er bekreftet! %{domain} ansatte gjennomgår nå registreringen din. Du vil motta en e-post hvis de godkjenner din konto! + awaiting_review_title: Din registrering blir vurdert + clicking_this_link: klikke på denne lenken + login_link: logg inn + proceed_to_login_html: Du kan nå fortsette til %{login_link}. + redirect_to_app_html: Du burde bli omdirigert til <strong>%{app_name}</strong> -appen. Hvis det ikke skjedde, kan du prøve %{clicking_this_link} eller manuelt gå tilbake til appen. + registration_complete: Registreringen på %{domain} er nå fullført! + welcome_title: Velkommen, %{name}! wrong_email_hint: Hvis e-postadressen ikke er riktig, kan du endre den i kontoinnstillingene. delete_account: Slett konto delete_account_html: Hvis du ønsker å slette kontoen din, kan du <a href="%{path}">gå hit</a>. Du vil bli spurt om bekreftelse. @@ -1739,6 +1752,10 @@ month: "%b %Y" time: "%H:%M" with_time_zone: "%-d. %b %Y, %H:%M %Z" + translation: + errors: + quota_exceeded: Den serveromfattende brukskvoten for oversettelsestjenesten er overskredet. + too_many_requests: Det har nylig vært for mange forespørsler til oversettelsestjenesten. two_factor_authentication: add: Legg til disable: Skru av diff --git a/config/locales/simple_form.he.yml b/config/locales/simple_form.he.yml index 581a66807..13ea8a0c4 100644 --- a/config/locales/simple_form.he.yml +++ b/config/locales/simple_form.he.yml @@ -5,7 +5,7 @@ he: account: discoverable: הפוסטים והפרופיל שלך עשויים להיות מוצגים או מומלצים באזורים שונים באתר וייתכן שהפרופיל שלך יוצע למשתמשים אחרים. display_name: שמך המלא או שם הכיף שלך. - fields: עמוד הבית שלך, כינויי גוף, גיל, וכל מידע אחר לפי העדפתך האישית. + fields: עמוד הבית שלך, לשון הפנייה, גיל, וכל מידע אחר לפי העדפתך האישית. indexable: ההודעות הפומביות שלך עשויות להופיע בתוצאות חיפוש במסטודון. אחרים שהדהדו, חיבבו או ענו להודעות האלו יוכלו למצוא אותן בחיפוש בכל מקרה. note: 'ניתן לאזכר @אחרים או #תגיות.' show_collections: אנשים יוכלו לדפדף בין העוקבים והנעקבים שלך. אנשים שאת.ה עוקב.ת אחריהם יראו את המעקב אחריהם כרגיל. diff --git a/config/locales/simple_form.no.yml b/config/locales/simple_form.no.yml index a9d5465f3..ca2020e21 100644 --- a/config/locales/simple_form.no.yml +++ b/config/locales/simple_form.no.yml @@ -323,6 +323,7 @@ url: Endepunkt lenke 'no': Nei not_recommended: Ikke anbefalt + overridden: Overstyrt recommended: Anbefalt required: mark: "*" From 5bca5c4c5b036c922144e713e776727cd06615bf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 14:24:14 +0100 Subject: [PATCH 06/63] Update formatjs monorepo (#27823) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 142 +++++++++++++++++++++++++++--------------------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0058a6837..c1e9ec9a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1800,13 +1800,13 @@ __metadata: languageName: node linkType: hard -"@formatjs/ecma402-abstract@npm:1.17.3": - version: 1.17.3 - resolution: "@formatjs/ecma402-abstract@npm:1.17.3" +"@formatjs/ecma402-abstract@npm:1.17.4": + version: 1.17.4 + resolution: "@formatjs/ecma402-abstract@npm:1.17.4" dependencies: - "@formatjs/intl-localematcher": "npm:0.5.0" + "@formatjs/intl-localematcher": "npm:0.5.1" tslib: "npm:^2.4.0" - checksum: 00eb87301272d22dcdb0f2de74df22fa96c0062a9c1b1d718da5b8d8a4d9841aa59f909da624bd7f99db97d19c8a0338e511ad57a01d6fcfc02e67c6627035ce + checksum: c24bf58cd3152ad64a29dfab185d1fde91e44423aabb041f332216b37a23256618efee1e252c0015e735bc688708ee279348e2a4a67a77f6cf918028848ef071 languageName: node linkType: hard @@ -1830,14 +1830,14 @@ __metadata: languageName: node linkType: hard -"@formatjs/icu-messageformat-parser@npm:2.7.1": - version: 2.7.1 - resolution: "@formatjs/icu-messageformat-parser@npm:2.7.1" +"@formatjs/icu-messageformat-parser@npm:2.7.2": + version: 2.7.2 + resolution: "@formatjs/icu-messageformat-parser@npm:2.7.2" dependencies: - "@formatjs/ecma402-abstract": "npm:1.17.3" - "@formatjs/icu-skeleton-parser": "npm:1.6.3" + "@formatjs/ecma402-abstract": "npm:1.17.4" + "@formatjs/icu-skeleton-parser": "npm:1.6.4" tslib: "npm:^2.4.0" - checksum: 4bfc01538ef385e8bcc7c86f08b20ab87ce22c355cf3ea322c8f8d941260e00484f10aa43266f5eea096879f782fa6cdb8fe649dcb13d2745868beea36316ecb + checksum: c7a2f7daecec9ba36acda2c5b7ef21f515883b886d4d9965d83c93bc55fc604f56c1097d4641608633c32917aaa0b9b0c65c0d162723428249dc29271270a064 languageName: node linkType: hard @@ -1851,35 +1851,35 @@ __metadata: languageName: node linkType: hard -"@formatjs/icu-skeleton-parser@npm:1.6.3": - version: 1.6.3 - resolution: "@formatjs/icu-skeleton-parser@npm:1.6.3" +"@formatjs/icu-skeleton-parser@npm:1.6.4": + version: 1.6.4 + resolution: "@formatjs/icu-skeleton-parser@npm:1.6.4" dependencies: - "@formatjs/ecma402-abstract": "npm:1.17.3" + "@formatjs/ecma402-abstract": "npm:1.17.4" tslib: "npm:^2.4.0" - checksum: 97a05fef16c93d9d663c5d7dadf9dcc72217d7a8afedda3a441ceedf085d45e500022dd67d3c578a1a508dd334538e4eca07129a9994c39c0c36e205e567c04b + checksum: 3688aad6d12fe677ef0ce3d6a3424c5bde9ed223dc49841de8dd33c547bdd2858f8bce4437fcc135048b4f92385374776ab48e39b3cc5063a45bdb1ce85ad2d4 languageName: node linkType: hard -"@formatjs/intl-displaynames@npm:6.6.2": - version: 6.6.2 - resolution: "@formatjs/intl-displaynames@npm:6.6.2" +"@formatjs/intl-displaynames@npm:6.6.3": + version: 6.6.3 + resolution: "@formatjs/intl-displaynames@npm:6.6.3" dependencies: - "@formatjs/ecma402-abstract": "npm:1.17.3" - "@formatjs/intl-localematcher": "npm:0.5.0" + "@formatjs/ecma402-abstract": "npm:1.17.4" + "@formatjs/intl-localematcher": "npm:0.5.1" tslib: "npm:^2.4.0" - checksum: e0dbf749b19b93e009d487e70f4926bac4f94b0a4e60b435d4f925f686a54a869fd871902f6af322843437b8f80725981136be20cfb145c7d7c3b9826bfc43e3 + checksum: b0520cb744a51290fbcde80860f39ed9c9df9b81beae98986e1fc089ef635f7699c750631fa42a559f3678d1dd02b14904614e70360477d18e68d3eba6592390 languageName: node linkType: hard -"@formatjs/intl-listformat@npm:7.5.1": - version: 7.5.1 - resolution: "@formatjs/intl-listformat@npm:7.5.1" +"@formatjs/intl-listformat@npm:7.5.2": + version: 7.5.2 + resolution: "@formatjs/intl-listformat@npm:7.5.2" dependencies: - "@formatjs/ecma402-abstract": "npm:1.17.3" - "@formatjs/intl-localematcher": "npm:0.5.0" + "@formatjs/ecma402-abstract": "npm:1.17.4" + "@formatjs/intl-localematcher": "npm:0.5.1" tslib: "npm:^2.4.0" - checksum: bb972ad9078db197699452a2958dea74cfd62fcea84015a8786ac8e399738c9fe08c3c71dd1ea34ff46f24783daa20d26d5af4b5d1df5ee0ca9af7530788d2a5 + checksum: 54fa03da4ea45504681d6d87d72d1cac574809ce43f965fa4b845e83be3072d92324c58cec57ad386827087fb1d6ecae438d29576f30176bf52eb212e454bce2 languageName: node linkType: hard @@ -1892,43 +1892,43 @@ __metadata: languageName: node linkType: hard -"@formatjs/intl-localematcher@npm:0.5.0": - version: 0.5.0 - resolution: "@formatjs/intl-localematcher@npm:0.5.0" +"@formatjs/intl-localematcher@npm:0.5.1": + version: 0.5.1 + resolution: "@formatjs/intl-localematcher@npm:0.5.1" dependencies: tslib: "npm:^2.4.0" - checksum: adc82cad4c37dfff2ba1d00216e2056ce4c91b85fd39c60474849ccd74ec1262a8203d460d0ee863b71eb399297c3e087f5ccbbf3a874d682b021e6e3fd2c943 + checksum: 2282db3e623d3f65681b6a2a2dbffc4f948b8411789f51af1b221610105f809ebec7f58f9afd5008e72c62ed5524c8c321f85c78cab0cffb632e20c0064b701b languageName: node linkType: hard "@formatjs/intl-pluralrules@npm:^5.2.2": - version: 5.2.8 - resolution: "@formatjs/intl-pluralrules@npm:5.2.8" + version: 5.2.9 + resolution: "@formatjs/intl-pluralrules@npm:5.2.9" dependencies: - "@formatjs/ecma402-abstract": "npm:1.17.3" - "@formatjs/intl-localematcher": "npm:0.5.0" + "@formatjs/ecma402-abstract": "npm:1.17.4" + "@formatjs/intl-localematcher": "npm:0.5.1" tslib: "npm:^2.4.0" - checksum: cc5826774829a9c424b05010c398192aef93d89cca0144ebdc91df29032b808b235e7dde8def27887c5cddb695affd518993d728a908911897599d67d10e1954 + checksum: a6ca5c498ce542facacf8ce8640d4ba068f9119b758547a23614b50611eb385a46abd386ff88fa423211355ec463cf102c2c908b74f6e23a5bc9e2a23873dc29 languageName: node linkType: hard -"@formatjs/intl@npm:2.9.6": - version: 2.9.6 - resolution: "@formatjs/intl@npm:2.9.6" +"@formatjs/intl@npm:2.9.8": + version: 2.9.8 + resolution: "@formatjs/intl@npm:2.9.8" dependencies: - "@formatjs/ecma402-abstract": "npm:1.17.3" + "@formatjs/ecma402-abstract": "npm:1.17.4" "@formatjs/fast-memoize": "npm:2.2.0" - "@formatjs/icu-messageformat-parser": "npm:2.7.1" - "@formatjs/intl-displaynames": "npm:6.6.2" - "@formatjs/intl-listformat": "npm:7.5.1" - intl-messageformat: "npm:10.5.5" + "@formatjs/icu-messageformat-parser": "npm:2.7.2" + "@formatjs/intl-displaynames": "npm:6.6.3" + "@formatjs/intl-listformat": "npm:7.5.2" + intl-messageformat: "npm:10.5.7" tslib: "npm:^2.4.0" peerDependencies: typescript: 5 peerDependenciesMeta: typescript: optional: true - checksum: 6dadbf3dfcd1534735899fb5eed3e340a547d92ca4b0c33857e7ec3a19bddbf7787d0ab2aa014f3c2d7288e81d0ed1cb3308b0ede8299fda6d12b0bd6b1ad89d + checksum: 6341f4bfb56a0e14373395b1232e1eeb8e64588a8c3d4614cd2b06f71d4e65dbd4a79e3a1c07e1b6c20c48e399ac2385977b01a559e1d2bd1a1d226e0eae3058 languageName: node linkType: hard @@ -1952,11 +1952,11 @@ __metadata: languageName: node linkType: hard -"@formatjs/ts-transformer@npm:3.13.7": - version: 3.13.7 - resolution: "@formatjs/ts-transformer@npm:3.13.7" +"@formatjs/ts-transformer@npm:3.13.8": + version: 3.13.8 + resolution: "@formatjs/ts-transformer@npm:3.13.8" dependencies: - "@formatjs/icu-messageformat-parser": "npm:2.7.1" + "@formatjs/icu-messageformat-parser": "npm:2.7.2" "@types/json-stable-stringify": "npm:^1.0.32" "@types/node": "npm:14 || 16 || 17" chalk: "npm:^4.0.0" @@ -1968,7 +1968,7 @@ __metadata: peerDependenciesMeta: ts-jest: optional: true - checksum: 1ce528a01fa831a4a07890ae6c447c1b1cb63870c98b5f5dfbbd9f69c7e42dcd3cfd58d2717cc7214b41ddb4c3949f69525e52b6f6001d1b494573036d1478c8 + checksum: 32b13b75732739ca016d9d654e5f40077cafa3ff2f924fbb5fd91155cd6af3292c5fee9be022bb224fb69d2ab60ed9cdda49ee83fbf9e1e8de470ee33ceae4f3 languageName: node linkType: hard @@ -4735,21 +4735,21 @@ __metadata: linkType: hard "babel-plugin-formatjs@npm:^10.5.1": - version: 10.5.8 - resolution: "babel-plugin-formatjs@npm:10.5.8" + version: 10.5.9 + resolution: "babel-plugin-formatjs@npm:10.5.9" dependencies: "@babel/core": "npm:^7.10.4" "@babel/helper-plugin-utils": "npm:^7.10.4" "@babel/plugin-syntax-jsx": "npm:7" "@babel/traverse": "npm:7" "@babel/types": "npm:^7.12.11" - "@formatjs/icu-messageformat-parser": "npm:2.7.1" - "@formatjs/ts-transformer": "npm:3.13.7" + "@formatjs/icu-messageformat-parser": "npm:2.7.2" + "@formatjs/ts-transformer": "npm:3.13.8" "@types/babel__core": "npm:^7.1.7" "@types/babel__helper-plugin-utils": "npm:^7.10.0" "@types/babel__traverse": "npm:^7.1.7" tslib: "npm:^2.4.0" - checksum: 837f031e46a771ac6874b7dbe852ebb0919da81af97d2a6d38e47cc798502ee0e02ce247c6f0d0665108aa336ef3b15b321070d444b6d6994499b931ab178537 + checksum: 5e4127cf7b4b9b3306a9d0ab5b029831712d22db5e2117225ce706b55d222d09a7eba1f3720fdad7a99f61843b5cba107296fc11ae00a6f0941217d9322aa02e languageName: node linkType: hard @@ -9276,15 +9276,15 @@ __metadata: languageName: node linkType: hard -"intl-messageformat@npm:10.5.5, intl-messageformat@npm:^10.3.5": - version: 10.5.5 - resolution: "intl-messageformat@npm:10.5.5" +"intl-messageformat@npm:10.5.7, intl-messageformat@npm:^10.3.5": + version: 10.5.7 + resolution: "intl-messageformat@npm:10.5.7" dependencies: - "@formatjs/ecma402-abstract": "npm:1.17.3" + "@formatjs/ecma402-abstract": "npm:1.17.4" "@formatjs/fast-memoize": "npm:2.2.0" - "@formatjs/icu-messageformat-parser": "npm:2.7.1" + "@formatjs/icu-messageformat-parser": "npm:2.7.2" tslib: "npm:^2.4.0" - checksum: 223f48d719585b572f07012e2432c742e0acc371950c01a6fc9d7cb3e022d8486593ab8b7ed50849444035cdce119484e49f7c32b3646a8601a4d8f312395cf2 + checksum: 7f341b3eb5b3d402167c99ca7fb98720c7ad553bed8a490b2210bd90ea9009a09f9030939307fecb111fce1454f31b4298b4f0a346999af627c86f8164a5c547 languageName: node linkType: hard @@ -13663,18 +13663,18 @@ __metadata: linkType: hard "react-intl@npm:^6.4.2": - version: 6.5.2 - resolution: "react-intl@npm:6.5.2" + version: 6.5.4 + resolution: "react-intl@npm:6.5.4" dependencies: - "@formatjs/ecma402-abstract": "npm:1.17.3" - "@formatjs/icu-messageformat-parser": "npm:2.7.1" - "@formatjs/intl": "npm:2.9.6" - "@formatjs/intl-displaynames": "npm:6.6.2" - "@formatjs/intl-listformat": "npm:7.5.1" + "@formatjs/ecma402-abstract": "npm:1.17.4" + "@formatjs/icu-messageformat-parser": "npm:2.7.2" + "@formatjs/intl": "npm:2.9.8" + "@formatjs/intl-displaynames": "npm:6.6.3" + "@formatjs/intl-listformat": "npm:7.5.2" "@types/hoist-non-react-statics": "npm:^3.3.1" "@types/react": "npm:16 || 17 || 18" hoist-non-react-statics: "npm:^3.3.2" - intl-messageformat: "npm:10.5.5" + intl-messageformat: "npm:10.5.7" tslib: "npm:^2.4.0" peerDependencies: react: ^16.6.0 || 17 || 18 @@ -13682,7 +13682,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 8c2cbad85c4fc3647f9498a5dab13c11490a77936dcf14cb769b5659b361e7070c7528bf0d8708eec06a6dbca93b34cccf5cc919005d33c645739865cfccb878 + checksum: 1117a7f866b103abf88a4087f5fe8b854d9c069c69444c592f8431e7d28c9b90423f7b50e550be0f2f173b7563e943bcc9238e80f6747181f81861275f6e2ce7 languageName: node linkType: hard From 07a4059901e8723cd3d986a0aefb216faa42b5fe Mon Sep 17 00:00:00 2001 From: Claire <claire.github-309c@sitedethib.com> Date: Mon, 13 Nov 2023 14:27:00 +0100 Subject: [PATCH 07/63] Add support for invite codes in the registration API (#27805) --- app/controllers/api/v1/accounts_controller.rb | 18 ++-- app/controllers/api/v1/invites_controller.rb | 30 ++++++ .../auth/registrations_controller.rb | 15 +-- app/helpers/registration_helper.rb | 21 +++++ app/services/app_sign_up_service.rb | 30 ++---- config/locales/en.yml | 1 + config/routes.rb | 2 + spec/requests/invite_spec.rb | 27 ++++++ spec/services/app_sign_up_service_spec.rb | 92 ++++++++++++------- 9 files changed, 158 insertions(+), 78 deletions(-) create mode 100644 app/controllers/api/v1/invites_controller.rb create mode 100644 app/helpers/registration_helper.rb create mode 100644 spec/requests/invite_spec.rb diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index ddb94d5ca..653529316 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Api::V1::AccountsController < Api::BaseController + include RegistrationHelper + before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :remove_from_followers, :block, :unblock, :mute, :unmute] before_action -> { doorkeeper_authorize! :follow, :write, :'write:follows' }, only: [:follow, :unfollow, :remove_from_followers] before_action -> { doorkeeper_authorize! :follow, :write, :'write:mutes' }, only: [:mute, :unmute] @@ -90,18 +92,14 @@ class Api::V1::AccountsController < Api::BaseController end def account_params - params.permit(:username, :email, :password, :agreement, :locale, :reason, :time_zone) + params.permit(:username, :email, :password, :agreement, :locale, :reason, :time_zone, :invite_code) + end + + def invite + Invite.find_by(code: params[:invite_code]) if params[:invite_code].present? end def check_enabled_registrations - forbidden if single_user_mode? || omniauth_only? || !allowed_registrations? - end - - def allowed_registrations? - Setting.registrations_mode != 'none' - end - - def omniauth_only? - ENV['OMNIAUTH_ONLY'] == 'true' + forbidden unless allowed_registration?(request.remote_ip, invite) end end diff --git a/app/controllers/api/v1/invites_controller.rb b/app/controllers/api/v1/invites_controller.rb new file mode 100644 index 000000000..ea17ba740 --- /dev/null +++ b/app/controllers/api/v1/invites_controller.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class Api::V1::InvitesController < Api::BaseController + include RegistrationHelper + + skip_before_action :require_authenticated_user! + skip_around_action :set_locale + + before_action :set_invite + before_action :check_enabled_registrations! + + # Override `current_user` to avoid reading session cookies + def current_user; end + + def show + render json: { invite_code: params[:invite_code], instance_api_url: api_v2_instance_url }, status: 200 + end + + private + + def set_invite + @invite = Invite.find_by!(code: params[:invite_code]) + end + + def check_enabled_registrations! + return render json: { error: I18n.t('invites.invalid') }, status: 401 unless @invite.valid_for_use? + + raise Mastodon::NotPermittedError unless allowed_registration?(request.remote_ip, @invite) + end +end diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index 331484f36..8be7c5f19 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Auth::RegistrationsController < Devise::RegistrationsController + include RegistrationHelper include RegistrationSpamConcern layout :determine_layout @@ -82,19 +83,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController end def check_enabled_registrations - redirect_to root_path if single_user_mode? || omniauth_only? || !allowed_registrations? || ip_blocked? - end - - def allowed_registrations? - Setting.registrations_mode != 'none' || @invite&.valid_for_use? - end - - def omniauth_only? - ENV['OMNIAUTH_ONLY'] == 'true' - end - - def ip_blocked? - IpBlock.where(severity: :sign_up_block).where('ip >>= ?', request.remote_ip.to_s).exists? + redirect_to root_path unless allowed_registration?(request.remote_ip, @invite) end def invite_code diff --git a/app/helpers/registration_helper.rb b/app/helpers/registration_helper.rb new file mode 100644 index 000000000..ef5462ac8 --- /dev/null +++ b/app/helpers/registration_helper.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module RegistrationHelper + extend ActiveSupport::Concern + + def allowed_registration?(remote_ip, invite) + !Rails.configuration.x.single_user_mode && !omniauth_only? && (registrations_open? || invite&.valid_for_use?) && !ip_blocked?(remote_ip) + end + + def registrations_open? + Setting.registrations_mode != 'none' + end + + def omniauth_only? + ENV['OMNIAUTH_ONLY'] == 'true' + end + + def ip_blocked?(remote_ip) + IpBlock.where(severity: :sign_up_block).exists?(['ip >>= ?', remote_ip.to_s]) + end +end diff --git a/app/services/app_sign_up_service.rb b/app/services/app_sign_up_service.rb index 94547b61b..766588011 100644 --- a/app/services/app_sign_up_service.rb +++ b/app/services/app_sign_up_service.rb @@ -1,12 +1,14 @@ # frozen_string_literal: true class AppSignUpService < BaseService + include RegistrationHelper + def call(app, remote_ip, params) @app = app @remote_ip = remote_ip @params = params - raise Mastodon::NotPermittedError unless allowed_registrations? + raise Mastodon::NotPermittedError unless allowed_registration?(remote_ip, invite) ApplicationRecord.transaction do create_user! @@ -34,8 +36,12 @@ class AppSignUpService < BaseService ) end + def invite + Invite.find_by(code: @params[:invite_code]) if @params[:invite_code].present? + end + def user_params - @params.slice(:email, :password, :agreement, :locale, :time_zone) + @params.slice(:email, :password, :agreement, :locale, :time_zone, :invite_code) end def account_params @@ -45,24 +51,4 @@ class AppSignUpService < BaseService def invite_request_params { text: @params[:reason] } end - - def allowed_registrations? - registrations_open? && !single_user_mode? && !omniauth_only? && !ip_blocked? - end - - def registrations_open? - Setting.registrations_mode != 'none' - end - - def single_user_mode? - Rails.configuration.x.single_user_mode - end - - def omniauth_only? - ENV['OMNIAUTH_ONLY'] == 'true' - end - - def ip_blocked? - IpBlock.where(severity: :sign_up_block).where('ip >>= ?', @remote_ip.to_s).exists? - end end diff --git a/config/locales/en.yml b/config/locales/en.yml index c298c47d3..7319de53d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1368,6 +1368,7 @@ en: '86400': 1 day expires_in_prompt: Never generate: Generate invite link + invalid: This invite is not valid invited_by: 'You were invited by:' max_uses: one: 1 use diff --git a/config/routes.rb b/config/routes.rb index 3adda3b82..82431f6ec 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -81,6 +81,8 @@ Rails.application.routes.draw do resource :outbox, only: [:show], module: :activitypub end + get '/invite/:invite_code', constraints: ->(req) { req.format == :json }, to: 'api/v1/invites#show' + devise_scope :user do get '/invite/:invite_code', to: 'auth/registrations#new', as: :public_invite diff --git a/spec/requests/invite_spec.rb b/spec/requests/invite_spec.rb new file mode 100644 index 000000000..c44ef2419 --- /dev/null +++ b/spec/requests/invite_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'invites' do + let(:invite) { Fabricate(:invite) } + + context 'when requesting a JSON document' do + it 'returns a JSON document with expected attributes' do + get "/invite/#{invite.code}", headers: { 'Accept' => 'application/activity+json' } + + expect(response).to have_http_status(200) + expect(response.media_type).to eq 'application/json' + + expect(body_as_json[:invite_code]).to eq invite.code + end + end + + context 'when not requesting a JSON document' do + it 'returns an HTML page' do + get "/invite/#{invite.code}" + + expect(response).to have_http_status(200) + expect(response.media_type).to eq 'text/html' + end + end +end diff --git a/spec/services/app_sign_up_service_spec.rb b/spec/services/app_sign_up_service_spec.rb index 253230496..d5946cf9b 100644 --- a/spec/services/app_sign_up_service_spec.rb +++ b/spec/services/app_sign_up_service_spec.rb @@ -10,46 +10,72 @@ RSpec.describe AppSignUpService, type: :service do let(:remote_ip) { IPAddr.new('198.0.2.1') } describe '#call' do - it 'returns nil when registrations are closed' do - tmp = Setting.registrations_mode - Setting.registrations_mode = 'none' - expect { subject.call(app, remote_ip, good_params) }.to raise_error Mastodon::NotPermittedError - Setting.registrations_mode = tmp + let(:params) { good_params } + + shared_examples 'successful registration' do + it 'creates an unconfirmed user with access token and the app\'s scope', :aggregate_failures do + access_token = subject.call(app, remote_ip, params) + expect(access_token).to_not be_nil + expect(access_token.scopes.to_s).to eq 'read write' + + user = User.find_by(id: access_token.resource_owner_id) + expect(user).to_not be_nil + expect(user.confirmed?).to be false + + expect(user.account).to_not be_nil + expect(user.invite_request).to be_nil + end + end + + context 'when registrations are closed' do + around do |example| + tmp = Setting.registrations_mode + Setting.registrations_mode = 'none' + + example.run + + Setting.registrations_mode = tmp + end + + it 'raises an error', :aggregate_failures do + expect { subject.call(app, remote_ip, good_params) }.to raise_error Mastodon::NotPermittedError + end + + context 'when using a valid invite' do + let(:params) { good_params.merge({ invite_code: invite.code }) } + let(:invite) { Fabricate(:invite) } + + before do + invite.user.approve! + end + + it_behaves_like 'successful registration' + end + + context 'when using an invalid invite' do + let(:params) { good_params.merge({ invite_code: invite.code }) } + let(:invite) { Fabricate(:invite, uses: 1, max_uses: 1) } + + it 'raises an error', :aggregate_failures do + expect { subject.call(app, remote_ip, params) }.to raise_error Mastodon::NotPermittedError + end + end end it 'raises an error when params are missing' do expect { subject.call(app, remote_ip, {}) }.to raise_error ActiveRecord::RecordInvalid end - it 'creates an unconfirmed user with access token' do - access_token = subject.call(app, remote_ip, good_params) - expect(access_token).to_not be_nil - user = User.find_by(id: access_token.resource_owner_id) - expect(user).to_not be_nil - expect(user.confirmed?).to be false - end + it_behaves_like 'successful registration' - it 'creates access token with the app\'s scopes' do - access_token = subject.call(app, remote_ip, good_params) - expect(access_token).to_not be_nil - expect(access_token.scopes.to_s).to eq 'read write' - end - - it 'creates an account' do - access_token = subject.call(app, remote_ip, good_params) - expect(access_token).to_not be_nil - user = User.find_by(id: access_token.resource_owner_id) - expect(user).to_not be_nil - expect(user.account).to_not be_nil - expect(user.invite_request).to be_nil - end - - it 'creates an account with invite request text' do - access_token = subject.call(app, remote_ip, good_params.merge(reason: 'Foo bar')) - expect(access_token).to_not be_nil - user = User.find_by(id: access_token.resource_owner_id) - expect(user).to_not be_nil - expect(user.invite_request&.text).to eq 'Foo bar' + context 'when given an invite request text' do + it 'creates an account with invite request text' do + access_token = subject.call(app, remote_ip, good_params.merge(reason: 'Foo bar')) + expect(access_token).to_not be_nil + user = User.find_by(id: access_token.resource_owner_id) + expect(user).to_not be_nil + expect(user.invite_request&.text).to eq 'Foo bar' + end end end end From ed79713f3ad20a78640f113d44454bab387a2d8c Mon Sep 17 00:00:00 2001 From: pajowu <pajowu@pajowu.de> Date: Mon, 13 Nov 2023 14:27:50 +0100 Subject: [PATCH 08/63] Fix modal content not being selectable (#27813) --- app/javascript/styles/mastodon/components.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index ae8e23f53..c8cfe46a8 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -5226,6 +5226,7 @@ a.status-card { .modal-root__modal { pointer-events: auto; + user-select: text; display: flex; } From 0c98a9d9becec126ea9a53619045dbb1b88f555a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 15:01:07 +0100 Subject: [PATCH 09/63] Update devDependencies (non-major) (#25612) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 828 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 434 insertions(+), 394 deletions(-) diff --git a/yarn.lock b/yarn.lock index c1e9ec9a4..891eb59fe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12,10 +12,10 @@ __metadata: languageName: node linkType: hard -"@adobe/css-tools@npm:^4.0.1": - version: 4.3.0 - resolution: "@adobe/css-tools@npm:4.3.0" - checksum: 70401c49ab6b7d715147c3637fc6467608276253150c1a5fa933ee45d7c1472b5188e31c1760d06c8a19c5bc2b2914583daa33486d7eac1e8fc7c6dc76a57b3c +"@adobe/css-tools@npm:^4.3.1": + version: 4.3.1 + resolution: "@adobe/css-tools@npm:4.3.1" + checksum: 05672719b544cc0c21ae3ed0eb6349bf458e9d09457578eeeb07cf0f696469ac6417e9c9be1b129e5d6a18098a061c1db55b2275591760ef30a79822436fcbfa languageName: node linkType: hard @@ -1538,29 +1538,29 @@ __metadata: languageName: node linkType: hard -"@csstools/css-parser-algorithms@npm:^2.3.0": - version: 2.3.1 - resolution: "@csstools/css-parser-algorithms@npm:2.3.1" +"@csstools/css-parser-algorithms@npm:^2.3.1": + version: 2.3.2 + resolution: "@csstools/css-parser-algorithms@npm:2.3.2" peerDependencies: - "@csstools/css-tokenizer": ^2.2.0 - checksum: 0f1688cc5de75f41af4581a0b4df994e9af90f6df2b3f962e0680c4ed8e2aa32b23fbf3ba4fdaffc09a9afcf93fcf13a9743204f179bab57a7603ce88cb635e8 + "@csstools/css-tokenizer": ^2.2.1 + checksum: ccae373a3ab5c10716418b69ce1f6db10a26d3a2d60b65df5fe69099afe4fb1d3192925f3c0f93c3b17c3ab1964b0f39ad2b0e97312ec4a51caa55d6b6a31672 languageName: node linkType: hard -"@csstools/css-tokenizer@npm:^2.1.1": - version: 2.2.0 - resolution: "@csstools/css-tokenizer@npm:2.2.0" - checksum: 7a6178d5a148e426ea79d4b2761857daacd7cde00512b45697146228d59183b0043f9803b48be55299f5a331f07ff14477612c608f18df7550ee642467d74564 +"@csstools/css-tokenizer@npm:^2.2.0": + version: 2.2.1 + resolution: "@csstools/css-tokenizer@npm:2.2.1" + checksum: 0c6901d291e99c567893846a47068057c2a28b3edc4219b6da589a530f55f51ddd4675f906f707b393bfe7a508ab2604bf3f75708f064db857bb277636bd5a44 languageName: node linkType: hard -"@csstools/media-query-list-parser@npm:^2.1.2": - version: 2.1.4 - resolution: "@csstools/media-query-list-parser@npm:2.1.4" +"@csstools/media-query-list-parser@npm:^2.1.4": + version: 2.1.5 + resolution: "@csstools/media-query-list-parser@npm:2.1.5" peerDependencies: - "@csstools/css-parser-algorithms": ^2.3.1 - "@csstools/css-tokenizer": ^2.2.0 - checksum: a796ebc8df7d1c8bc2bb71f152b6bedd540bd6679a33a50c1d99f8c383a24d79e9cb8fa59b2ad8f39da12c9d4501fd799caa16da542c23b8dff8257b0b5a0ea7 + "@csstools/css-parser-algorithms": ^2.3.2 + "@csstools/css-tokenizer": ^2.2.1 + checksum: ae0692c6f92cdc82053291c7a50028b692094dfed795f0259571c5eb40f4b3fa580182ac3701e56c2834e40a62a122ea6639299e43ae88b3a835ae4c869a1a12 languageName: node linkType: hard @@ -1777,16 +1777,16 @@ __metadata: linkType: hard "@formatjs/cli@npm:^6.1.1": - version: 6.1.3 - resolution: "@formatjs/cli@npm:6.1.3" + version: 6.2.3 + resolution: "@formatjs/cli@npm:6.2.3" peerDependencies: - "@vue/compiler-sfc": ^3.2.34 + vue: ^3.3.4 peerDependenciesMeta: - "@vue/compiler-sfc": + vue: optional: true bin: formatjs: bin/formatjs - checksum: d7f069b4813c7c1a4fd7e16906da6bc024553c5df3d3ebb0b3da12ec23bb763d184c0424c562bc7ef89d62a963dadd114dd5acb5acec8f0c9160a36bb79f05c2 + checksum: 91eada7676333e2e647cbfbf9c0da88e4ca52e7b486dca73a7299594b0b0dea99de00e1b4110fac993633feb4bf5f26c97885b1a870dfd0ef95688d7f3234a03 languageName: node linkType: hard @@ -2061,50 +2061,50 @@ __metadata: languageName: node linkType: hard -"@jest/console@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/console@npm:29.6.2" +"@jest/console@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/console@npm:29.7.0" dependencies: - "@jest/types": "npm:^29.6.1" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" chalk: "npm:^4.0.0" - jest-message-util: "npm:^29.6.2" - jest-util: "npm:^29.6.2" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" slash: "npm:^3.0.0" - checksum: cdd97d89df5e3e84ba7927ba58a297eb0eae25190575299fede876f7d09a08dc120094be08e49bf01859c54053470215194c0d9a64fc56beb735c5de4de8c5a8 + checksum: 7be408781d0a6f657e969cbec13b540c329671819c2f57acfad0dae9dbfe2c9be859f38fe99b35dba9ff1536937dc6ddc69fdcd2794812fa3c647a1619797f6c languageName: node linkType: hard -"@jest/core@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/core@npm:29.6.2" +"@jest/core@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/core@npm:29.7.0" dependencies: - "@jest/console": "npm:^29.6.2" - "@jest/reporters": "npm:^29.6.2" - "@jest/test-result": "npm:^29.6.2" - "@jest/transform": "npm:^29.6.2" - "@jest/types": "npm:^29.6.1" + "@jest/console": "npm:^29.7.0" + "@jest/reporters": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" ansi-escapes: "npm:^4.2.1" chalk: "npm:^4.0.0" ci-info: "npm:^3.2.0" exit: "npm:^0.1.2" graceful-fs: "npm:^4.2.9" - jest-changed-files: "npm:^29.5.0" - jest-config: "npm:^29.6.2" - jest-haste-map: "npm:^29.6.2" - jest-message-util: "npm:^29.6.2" - jest-regex-util: "npm:^29.4.3" - jest-resolve: "npm:^29.6.2" - jest-resolve-dependencies: "npm:^29.6.2" - jest-runner: "npm:^29.6.2" - jest-runtime: "npm:^29.6.2" - jest-snapshot: "npm:^29.6.2" - jest-util: "npm:^29.6.2" - jest-validate: "npm:^29.6.2" - jest-watcher: "npm:^29.6.2" + jest-changed-files: "npm:^29.7.0" + jest-config: "npm:^29.7.0" + jest-haste-map: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-regex-util: "npm:^29.6.3" + jest-resolve: "npm:^29.7.0" + jest-resolve-dependencies: "npm:^29.7.0" + jest-runner: "npm:^29.7.0" + jest-runtime: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" + jest-watcher: "npm:^29.7.0" micromatch: "npm:^4.0.4" - pretty-format: "npm:^29.6.2" + pretty-format: "npm:^29.7.0" slash: "npm:^3.0.0" strip-ansi: "npm:^6.0.0" peerDependencies: @@ -2112,23 +2112,23 @@ __metadata: peerDependenciesMeta: node-notifier: optional: true - checksum: 066fc9dc66bb3785c2670280f05cb4f01a776a2d88bc6106ad4224e4a1064b1dbe3752545b4d744d6e0e3203fb0a2a102e9864104f160f2266fd30e756d9d693 + checksum: 934f7bf73190f029ac0f96662c85cd276ec460d407baf6b0dbaec2872e157db4d55a7ee0b1c43b18874602f662b37cb973dda469a4e6d88b4e4845b521adeeb2 languageName: node linkType: hard -"@jest/environment@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/environment@npm:29.6.2" +"@jest/environment@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/environment@npm:29.7.0" dependencies: - "@jest/fake-timers": "npm:^29.6.2" - "@jest/types": "npm:^29.6.1" + "@jest/fake-timers": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" - jest-mock: "npm:^29.6.2" - checksum: 82f040b4f729e1a3ab9e61b33e009a7b4ccf572ff94fc157e6fe8ecd267c8af53c8c02853bfe7f023d0f6bf35edf06b6bc7873efc433f335a3774b6c2445662d + jest-mock: "npm:^29.7.0" + checksum: c7b1b40c618f8baf4d00609022d2afa086d9c6acc706f303a70bb4b67275868f620ad2e1a9efc5edd418906157337cce50589a627a6400bbdf117d351b91ef86 languageName: node linkType: hard -"@jest/expect-utils@npm:^29.6.2, @jest/expect-utils@npm:^29.7.0": +"@jest/expect-utils@npm:^29.7.0": version: 29.7.0 resolution: "@jest/expect-utils@npm:29.7.0" dependencies: @@ -2137,51 +2137,51 @@ __metadata: languageName: node linkType: hard -"@jest/expect@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/expect@npm:29.6.2" +"@jest/expect@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/expect@npm:29.7.0" dependencies: - expect: "npm:^29.6.2" - jest-snapshot: "npm:^29.6.2" - checksum: 2cd9a5613b1bae5300dd16d76c7790d1d3b43cb55654dc2b64b202d1522bb03844f88c7bc60b72e3095c8479873ade91009ab0cb8a851842dab00d4d9fc1e3cb + expect: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + checksum: b41f193fb697d3ced134349250aed6ccea075e48c4f803159db102b826a4e473397c68c31118259868fd69a5cba70e97e1c26d2c2ff716ca39dc73a2ccec037e languageName: node linkType: hard -"@jest/fake-timers@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/fake-timers@npm:29.6.2" +"@jest/fake-timers@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/fake-timers@npm:29.7.0" dependencies: - "@jest/types": "npm:^29.6.1" + "@jest/types": "npm:^29.6.3" "@sinonjs/fake-timers": "npm:^10.0.2" "@types/node": "npm:*" - jest-message-util: "npm:^29.6.2" - jest-mock: "npm:^29.6.2" - jest-util: "npm:^29.6.2" - checksum: 4f333b7f8f6bc8e0549e3838e68c3859de1faa3e0639f8ede2786602ec1c237f4691e7bd13649b308ddfaf3fd5aa6b75067fe34f6b6dfa9c0b570773611e0e73 + jest-message-util: "npm:^29.7.0" + jest-mock: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + checksum: cf0a8bcda801b28dc2e2b2ba36302200ee8104a45ad7a21e6c234148932f826cb3bc57c8df3b7b815aeea0861d7b6ca6f0d4778f93b9219398ef28749e03595c languageName: node linkType: hard -"@jest/globals@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/globals@npm:29.6.2" +"@jest/globals@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/globals@npm:29.7.0" dependencies: - "@jest/environment": "npm:^29.6.2" - "@jest/expect": "npm:^29.6.2" - "@jest/types": "npm:^29.6.1" - jest-mock: "npm:^29.6.2" - checksum: 3ee73f13d51a08b9fe3bc39305a3b9c0259a7610d89f17b9579684b80bdff3e079adc81d6aec298f5ebe07b43ba0dfdb305be2747b9dc87aa7f337bddc83fedc + "@jest/environment": "npm:^29.7.0" + "@jest/expect": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + jest-mock: "npm:^29.7.0" + checksum: a385c99396878fe6e4460c43bd7bb0a5cc52befb462cc6e7f2a3810f9e7bcce7cdeb51908fd530391ee452dc856c98baa2c5f5fa8a5b30b071d31ef7f6955cea languageName: node linkType: hard -"@jest/reporters@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/reporters@npm:29.6.2" +"@jest/reporters@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/reporters@npm:29.7.0" dependencies: "@bcoe/v8-coverage": "npm:^0.2.3" - "@jest/console": "npm:^29.6.2" - "@jest/test-result": "npm:^29.6.2" - "@jest/transform": "npm:^29.6.2" - "@jest/types": "npm:^29.6.1" + "@jest/console": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" "@jridgewell/trace-mapping": "npm:^0.3.18" "@types/node": "npm:*" chalk: "npm:^4.0.0" @@ -2190,13 +2190,13 @@ __metadata: glob: "npm:^7.1.3" graceful-fs: "npm:^4.2.9" istanbul-lib-coverage: "npm:^3.0.0" - istanbul-lib-instrument: "npm:^5.1.0" + istanbul-lib-instrument: "npm:^6.0.0" istanbul-lib-report: "npm:^3.0.0" istanbul-lib-source-maps: "npm:^4.0.0" istanbul-reports: "npm:^3.1.3" - jest-message-util: "npm:^29.6.2" - jest-util: "npm:^29.6.2" - jest-worker: "npm:^29.6.2" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-worker: "npm:^29.7.0" slash: "npm:^3.0.0" string-length: "npm:^4.0.1" strip-ansi: "npm:^6.0.0" @@ -2206,7 +2206,7 @@ __metadata: peerDependenciesMeta: node-notifier: optional: true - checksum: d4321978208fa8c64ff4e14694508ec8fa5712801b66db62a6c58456798ffc2fab5761db24b1c3596664f2ad0862fcabc69927f0ed54cc9f219689a77cc7db4a + checksum: a754402a799541c6e5aff2c8160562525e2a47e7d568f01ebfc4da66522de39cbb809bbb0a841c7052e4270d79214e70aec3c169e4eae42a03bc1a8a20cb9fa2 languageName: node linkType: hard @@ -2219,65 +2219,65 @@ __metadata: languageName: node linkType: hard -"@jest/source-map@npm:^29.6.0": - version: 29.6.0 - resolution: "@jest/source-map@npm:29.6.0" +"@jest/source-map@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/source-map@npm:29.6.3" dependencies: "@jridgewell/trace-mapping": "npm:^0.3.18" callsites: "npm:^3.0.0" graceful-fs: "npm:^4.2.9" - checksum: afa654e3634ad74d5f8388ccffd7ecbd745bdce7f6f0860b69c07827c3ee5bb408f52b6c3136b43157ef5874c099059484e43bd3aa391232ab27d8c330399789 + checksum: a2f177081830a2e8ad3f2e29e20b63bd40bade294880b595acf2fc09ec74b6a9dd98f126a2baa2bf4941acd89b13a4ade5351b3885c224107083a0059b60a219 languageName: node linkType: hard -"@jest/test-result@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/test-result@npm:29.6.2" +"@jest/test-result@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-result@npm:29.7.0" dependencies: - "@jest/console": "npm:^29.6.2" - "@jest/types": "npm:^29.6.1" + "@jest/console": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" "@types/istanbul-lib-coverage": "npm:^2.0.0" collect-v8-coverage: "npm:^1.0.0" - checksum: 9c373db297d6cc4b23b143592b3121da047890ba3609115e4db7b94220095d5e32a11f7179ca3dfa1ab29fa30a5e51cbdbbdf58dcd8ef3216e92e86d2aa3251c + checksum: 7de54090e54a674ca173470b55dc1afdee994f2d70d185c80236003efd3fa2b753fff51ffcdda8e2890244c411fd2267529d42c4a50a8303755041ee493e6a04 languageName: node linkType: hard -"@jest/test-sequencer@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/test-sequencer@npm:29.6.2" +"@jest/test-sequencer@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-sequencer@npm:29.7.0" dependencies: - "@jest/test-result": "npm:^29.6.2" + "@jest/test-result": "npm:^29.7.0" graceful-fs: "npm:^4.2.9" - jest-haste-map: "npm:^29.6.2" + jest-haste-map: "npm:^29.7.0" slash: "npm:^3.0.0" - checksum: dc6a37f0eb93a72ce76a5100f8adb97e40bb0ab9b6f49b07476e0b83b07d1366803185b3d64da2219448312fa78a687f473f54e0c1da08efc4d2e1cb2d3c1dfb + checksum: 593a8c4272797bb5628984486080cbf57aed09c7cfdc0a634e8c06c38c6bef329c46c0016e84555ee55d1cd1f381518cf1890990ff845524c1123720c8c1481b languageName: node linkType: hard -"@jest/transform@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/transform@npm:29.6.2" +"@jest/transform@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/transform@npm:29.7.0" dependencies: "@babel/core": "npm:^7.11.6" - "@jest/types": "npm:^29.6.1" + "@jest/types": "npm:^29.6.3" "@jridgewell/trace-mapping": "npm:^0.3.18" babel-plugin-istanbul: "npm:^6.1.1" chalk: "npm:^4.0.0" convert-source-map: "npm:^2.0.0" fast-json-stable-stringify: "npm:^2.1.0" graceful-fs: "npm:^4.2.9" - jest-haste-map: "npm:^29.6.2" - jest-regex-util: "npm:^29.4.3" - jest-util: "npm:^29.6.2" + jest-haste-map: "npm:^29.7.0" + jest-regex-util: "npm:^29.6.3" + jest-util: "npm:^29.7.0" micromatch: "npm:^4.0.4" pirates: "npm:^4.0.4" slash: "npm:^3.0.0" write-file-atomic: "npm:^4.0.2" - checksum: dce3a28ca01ce78923bb0faf7ff4fa6e64f1ec77a729a89f874b05a98c8f4408df52fc41a9e39755e9490660163ecebb58d2364530391a443fc2e4bd0e4195d6 + checksum: 7f4a7f73dcf45dfdf280c7aa283cbac7b6e5a904813c3a93ead7e55873761fc20d5c4f0191d2019004fac6f55f061c82eb3249c2901164ad80e362e7a7ede5a6 languageName: node linkType: hard -"@jest/types@npm:^29.6.1, @jest/types@npm:^29.6.3": +"@jest/types@npm:^29.6.3": version: 29.6.3 resolution: "@jest/types@npm:29.6.3" dependencies: @@ -2941,10 +2941,10 @@ __metadata: linkType: hard "@testing-library/jest-dom@npm:^6.0.0": - version: 6.0.0 - resolution: "@testing-library/jest-dom@npm:6.0.0" + version: 6.1.4 + resolution: "@testing-library/jest-dom@npm:6.1.4" dependencies: - "@adobe/css-tools": "npm:^4.0.1" + "@adobe/css-tools": "npm:^4.3.1" "@babel/runtime": "npm:^7.9.2" aria-query: "npm:^5.0.0" chalk: "npm:^3.0.0" @@ -2966,13 +2966,13 @@ __metadata: optional: true vitest: optional: true - checksum: dd5a7ff79dad99b08195cab8d7b818b3de0d02f6c84b5d28d5dbca265ceb867931a0ff79da2279a2d476db1cdafb81c14255d898f0bd1ace9d0b36896368cf96 + checksum: 2e23f120613fd8ae6d5169bbc94f1a2e4c82b07182057dc94db8ec54ebf32555833442e6c43a187e59715d83704ffb5df49ba88a71f6f32d2683f3d95ba721c7 languageName: node linkType: hard "@testing-library/react@npm:^14.0.0": - version: 14.0.0 - resolution: "@testing-library/react@npm:14.0.0" + version: 14.1.0 + resolution: "@testing-library/react@npm:14.1.0" dependencies: "@babel/runtime": "npm:^7.12.5" "@testing-library/dom": "npm:^9.0.0" @@ -2980,7 +2980,7 @@ __metadata: peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 81035913024faf18ba7e163418af517b2c3b85aef496fbd6334bda38f6f6dd4072678c6b76c41148b46b7fc846764f875e1156cbfc7643ffa1b62ee069d78951 + checksum: 357ad80b11bdd4b6d10d2fb1bf86d5b39fb457cf09293033cf42bcc7a95738a86a2b12b760ae15bad326da0b9c074ca015d2bbf0baae7da38fdbc7c808925820 languageName: node linkType: hard @@ -4702,20 +4702,20 @@ __metadata: languageName: node linkType: hard -"babel-jest@npm:^29.5.0, babel-jest@npm:^29.6.2": - version: 29.6.2 - resolution: "babel-jest@npm:29.6.2" +"babel-jest@npm:^29.5.0, babel-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "babel-jest@npm:29.7.0" dependencies: - "@jest/transform": "npm:^29.6.2" + "@jest/transform": "npm:^29.7.0" "@types/babel__core": "npm:^7.1.14" babel-plugin-istanbul: "npm:^6.1.1" - babel-preset-jest: "npm:^29.5.0" + babel-preset-jest: "npm:^29.6.3" chalk: "npm:^4.0.0" graceful-fs: "npm:^4.2.9" slash: "npm:^3.0.0" peerDependencies: "@babel/core": ^7.8.0 - checksum: c1ebaecd1323852867765a6920ab8b5e1e4236254415090a682e0ebf6a3339a9861f65791b23acad2e3a4c4bf5bca31c9abc154306ba7cf9725c2f6e78c92444 + checksum: 2eda9c1391e51936ca573dd1aedfee07b14c59b33dbe16ef347873ddd777bcf6e2fc739681e9e9661ab54ef84a3109a03725be2ac32cd2124c07ea4401cbe8c1 languageName: node linkType: hard @@ -4766,15 +4766,15 @@ __metadata: languageName: node linkType: hard -"babel-plugin-jest-hoist@npm:^29.5.0": - version: 29.5.0 - resolution: "babel-plugin-jest-hoist@npm:29.5.0" +"babel-plugin-jest-hoist@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-plugin-jest-hoist@npm:29.6.3" dependencies: "@babel/template": "npm:^7.3.3" "@babel/types": "npm:^7.3.3" "@types/babel__core": "npm:^7.1.14" "@types/babel__traverse": "npm:^7.0.6" - checksum: 385547c4d81647848dc3e86fecf4381032be99ed97d87aee78d422631f651042600371ee31e37ec9bb6f4a0a4f296b3b5798d69c410626ea94eae76d9c64da63 + checksum: 7e6451caaf7dce33d010b8aafb970e62f1b0c0b57f4978c37b0d457bbcf0874d75a395a102daf0bae0bd14eafb9f6e9a165ee5e899c0a4f1f3bb2e07b304ed2e languageName: node linkType: hard @@ -4892,15 +4892,15 @@ __metadata: languageName: node linkType: hard -"babel-preset-jest@npm:^29.5.0": - version: 29.5.0 - resolution: "babel-preset-jest@npm:29.5.0" +"babel-preset-jest@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-preset-jest@npm:29.6.3" dependencies: - babel-plugin-jest-hoist: "npm:^29.5.0" + babel-plugin-jest-hoist: "npm:^29.6.3" babel-preset-current-node-syntax: "npm:^1.0.0" peerDependencies: "@babel/core": ^7.0.0 - checksum: 752b8682c8cf55bca46d870003f4ce43a4ba0fcaa1138ff7f0e02340628e221810b0c2c3e77a7d5070168dc163eb11907f6c9256f187242abe0f14219d1f6b12 + checksum: ec5fd0276b5630b05f0c14bb97cc3815c6b31600c683ebb51372e54dcb776cff790bdeeabd5b8d01ede375a040337ccbf6a3ccd68d3a34219125945e167ad943 languageName: node linkType: hard @@ -6067,6 +6067,23 @@ __metadata: languageName: node linkType: hard +"create-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "create-jest@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + chalk: "npm:^4.0.0" + exit: "npm:^0.1.2" + graceful-fs: "npm:^4.2.9" + jest-config: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + prompts: "npm:^2.0.1" + bin: + create-jest: bin/create-jest.js + checksum: e7e54c280692470d3398f62a6238fd396327e01c6a0757002833f06d00afc62dd7bfe04ff2b9cd145264460e6b4d1eb8386f2925b7e567f97939843b7b0e812f + languageName: node + linkType: hard + "cross-env@npm:^7.0.3": version: 7.0.3 resolution: "cross-env@npm:7.0.3" @@ -6138,10 +6155,10 @@ __metadata: languageName: node linkType: hard -"css-functions-list@npm:^3.2.0": - version: 3.2.0 - resolution: "css-functions-list@npm:3.2.0" - checksum: 640a1760c6d63536def671b7ccd89c2525a7197fc845a5ae1ec9f380fedd1aa9634f547db81f02f1a3736492867e9333ee8d6bf9aa498d211e36feae0e71a672 +"css-functions-list@npm:^3.2.1": + version: 3.2.1 + resolution: "css-functions-list@npm:3.2.1" + checksum: e6e2d9580437ad6df9f2cf18cff3f941691ec5cbbaebd4cb17a5da40d8d5dac50004807ddd05c00a121d2f21a224e2c5d339fe8e13614af21c00181d7d1c22b9 languageName: node linkType: hard @@ -7820,7 +7837,7 @@ __metadata: languageName: node linkType: hard -"expect@npm:^29.0.0, expect@npm:^29.6.2": +"expect@npm:^29.0.0, expect@npm:^29.7.0": version: 29.7.0 resolution: "expect@npm:29.7.0" dependencies: @@ -7998,6 +8015,15 @@ __metadata: languageName: node linkType: hard +"file-entry-cache@npm:^7.0.0": + version: 7.0.1 + resolution: "file-entry-cache@npm:7.0.1" + dependencies: + flat-cache: "npm:^3.1.1" + checksum: fc0e4f830777e07087f97da9a6734820fdffa2945583355433f40d9819dd97b89f16ac87c07118737a6bc3eb9cf4bd896e7b38b07f0768aefcf44da33e797363 + languageName: node + linkType: hard + "file-loader@npm:^6.2.0": version: 6.2.0 resolution: "file-loader@npm:6.2.0" @@ -8128,14 +8154,14 @@ __metadata: languageName: node linkType: hard -"flat-cache@npm:^3.0.4": - version: 3.1.1 - resolution: "flat-cache@npm:3.1.1" +"flat-cache@npm:^3.0.4, flat-cache@npm:^3.1.1": + version: 3.2.0 + resolution: "flat-cache@npm:3.2.0" dependencies: flatted: "npm:^3.2.9" keyv: "npm:^4.5.3" rimraf: "npm:^3.0.2" - checksum: 15f7f854830089a903ea660809b67ee25632b8b1965da6a328d3dc59d451abe2e9f16ad0b7523571ece2b5424d1e1979469ba25870f76f49ce3bbffc836072ef + checksum: b76f611bd5f5d68f7ae632e3ae503e678d205cf97a17c6ab5b12f6ca61188b5f1f7464503efae6dc18683ed8f0b41460beb48ac4b9ac63fe6201296a91ba2f75 languageName: node linkType: hard @@ -9955,7 +9981,7 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-instrument@npm:^5.0.4, istanbul-lib-instrument@npm:^5.1.0": +"istanbul-lib-instrument@npm:^5.0.4": version: 5.2.1 resolution: "istanbul-lib-instrument@npm:5.2.1" dependencies: @@ -9968,6 +9994,19 @@ __metadata: languageName: node linkType: hard +"istanbul-lib-instrument@npm:^6.0.0": + version: 6.0.1 + resolution: "istanbul-lib-instrument@npm:6.0.1" + dependencies: + "@babel/core": "npm:^7.12.3" + "@babel/parser": "npm:^7.14.7" + "@istanbuljs/schema": "npm:^0.1.2" + istanbul-lib-coverage: "npm:^3.2.0" + semver: "npm:^7.5.4" + checksum: 313d61aca3f82a04ad9377841d05061d603ea3d4a4dd281fdda2479ec4ddbc86dc1792c73651f21c93480570d1ecadc5f63011e2df86f30ee662b62c0c00e3d8 + languageName: node + linkType: hard + "istanbul-lib-report@npm:^3.0.0": version: 3.0.1 resolution: "istanbul-lib-report@npm:3.0.1" @@ -10040,59 +10079,59 @@ __metadata: languageName: node linkType: hard -"jest-changed-files@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-changed-files@npm:29.5.0" +"jest-changed-files@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-changed-files@npm:29.7.0" dependencies: execa: "npm:^5.0.0" + jest-util: "npm:^29.7.0" p-limit: "npm:^3.1.0" - checksum: 96334c78507a13c0f11f1360d893ade78fba7fd169825ca4acf7565156ceddd89b952be81c00378fa87ab642d3f44902c34a20f21b561e985e79f6e81fa7e9a8 + checksum: e071384d9e2f6bb462231ac53f29bff86f0e12394c1b49ccafbad225ce2ab7da226279a8a94f421949920bef9be7ef574fd86aee22e8adfa149be73554ab828b languageName: node linkType: hard -"jest-circus@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-circus@npm:29.6.2" +"jest-circus@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-circus@npm:29.7.0" dependencies: - "@jest/environment": "npm:^29.6.2" - "@jest/expect": "npm:^29.6.2" - "@jest/test-result": "npm:^29.6.2" - "@jest/types": "npm:^29.6.1" + "@jest/environment": "npm:^29.7.0" + "@jest/expect": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" chalk: "npm:^4.0.0" co: "npm:^4.6.0" dedent: "npm:^1.0.0" is-generator-fn: "npm:^2.0.0" - jest-each: "npm:^29.6.2" - jest-matcher-utils: "npm:^29.6.2" - jest-message-util: "npm:^29.6.2" - jest-runtime: "npm:^29.6.2" - jest-snapshot: "npm:^29.6.2" - jest-util: "npm:^29.6.2" + jest-each: "npm:^29.7.0" + jest-matcher-utils: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-runtime: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + jest-util: "npm:^29.7.0" p-limit: "npm:^3.1.0" - pretty-format: "npm:^29.6.2" + pretty-format: "npm:^29.7.0" pure-rand: "npm:^6.0.0" slash: "npm:^3.0.0" stack-utils: "npm:^2.0.3" - checksum: 04f3176bcc3adf0a5d5895f3ce2cb86fafa5d0d03d246cddd0a39021ec4bbc1092ef30792a9d8cdfb1cb6fcee75a277354d65aef6ca8c364fd3747d8ce67e255 + checksum: 8d15344cf7a9f14e926f0deed64ed190c7a4fa1ed1acfcd81e4cc094d3cc5bf7902ebb7b874edc98ada4185688f90c91e1747e0dfd7ac12463b097968ae74b5e languageName: node linkType: hard -"jest-cli@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-cli@npm:29.6.2" +"jest-cli@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-cli@npm:29.7.0" dependencies: - "@jest/core": "npm:^29.6.2" - "@jest/test-result": "npm:^29.6.2" - "@jest/types": "npm:^29.6.1" + "@jest/core": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" chalk: "npm:^4.0.0" + create-jest: "npm:^29.7.0" exit: "npm:^0.1.2" - graceful-fs: "npm:^4.2.9" import-local: "npm:^3.0.2" - jest-config: "npm:^29.6.2" - jest-util: "npm:^29.6.2" - jest-validate: "npm:^29.6.2" - prompts: "npm:^2.0.1" + jest-config: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" yargs: "npm:^17.3.1" peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -10101,34 +10140,34 @@ __metadata: optional: true bin: jest: bin/jest.js - checksum: 76d359427a573821b0b4f80a8b752e54778b8da1e09e737ae1ff5c29487d762a6f0d16becd5c1d2017cd337295945be82448539f90d04d173c72ee577c6cf897 + checksum: a658fd55050d4075d65c1066364595962ead7661711495cfa1dfeecf3d6d0a8ffec532f3dbd8afbb3e172dd5fd2fb2e813c5e10256e7cf2fea766314942fb43a languageName: node linkType: hard -"jest-config@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-config@npm:29.6.2" +"jest-config@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-config@npm:29.7.0" dependencies: "@babel/core": "npm:^7.11.6" - "@jest/test-sequencer": "npm:^29.6.2" - "@jest/types": "npm:^29.6.1" - babel-jest: "npm:^29.6.2" + "@jest/test-sequencer": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + babel-jest: "npm:^29.7.0" chalk: "npm:^4.0.0" ci-info: "npm:^3.2.0" deepmerge: "npm:^4.2.2" glob: "npm:^7.1.3" graceful-fs: "npm:^4.2.9" - jest-circus: "npm:^29.6.2" - jest-environment-node: "npm:^29.6.2" - jest-get-type: "npm:^29.4.3" - jest-regex-util: "npm:^29.4.3" - jest-resolve: "npm:^29.6.2" - jest-runner: "npm:^29.6.2" - jest-util: "npm:^29.6.2" - jest-validate: "npm:^29.6.2" + jest-circus: "npm:^29.7.0" + jest-environment-node: "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + jest-regex-util: "npm:^29.6.3" + jest-resolve: "npm:^29.7.0" + jest-runner: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" micromatch: "npm:^4.0.4" parse-json: "npm:^5.2.0" - pretty-format: "npm:^29.6.2" + pretty-format: "npm:^29.7.0" slash: "npm:^3.0.0" strip-json-comments: "npm:^3.1.1" peerDependencies: @@ -10139,11 +10178,11 @@ __metadata: optional: true ts-node: optional: true - checksum: 334b8cf02c9c9f5f3685fd6f673d634691a370c9a96f1a855234c7513c409a1cc842f2c8e786da9ef8734d33b6ee95d7b7b4d586c1a4f22bcae59118755d7d2a + checksum: bab23c2eda1fff06e0d104b00d6adfb1d1aabb7128441899c9bff2247bd26710b050a5364281ce8d52b46b499153bf7e3ee88b19831a8f3451f1477a0246a0f1 languageName: node linkType: hard -"jest-diff@npm:^29.6.2, jest-diff@npm:^29.7.0": +"jest-diff@npm:^29.7.0": version: 29.7.0 resolution: "jest-diff@npm:29.7.0" dependencies: @@ -10155,104 +10194,104 @@ __metadata: languageName: node linkType: hard -"jest-docblock@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-docblock@npm:29.4.3" +"jest-docblock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-docblock@npm:29.7.0" dependencies: detect-newline: "npm:^3.0.0" - checksum: 25cdea8fe77ff09d958abd347e26dcd8766ca69d9935bc626a89d694c91d33be06d4c088b02e4b3f143f532f726a10dff0bfe1e2387a0972a95addf5d64ed407 + checksum: d932a8272345cf6b6142bb70a2bb63e0856cc0093f082821577ea5bdf4643916a98744dfc992189d2b1417c38a11fa42466f6111526bc1fb81366f56410f3be9 languageName: node linkType: hard -"jest-each@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-each@npm:29.6.2" +"jest-each@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-each@npm:29.7.0" dependencies: - "@jest/types": "npm:^29.6.1" + "@jest/types": "npm:^29.6.3" chalk: "npm:^4.0.0" - jest-get-type: "npm:^29.4.3" - jest-util: "npm:^29.6.2" - pretty-format: "npm:^29.6.2" - checksum: b586f5c811011589308f2d8e0d5e596fa26d101e1116b55c624342327b932d3644aac37ce7b6c4eb8ef018893d2a41610ed7edbabfe125c3b46cf9a2b0f03d9b + jest-get-type: "npm:^29.6.3" + jest-util: "npm:^29.7.0" + pretty-format: "npm:^29.7.0" + checksum: f7f9a90ebee80cc688e825feceb2613627826ac41ea76a366fa58e669c3b2403d364c7c0a74d862d469b103c843154f8456d3b1c02b487509a12afa8b59edbb4 languageName: node linkType: hard "jest-environment-jsdom@npm:^29.5.0": - version: 29.6.2 - resolution: "jest-environment-jsdom@npm:29.6.2" + version: 29.7.0 + resolution: "jest-environment-jsdom@npm:29.7.0" dependencies: - "@jest/environment": "npm:^29.6.2" - "@jest/fake-timers": "npm:^29.6.2" - "@jest/types": "npm:^29.6.1" + "@jest/environment": "npm:^29.7.0" + "@jest/fake-timers": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" "@types/jsdom": "npm:^20.0.0" "@types/node": "npm:*" - jest-mock: "npm:^29.6.2" - jest-util: "npm:^29.6.2" + jest-mock: "npm:^29.7.0" + jest-util: "npm:^29.7.0" jsdom: "npm:^20.0.0" peerDependencies: canvas: ^2.5.0 peerDependenciesMeta: canvas: optional: true - checksum: 7f5885f6fa42d7d14d7dbdc58186283d3ed409a0abc8289bf365a4a2e92ea84d0eeb2087cd5ce6db39394652d817e4c7505d28555e2594309a324634d2b45718 + checksum: 139b94e2c8ec1bb5a46ce17df5211da65ce867354b3fd4e00fa6a0d1da95902df4cf7881273fc6ea937e5c325d39d6773f0d41b6c469363334de9d489d2c321f languageName: node linkType: hard -"jest-environment-node@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-environment-node@npm:29.6.2" +"jest-environment-node@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-environment-node@npm:29.7.0" dependencies: - "@jest/environment": "npm:^29.6.2" - "@jest/fake-timers": "npm:^29.6.2" - "@jest/types": "npm:^29.6.1" + "@jest/environment": "npm:^29.7.0" + "@jest/fake-timers": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" - jest-mock: "npm:^29.6.2" - jest-util: "npm:^29.6.2" - checksum: fea7c71e2b6ef901679983809918f670551d0122380f60695df554ca1dc9a065ec347e14c516c9b5a184494572320cd1696bd5bc817853a3e6cdb89b44d4054e + jest-mock: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + checksum: 61f04fec077f8b1b5c1a633e3612fc0c9aa79a0ab7b05600683428f1e01a4d35346c474bde6f439f9fcc1a4aa9a2861ff852d079a43ab64b02105d1004b2592b languageName: node linkType: hard -"jest-get-type@npm:^29.4.3, jest-get-type@npm:^29.6.3": +"jest-get-type@npm:^29.6.3": version: 29.6.3 resolution: "jest-get-type@npm:29.6.3" checksum: 552e7a97a983d3c2d4e412a44eb7de0430ff773dd99f7500962c268d6dfbfa431d7d08f919c9d960530e5f7f78eb47f267ad9b318265e5092b3ff9ede0db7c2b languageName: node linkType: hard -"jest-haste-map@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-haste-map@npm:29.6.2" +"jest-haste-map@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-haste-map@npm:29.7.0" dependencies: - "@jest/types": "npm:^29.6.1" + "@jest/types": "npm:^29.6.3" "@types/graceful-fs": "npm:^4.1.3" "@types/node": "npm:*" anymatch: "npm:^3.0.3" fb-watchman: "npm:^2.0.0" fsevents: "npm:^2.3.2" graceful-fs: "npm:^4.2.9" - jest-regex-util: "npm:^29.4.3" - jest-util: "npm:^29.6.2" - jest-worker: "npm:^29.6.2" + jest-regex-util: "npm:^29.6.3" + jest-util: "npm:^29.7.0" + jest-worker: "npm:^29.7.0" micromatch: "npm:^4.0.4" walker: "npm:^1.0.8" dependenciesMeta: fsevents: optional: true - checksum: 12c921ff059613b67e8b3a0730fe8f5f38e39a1aeb2050948a5c6890c4705f39decd4f7da8ebc7ede22e0eeef37fef2e9256952ac6557dd3bcd62416cab0612f + checksum: 2683a8f29793c75a4728787662972fedd9267704c8f7ef9d84f2beed9a977f1cf5e998c07b6f36ba5603f53cb010c911fe8cd0ac9886e073fe28ca66beefd30c languageName: node linkType: hard -"jest-leak-detector@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-leak-detector@npm:29.6.2" +"jest-leak-detector@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-leak-detector@npm:29.7.0" dependencies: - jest-get-type: "npm:^29.4.3" - pretty-format: "npm:^29.6.2" - checksum: 70eb538bb137b769908d4d1e15d9b380a06285ea31c1d2ae05bcc9269863efe6369484cb33bf6c6f3e325dd53cd252cd7e868bdbd2b31367a9b41b449eb8e4a9 + jest-get-type: "npm:^29.6.3" + pretty-format: "npm:^29.7.0" + checksum: 71bb9f77fc489acb842a5c7be030f2b9acb18574dc9fb98b3100fc57d422b1abc55f08040884bd6e6dbf455047a62f7eaff12aa4058f7cbdc11558718ca6a395 languageName: node linkType: hard -"jest-matcher-utils@npm:^29.6.2, jest-matcher-utils@npm:^29.7.0": +"jest-matcher-utils@npm:^29.7.0": version: 29.7.0 resolution: "jest-matcher-utils@npm:29.7.0" dependencies: @@ -10264,7 +10303,7 @@ __metadata: languageName: node linkType: hard -"jest-message-util@npm:^29.6.2, jest-message-util@npm:^29.7.0": +"jest-message-util@npm:^29.7.0": version: 29.7.0 resolution: "jest-message-util@npm:29.7.0" dependencies: @@ -10281,14 +10320,14 @@ __metadata: languageName: node linkType: hard -"jest-mock@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-mock@npm:29.6.2" +"jest-mock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-mock@npm:29.7.0" dependencies: - "@jest/types": "npm:^29.6.1" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" - jest-util: "npm:^29.6.2" - checksum: 34e8119876696d640db1b33b2c88f3bbd56b676f5e82ae65babdb56b0dab054d856b903785d38e1e8e3274549622b9a4556bfaa301d75fe4e2b30494cac5b8ee + jest-util: "npm:^29.7.0" + checksum: 7b9f8349ee87695a309fe15c46a74ab04c853369e5c40952d68061d9dc3159a0f0ed73e215f81b07ee97a9faaf10aebe5877a9d6255068a0977eae6a9ff1d5ac languageName: node linkType: hard @@ -10304,128 +10343,128 @@ __metadata: languageName: node linkType: hard -"jest-regex-util@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-regex-util@npm:29.4.3" - checksum: a7a4508bda47c5177e7337fb6fb22e9adab414ba141f224c9992c86973da1ccf5c69040e63636090ad26ef3a123d28bec950fa99496c157444b4f847e5e5a670 +"jest-regex-util@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-regex-util@npm:29.6.3" + checksum: 4e33fb16c4f42111159cafe26397118dcfc4cf08bc178a67149fb05f45546a91928b820894572679d62559839d0992e21080a1527faad65daaae8743a5705a3b languageName: node linkType: hard -"jest-resolve-dependencies@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-resolve-dependencies@npm:29.6.2" +"jest-resolve-dependencies@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve-dependencies@npm:29.7.0" dependencies: - jest-regex-util: "npm:^29.4.3" - jest-snapshot: "npm:^29.6.2" - checksum: b80172d164fe36a3cd9b19c458c3e8075e7935cdaa191f6e2e335f9b5c603faf0785efc35f9cf6c496729de34a3bd98f6cb8dd877c11fa6e17adf385d1ca85a6 + jest-regex-util: "npm:^29.6.3" + jest-snapshot: "npm:^29.7.0" + checksum: b6e9ad8ae5b6049474118ea6441dfddd385b6d1fc471db0136f7c8fbcfe97137a9665e4f837a9f49f15a29a1deb95a14439b7aec812f3f99d08f228464930f0d languageName: node linkType: hard -"jest-resolve@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-resolve@npm:29.6.2" +"jest-resolve@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve@npm:29.7.0" dependencies: chalk: "npm:^4.0.0" graceful-fs: "npm:^4.2.9" - jest-haste-map: "npm:^29.6.2" + jest-haste-map: "npm:^29.7.0" jest-pnp-resolver: "npm:^1.2.2" - jest-util: "npm:^29.6.2" - jest-validate: "npm:^29.6.2" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" resolve: "npm:^1.20.0" resolve.exports: "npm:^2.0.0" slash: "npm:^3.0.0" - checksum: df6ace45facf1f9d8f2911fcc1eefcc871afa107748f41a2f84a3d7b707d2211be1450ba5044fe8fa1ffc497b6814309f71f376aac139683ddc7b05b263d45f9 + checksum: 59da5c9c5b50563e959a45e09e2eace783d7f9ac0b5dcc6375dea4c0db938d2ebda97124c8161310082760e8ebbeff9f6b177c15ca2f57fb424f637a5d2adb47 languageName: node linkType: hard -"jest-runner@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-runner@npm:29.6.2" +"jest-runner@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runner@npm:29.7.0" dependencies: - "@jest/console": "npm:^29.6.2" - "@jest/environment": "npm:^29.6.2" - "@jest/test-result": "npm:^29.6.2" - "@jest/transform": "npm:^29.6.2" - "@jest/types": "npm:^29.6.1" + "@jest/console": "npm:^29.7.0" + "@jest/environment": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" chalk: "npm:^4.0.0" emittery: "npm:^0.13.1" graceful-fs: "npm:^4.2.9" - jest-docblock: "npm:^29.4.3" - jest-environment-node: "npm:^29.6.2" - jest-haste-map: "npm:^29.6.2" - jest-leak-detector: "npm:^29.6.2" - jest-message-util: "npm:^29.6.2" - jest-resolve: "npm:^29.6.2" - jest-runtime: "npm:^29.6.2" - jest-util: "npm:^29.6.2" - jest-watcher: "npm:^29.6.2" - jest-worker: "npm:^29.6.2" + jest-docblock: "npm:^29.7.0" + jest-environment-node: "npm:^29.7.0" + jest-haste-map: "npm:^29.7.0" + jest-leak-detector: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-resolve: "npm:^29.7.0" + jest-runtime: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-watcher: "npm:^29.7.0" + jest-worker: "npm:^29.7.0" p-limit: "npm:^3.1.0" source-map-support: "npm:0.5.13" - checksum: d0f2fc80b01c40b28bb86ace6a1f913a346dbdd81d8ed84e689bc0e21b27f7e9d1b963e6d8ece44df1a870ba14016730ce08444b15f3fdee92a15dff0c6c1aa3 + checksum: 2194b4531068d939f14c8d3274fe5938b77fa73126aedf9c09ec9dec57d13f22c72a3b5af01ac04f5c1cf2e28d0ac0b4a54212a61b05f10b5d6b47f2a1097bb4 languageName: node linkType: hard -"jest-runtime@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-runtime@npm:29.6.2" +"jest-runtime@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runtime@npm:29.7.0" dependencies: - "@jest/environment": "npm:^29.6.2" - "@jest/fake-timers": "npm:^29.6.2" - "@jest/globals": "npm:^29.6.2" - "@jest/source-map": "npm:^29.6.0" - "@jest/test-result": "npm:^29.6.2" - "@jest/transform": "npm:^29.6.2" - "@jest/types": "npm:^29.6.1" + "@jest/environment": "npm:^29.7.0" + "@jest/fake-timers": "npm:^29.7.0" + "@jest/globals": "npm:^29.7.0" + "@jest/source-map": "npm:^29.6.3" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" chalk: "npm:^4.0.0" cjs-module-lexer: "npm:^1.0.0" collect-v8-coverage: "npm:^1.0.0" glob: "npm:^7.1.3" graceful-fs: "npm:^4.2.9" - jest-haste-map: "npm:^29.6.2" - jest-message-util: "npm:^29.6.2" - jest-mock: "npm:^29.6.2" - jest-regex-util: "npm:^29.4.3" - jest-resolve: "npm:^29.6.2" - jest-snapshot: "npm:^29.6.2" - jest-util: "npm:^29.6.2" + jest-haste-map: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-mock: "npm:^29.7.0" + jest-regex-util: "npm:^29.6.3" + jest-resolve: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + jest-util: "npm:^29.7.0" slash: "npm:^3.0.0" strip-bom: "npm:^4.0.0" - checksum: 026a5fa33fa370561e6ab33a01b59e3e382b72f8eb7a42a85d1c9619bc9123a274ec791b823ad4bf58e20285758e9e895e53da6ae971c92124612f99fe7c7ffe + checksum: 7cd89a1deda0bda7d0941835434e44f9d6b7bd50b5c5d9b0fc9a6c990b2d4d2cab59685ab3cb2850ed4cc37059f6de903af5a50565d7f7f1192a77d3fd6dd2a6 languageName: node linkType: hard -"jest-snapshot@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-snapshot@npm:29.6.2" +"jest-snapshot@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-snapshot@npm:29.7.0" dependencies: "@babel/core": "npm:^7.11.6" "@babel/generator": "npm:^7.7.2" "@babel/plugin-syntax-jsx": "npm:^7.7.2" "@babel/plugin-syntax-typescript": "npm:^7.7.2" "@babel/types": "npm:^7.3.3" - "@jest/expect-utils": "npm:^29.6.2" - "@jest/transform": "npm:^29.6.2" - "@jest/types": "npm:^29.6.1" + "@jest/expect-utils": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" babel-preset-current-node-syntax: "npm:^1.0.0" chalk: "npm:^4.0.0" - expect: "npm:^29.6.2" + expect: "npm:^29.7.0" graceful-fs: "npm:^4.2.9" - jest-diff: "npm:^29.6.2" - jest-get-type: "npm:^29.4.3" - jest-matcher-utils: "npm:^29.6.2" - jest-message-util: "npm:^29.6.2" - jest-util: "npm:^29.6.2" + jest-diff: "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + jest-matcher-utils: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" natural-compare: "npm:^1.4.0" - pretty-format: "npm:^29.6.2" + pretty-format: "npm:^29.7.0" semver: "npm:^7.5.3" - checksum: 79f02c2becf90a1b5c5d06833b0a4c9f6e0d7a9fcd36e69f81750ab147180dd06e3565e83c1d79a1ef8b7943c5af3eb3e0119c45e92f78e1189279c4fba2e136 + checksum: 6e9003c94ec58172b4a62864a91c0146513207bedf4e0a06e1e2ac70a4484088a2683e3a0538d8ea913bcfd53dc54a9b98a98cdfa562e7fe1d1339aeae1da570 languageName: node linkType: hard -"jest-util@npm:^29.6.2, jest-util@npm:^29.7.0": +"jest-util@npm:^29.7.0": version: 29.7.0 resolution: "jest-util@npm:29.7.0" dependencies: @@ -10439,33 +10478,33 @@ __metadata: languageName: node linkType: hard -"jest-validate@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-validate@npm:29.6.2" +"jest-validate@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-validate@npm:29.7.0" dependencies: - "@jest/types": "npm:^29.6.1" + "@jest/types": "npm:^29.6.3" camelcase: "npm:^6.2.0" chalk: "npm:^4.0.0" - jest-get-type: "npm:^29.4.3" + jest-get-type: "npm:^29.6.3" leven: "npm:^3.1.0" - pretty-format: "npm:^29.6.2" - checksum: 79af1153268d896deb183230fba547398fde7b8a4f45fe33f1cd5c3b6b84d317e4b87ea7988d1137348c693e7f9450cce7af4529d5b190891bf493bc93024e40 + pretty-format: "npm:^29.7.0" + checksum: a20b930480c1ed68778c739f4739dce39423131bc070cd2505ddede762a5570a256212e9c2401b7ae9ba4d7b7c0803f03c5b8f1561c62348213aba18d9dbece2 languageName: node linkType: hard -"jest-watcher@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-watcher@npm:29.6.2" +"jest-watcher@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-watcher@npm:29.7.0" dependencies: - "@jest/test-result": "npm:^29.6.2" - "@jest/types": "npm:^29.6.1" + "@jest/test-result": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" ansi-escapes: "npm:^4.2.1" chalk: "npm:^4.0.0" emittery: "npm:^0.13.1" - jest-util: "npm:^29.6.2" + jest-util: "npm:^29.7.0" string-length: "npm:^4.0.1" - checksum: ba567798961d52b3ca1f853169a5860111ae764de90634b86a4a5cc676848c147bee5d95cd168b5c5941533ed384f677764474d009437a03b6b6a15da6232eb3 + checksum: ec6c75030562fc8f8c727cb8f3b94e75d831fc718785abfc196e1f2a2ebc9a2e38744a15147170039628a853d77a3b695561ce850375ede3a4ee6037a2574567 languageName: node linkType: hard @@ -10480,26 +10519,26 @@ __metadata: languageName: node linkType: hard -"jest-worker@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-worker@npm:29.6.2" +"jest-worker@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-worker@npm:29.7.0" dependencies: "@types/node": "npm:*" - jest-util: "npm:^29.6.2" + jest-util: "npm:^29.7.0" merge-stream: "npm:^2.0.0" supports-color: "npm:^8.0.0" - checksum: 8b978cb4851222e536aef552bdc06a60db580d0f921107fe1a1b94cdc8b39ddeb076b23e5bb96b69752c2f936b803295cdff11484f7c5efaf4562952e2cc0897 + checksum: 5570a3a005b16f46c131968b8a5b56d291f9bbb85ff4217e31c80bd8a02e7de799e59a54b95ca28d5c302f248b54cbffde2d177c2f0f52ffcee7504c6eabf660 languageName: node linkType: hard "jest@npm:^29.5.0": - version: 29.6.2 - resolution: "jest@npm:29.6.2" + version: 29.7.0 + resolution: "jest@npm:29.7.0" dependencies: - "@jest/core": "npm:^29.6.2" - "@jest/types": "npm:^29.6.1" + "@jest/core": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" import-local: "npm:^3.0.2" - jest-cli: "npm:^29.6.2" + jest-cli: "npm:^29.7.0" peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: @@ -10507,7 +10546,7 @@ __metadata: optional: true bin: jest: bin/jest.js - checksum: fdb4df81f2bf1ad58f98f74b6d6f74d7727bd8fd5a8ddefc1e7612b8a68cbd0a4ae134060c5b987b01281f1fe93276c1abb034ef1ce06a0ea1468f288fafc7c1 + checksum: f40eb8171cf147c617cc6ada49d062fbb03b4da666cb8d39cdbfb739a7d75eea4c3ca150fb072d0d273dce0c753db4d0467d54906ad0293f59c54f9db4a09d8b languageName: node linkType: hard @@ -10824,10 +10863,10 @@ __metadata: languageName: node linkType: hard -"known-css-properties@npm:^0.27.0": - version: 0.27.0 - resolution: "known-css-properties@npm:0.27.0" - checksum: 49bf8d0a773039d07726d263c92145fa73be9a18990d54c3b3cebf472fdfb0095124a3fcfca3ccd1225231d4bdf9615c82e029b2d0d508de130f6be7467af9e4 +"known-css-properties@npm:^0.29.0": + version: 0.29.0 + resolution: "known-css-properties@npm:0.29.0" + checksum: f66e9992097b8f54e97dbe729943d4a11b8d3ba15f68dbb3deb8bb0122cb89c22c90c9221ecb1e3f2e236838fe3c0faae319b43908c81b6e254ac43cafde2906 languageName: node linkType: hard @@ -10879,8 +10918,8 @@ __metadata: linkType: hard "lint-staged@npm:^15.0.0": - version: 15.0.2 - resolution: "lint-staged@npm:15.0.2" + version: 15.1.0 + resolution: "lint-staged@npm:15.1.0" dependencies: chalk: "npm:5.3.0" commander: "npm:11.1.0" @@ -10891,10 +10930,10 @@ __metadata: micromatch: "npm:4.0.5" pidtree: "npm:0.6.0" string-argv: "npm:0.3.2" - yaml: "npm:2.3.3" + yaml: "npm:2.3.4" bin: lint-staged: bin/lint-staged.js - checksum: 5c8806137c8c9c63e1156e16f2a2763586a9eab8f24713b95bbfd099308c83f85af2df622fdc326a9f0e455e657718f61f6d4a81067a5c77243e65e822c8f16c + checksum: d427408be98df7558e918593cb765d5caaa67a5cdca89671fb54280a6c959f4e448db36d4f85e8e0bd9c2c1e996aa133916925cf47c9df573b47308d5e298d84 languageName: node linkType: hard @@ -13152,12 +13191,12 @@ __metadata: languageName: node linkType: hard -"postcss-scss@npm:^4.0.7": - version: 4.0.7 - resolution: "postcss-scss@npm:4.0.7" +"postcss-scss@npm:^4.0.9": + version: 4.0.9 + resolution: "postcss-scss@npm:4.0.9" peerDependencies: - postcss: ^8.4.19 - checksum: 2f86938fef39bd766ada496d8ccac840bf9f2dee0d9c6006dc2903ba0fbdc9f5c2d6ead1f3e7508f8d82eee6ad25df7d77e9196c4c6bec8952ef9a7403f30efc + postcss: ^8.4.29 + checksum: f917ecfd4b9113a6648e966a41f027ff7e14238393914978d44596e227a50f084667dc8818742348dc7d8b20130b30d4259aca1d4db86754a9c141202ae03714 languageName: node linkType: hard @@ -13201,7 +13240,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.2.15, postcss@npm:^8.4.24, postcss@npm:^8.4.25": +"postcss@npm:^8.2.15, postcss@npm:^8.4.24, postcss@npm:^8.4.28": version: 8.4.31 resolution: "postcss@npm:8.4.31" dependencies: @@ -13296,11 +13335,11 @@ __metadata: linkType: hard "prettier@npm:^3.0.0": - version: 3.0.1 - resolution: "prettier@npm:3.0.1" + version: 3.1.0 + resolution: "prettier@npm:3.1.0" bin: prettier: bin/prettier.cjs - checksum: 7231768b6e0f0f17cbaa83a4f7cdb100df0229ef1910b0b2cf72ce5ed8ee25ae7ec0d30cde20dcd898a002c6d1fcdb8a6ab0f8f5d8fc1275b7c29ea9e56305f2 + checksum: a45ea70aa97fde162ea4c4aba3dfc7859aa6a732a1db34458d9535dc3c2c16d3bc3fb5689e6cd76aa835562555303b02d9449fd2e15af3b73c8053557e25c5b6 languageName: node linkType: hard @@ -13322,7 +13361,7 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^29.0.0, pretty-format@npm:^29.6.2, pretty-format@npm:^29.7.0": +"pretty-format@npm:^29.0.0, pretty-format@npm:^29.7.0": version: 29.7.0 resolution: "pretty-format@npm:29.7.0" dependencies: @@ -15720,20 +15759,20 @@ __metadata: languageName: node linkType: hard -"stylelint-config-recommended-scss@npm:^13.0.0": - version: 13.0.0 - resolution: "stylelint-config-recommended-scss@npm:13.0.0" +"stylelint-config-recommended-scss@npm:^13.1.0": + version: 13.1.0 + resolution: "stylelint-config-recommended-scss@npm:13.1.0" dependencies: - postcss-scss: "npm:^4.0.7" + postcss-scss: "npm:^4.0.9" stylelint-config-recommended: "npm:^13.0.0" - stylelint-scss: "npm:^5.1.0" + stylelint-scss: "npm:^5.3.0" peerDependencies: postcss: ^8.3.3 stylelint: ^15.10.0 peerDependenciesMeta: postcss: optional: true - checksum: 370152e20e6395800ac89517019f03153b121ce7a7dfa0865442365bf2725935da9df2eb3e8b5ee4e240d1b3a7438ee2cd1932009bdc59da8c2a790066379387 + checksum: e07d0172c7936b4f644138e4129df2f187d297f1f96ce5865ab21ccd1c22caf94220f7caf9d6985e93e515de4c0356f6cb9c924d00df2eee5b3bc237f7e5bb48 languageName: node linkType: hard @@ -15747,10 +15786,10 @@ __metadata: linkType: hard "stylelint-config-standard-scss@npm:^11.0.0": - version: 11.0.0 - resolution: "stylelint-config-standard-scss@npm:11.0.0" + version: 11.1.0 + resolution: "stylelint-config-standard-scss@npm:11.1.0" dependencies: - stylelint-config-recommended-scss: "npm:^13.0.0" + stylelint-config-recommended-scss: "npm:^13.1.0" stylelint-config-standard: "npm:^34.0.0" peerDependencies: postcss: ^8.3.3 @@ -15758,7 +15797,7 @@ __metadata: peerDependenciesMeta: postcss: optional: true - checksum: 9b0766ec11f1e6f690c1e22d74e7f7103f64569bdca966c26ff43f81f55f13797fdb1503338f973672c001daf50b13682361dd4105d22497841437c148f86be5 + checksum: 22d00e75c1eacce9883fd48c3d67b1107b0e39d7d86e9f73deaa332b11c39a9678c947ae2c34cd5159a452ec9a857694ed58b5a851087480d3c9a66dab629415 languageName: node linkType: hard @@ -15773,37 +15812,38 @@ __metadata: languageName: node linkType: hard -"stylelint-scss@npm:^5.1.0": - version: 5.1.0 - resolution: "stylelint-scss@npm:5.1.0" +"stylelint-scss@npm:^5.3.0": + version: 5.3.1 + resolution: "stylelint-scss@npm:5.3.1" dependencies: + known-css-properties: "npm:^0.29.0" postcss-media-query-parser: "npm:^0.2.3" postcss-resolve-nested-selector: "npm:^0.1.1" postcss-selector-parser: "npm:^6.0.13" postcss-value-parser: "npm:^4.2.0" peerDependencies: stylelint: ^14.5.1 || ^15.0.0 - checksum: af176340227e77942429d2032cd345ebc8e40e4ff17a2ce69b6000252da178f21d1a97311a258a81c76c0610a96650c1e2ecdfa0d598a6fc41e31d6a7cd03847 + checksum: 5dfed5f9ac9812cd2ac6ef0272c720dee0326aaaee2998315a23bdcd71b8f04427f29cad634793eea2b45984182e20f03e90d43501e8e4d55bc956f80e2de477 languageName: node linkType: hard "stylelint@npm:^15.10.1": - version: 15.10.2 - resolution: "stylelint@npm:15.10.2" + version: 15.11.0 + resolution: "stylelint@npm:15.11.0" dependencies: - "@csstools/css-parser-algorithms": "npm:^2.3.0" - "@csstools/css-tokenizer": "npm:^2.1.1" - "@csstools/media-query-list-parser": "npm:^2.1.2" + "@csstools/css-parser-algorithms": "npm:^2.3.1" + "@csstools/css-tokenizer": "npm:^2.2.0" + "@csstools/media-query-list-parser": "npm:^2.1.4" "@csstools/selector-specificity": "npm:^3.0.0" balanced-match: "npm:^2.0.0" colord: "npm:^2.9.3" cosmiconfig: "npm:^8.2.0" - css-functions-list: "npm:^3.2.0" + css-functions-list: "npm:^3.2.1" css-tree: "npm:^2.3.1" debug: "npm:^4.3.4" - fast-glob: "npm:^3.3.0" + fast-glob: "npm:^3.3.1" fastest-levenshtein: "npm:^1.0.16" - file-entry-cache: "npm:^6.0.1" + file-entry-cache: "npm:^7.0.0" global-modules: "npm:^2.0.0" globby: "npm:^11.1.0" globjoin: "npm:^0.1.4" @@ -15812,13 +15852,13 @@ __metadata: import-lazy: "npm:^4.0.0" imurmurhash: "npm:^0.1.4" is-plain-object: "npm:^5.0.0" - known-css-properties: "npm:^0.27.0" + known-css-properties: "npm:^0.29.0" mathml-tag-names: "npm:^2.1.3" meow: "npm:^10.1.5" micromatch: "npm:^4.0.5" normalize-path: "npm:^3.0.0" picocolors: "npm:^1.0.0" - postcss: "npm:^8.4.25" + postcss: "npm:^8.4.28" postcss-resolve-nested-selector: "npm:^0.1.1" postcss-safe-parser: "npm:^6.0.0" postcss-selector-parser: "npm:^6.0.13" @@ -15833,7 +15873,7 @@ __metadata: write-file-atomic: "npm:^5.0.1" bin: stylelint: bin/stylelint.mjs - checksum: 8378ee868b09322d2b7e07b03461c524736778a391b2ce6d4ad46c636e55a90ed98723f253db10d9a762cfefec60200d0b9c5cd7798b2f373efd2d986768ebc7 + checksum: 2d88b7293e308b7e418c14ba4130777b1a28b214304957f03b41a6dc8e00005266caf47479f718a6ec5e572cb52e903ca34aabf3febbe3a3ae32fff6b018d9fd languageName: node linkType: hard @@ -17724,10 +17764,10 @@ __metadata: languageName: node linkType: hard -"yaml@npm:2.3.3": - version: 2.3.3 - resolution: "yaml@npm:2.3.3" - checksum: a0c56bf682159b0567e9cbbddf23efc2f6806f6450716d9be6ec5eb1af1b941e95c8d3dc9c47da20d1b6883a9d6c61e31cf98bb4b77ebca4396bf772657f2f00 +"yaml@npm:2.3.4": + version: 2.3.4 + resolution: "yaml@npm:2.3.4" + checksum: cf03b68f8fef5e8516b0f0b54edaf2459f1648317fc6210391cf606d247e678b449382f4bd01f77392538429e306c7cba8ff46ff6b37cac4de9a76aff33bd9e1 languageName: node linkType: hard From a36b59be8ad7656b7ceab9751c9ec5b3563e3a30 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 13 Nov 2023 09:32:36 -0500 Subject: [PATCH 10/63] Spec coverage for `api/v1/trends` controllers (#27837) --- .../api/v1/trends/links_controller_spec.rb | 38 +++++++++++++++-- .../api/v1/trends/statuses_controller_spec.rb | 38 +++++++++++++++-- .../api/v1/trends/tags_controller_spec.rb | 41 +++++++++++++++---- 3 files changed, 101 insertions(+), 16 deletions(-) diff --git a/spec/controllers/api/v1/trends/links_controller_spec.rb b/spec/controllers/api/v1/trends/links_controller_spec.rb index 71a7e2e47..12d4198aa 100644 --- a/spec/controllers/api/v1/trends/links_controller_spec.rb +++ b/spec/controllers/api/v1/trends/links_controller_spec.rb @@ -2,14 +2,44 @@ require 'rails_helper' -describe Api::V1::Trends::LinksController do +RSpec.describe Api::V1::Trends::LinksController do render_views describe 'GET #index' do - it 'returns http success' do - get :index + around do |example| + previous = Setting.trends + example.run + Setting.trends = previous + end - expect(response).to have_http_status(200) + context 'when trends are disabled' do + before { Setting.trends = false } + + it 'returns http success' do + get :index + + expect(response).to have_http_status(200) + end + end + + context 'when trends are enabled' do + before { Setting.trends = true } + + it 'returns http success' do + prepare_trends + stub_const('Api::V1::Trends::LinksController::DEFAULT_LINKS_LIMIT', 2) + get :index + + expect(response).to have_http_status(200) + expect(response.headers).to include('Link') + end + + def prepare_trends + Fabricate.times(3, :preview_card, trendable: true, language: 'en').each do |link| + 2.times { |i| Trends.links.add(link, i) } + end + Trends::Links.new(threshold: 1).refresh + end end end end diff --git a/spec/controllers/api/v1/trends/statuses_controller_spec.rb b/spec/controllers/api/v1/trends/statuses_controller_spec.rb index e9892bb14..69fdb270d 100644 --- a/spec/controllers/api/v1/trends/statuses_controller_spec.rb +++ b/spec/controllers/api/v1/trends/statuses_controller_spec.rb @@ -2,14 +2,44 @@ require 'rails_helper' -describe Api::V1::Trends::StatusesController do +RSpec.describe Api::V1::Trends::StatusesController do render_views describe 'GET #index' do - it 'returns http success' do - get :index + around do |example| + previous = Setting.trends + example.run + Setting.trends = previous + end - expect(response).to have_http_status(200) + context 'when trends are disabled' do + before { Setting.trends = false } + + it 'returns http success' do + get :index + + expect(response).to have_http_status(200) + end + end + + context 'when trends are enabled' do + before { Setting.trends = true } + + it 'returns http success' do + prepare_trends + stub_const('Api::BaseController::DEFAULT_STATUSES_LIMIT', 2) + get :index + + expect(response).to have_http_status(200) + expect(response.headers).to include('Link') + end + + def prepare_trends + Fabricate.times(3, :status, trendable: true, language: 'en').each do |status| + 2.times { |i| Trends.statuses.add(status, i) } + end + Trends::Statuses.new(threshold: 1, decay_threshold: -1).refresh + end end end end diff --git a/spec/controllers/api/v1/trends/tags_controller_spec.rb b/spec/controllers/api/v1/trends/tags_controller_spec.rb index 84370d841..9311392cd 100644 --- a/spec/controllers/api/v1/trends/tags_controller_spec.rb +++ b/spec/controllers/api/v1/trends/tags_controller_spec.rb @@ -6,16 +6,41 @@ RSpec.describe Api::V1::Trends::TagsController do render_views describe 'GET #index' do - before do - Fabricate.times(10, :tag).each do |tag| - 10.times { |i| Trends.tags.add(tag, i) } - end - - get :index + around do |example| + previous = Setting.trends + example.run + Setting.trends = previous end - it 'returns http success' do - expect(response).to have_http_status(200) + context 'when trends are disabled' do + before { Setting.trends = false } + + it 'returns http success' do + get :index + + expect(response).to have_http_status(200) + expect(response.headers).to_not include('Link') + end + end + + context 'when trends are enabled' do + before { Setting.trends = true } + + it 'returns http success' do + prepare_trends + stub_const('Api::V1::Trends::TagsController::DEFAULT_TAGS_LIMIT', 2) + get :index + + expect(response).to have_http_status(200) + expect(response.headers).to include('Link') + end + + def prepare_trends + Fabricate.times(3, :tag, trendable: true).each do |tag| + 2.times { |i| Trends.tags.add(tag, i) } + end + Trends::Tags.new(threshold: 1).refresh + end end end end From 7e3c10dec676b0a560493071c5950c7e5df759b2 Mon Sep 17 00:00:00 2001 From: Eugen Rochko <eugen@zeonfederated.com> Date: Mon, 13 Nov 2023 15:39:45 +0100 Subject: [PATCH 11/63] Add icons for private and disabled boost in web UI (#27817) Co-authored-by: Claire <claire.github-309c@sitedethib.com> --- .../mastodon/components/status_action_bar.jsx | 12 ++++++++++-- .../features/status/components/action_bar.jsx | 12 ++++++++++-- app/javascript/svg-icons/repeat_disabled.svg | 5 +++++ app/javascript/svg-icons/repeat_private.svg | 5 +++++ config/webpack/rules/material_icons.js | 2 +- 5 files changed, 31 insertions(+), 5 deletions(-) create mode 100755 app/javascript/svg-icons/repeat_disabled.svg create mode 100755 app/javascript/svg-icons/repeat_private.svg diff --git a/app/javascript/mastodon/components/status_action_bar.jsx b/app/javascript/mastodon/components/status_action_bar.jsx index 240174739..25eab91fe 100644 --- a/app/javascript/mastodon/components/status_action_bar.jsx +++ b/app/javascript/mastodon/components/status_action_bar.jsx @@ -19,6 +19,8 @@ import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/s import { ReactComponent as StarBorderIcon } from '@material-symbols/svg-600/outlined/star.svg'; import { ReactComponent as VisibilityIcon } from '@material-symbols/svg-600/outlined/visibility.svg'; +import { ReactComponent as RepeatDisabledIcon } from 'mastodon/../svg-icons/repeat_disabled.svg'; +import { ReactComponent as RepeatPrivateIcon } from 'mastodon/../svg-icons/repeat_private.svg'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions'; import { WithRouterPropTypes } from 'mastodon/utils/react_router'; @@ -348,6 +350,7 @@ class StatusActionBar extends ImmutablePureComponent { let replyIcon; let replyIconComponent; let replyTitle; + if (status.get('in_reply_to_id', null) === null) { replyIcon = 'reply'; replyIconComponent = ReplyIcon; @@ -360,15 +363,20 @@ class StatusActionBar extends ImmutablePureComponent { const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private'; - let reblogTitle = ''; + let reblogTitle, reblogIconComponent; + if (status.get('reblogged')) { reblogTitle = intl.formatMessage(messages.cancel_reblog_private); + reblogIconComponent = publicStatus ? RepeatIcon : RepeatPrivateIcon; } else if (publicStatus) { reblogTitle = intl.formatMessage(messages.reblog); + reblogIconComponent = RepeatIcon; } else if (reblogPrivate) { reblogTitle = intl.formatMessage(messages.reblog_private); + reblogIconComponent = RepeatPrivateIcon; } else { reblogTitle = intl.formatMessage(messages.cannot_reblog); + reblogIconComponent = RepeatDisabledIcon; } const filterButton = this.props.onFilter && ( @@ -380,7 +388,7 @@ class StatusActionBar extends ImmutablePureComponent { return ( <div className='status__action-bar'> <IconButton className='status__action-bar__button' title={replyTitle} icon={isReply ? 'reply' : replyIcon} iconComponent={isReply ? ReplyIcon : replyIconComponent} onClick={this.handleReplyClick} counter={status.get('replies_count')} /> - <IconButton className={classNames('status__action-bar__button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={RepeatIcon} onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} /> + <IconButton className={classNames('status__action-bar__button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={reblogIconComponent} onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} /> <IconButton className='status__action-bar__button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={status.get('favourited') ? StarIcon : StarBorderIcon} onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} /> <IconButton className='status__action-bar__button bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' iconComponent={status.get('bookmarked') ? BookmarkIcon : BookmarkBorderIcon} onClick={this.handleBookmarkClick} /> diff --git a/app/javascript/mastodon/features/status/components/action_bar.jsx b/app/javascript/mastodon/features/status/components/action_bar.jsx index eac0bab7e..663bce743 100644 --- a/app/javascript/mastodon/features/status/components/action_bar.jsx +++ b/app/javascript/mastodon/features/status/components/action_bar.jsx @@ -18,6 +18,8 @@ import { ReactComponent as ReplyAllIcon } from '@material-symbols/svg-600/outlin import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star-fill.svg'; import { ReactComponent as StarBorderIcon } from '@material-symbols/svg-600/outlined/star.svg'; +import { ReactComponent as RepeatDisabledIcon } from 'mastodon/../svg-icons/repeat_disabled.svg'; +import { ReactComponent as RepeatPrivateIcon } from 'mastodon/../svg-icons/repeat_private.svg'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions'; import { WithRouterPropTypes } from 'mastodon/utils/react_router'; @@ -280,6 +282,7 @@ class ActionBar extends PureComponent { let replyIcon; let replyIconComponent; + if (status.get('in_reply_to_id', null) === null) { replyIcon = 'reply'; replyIconComponent = ReplyIcon; @@ -290,21 +293,26 @@ class ActionBar extends PureComponent { const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private'; - let reblogTitle; + let reblogTitle, reblogIconComponent; + if (status.get('reblogged')) { reblogTitle = intl.formatMessage(messages.cancel_reblog_private); + reblogIconComponent = publicStatus ? RepeatIcon : RepeatPrivateIcon; } else if (publicStatus) { reblogTitle = intl.formatMessage(messages.reblog); + reblogIconComponent = RepeatIcon; } else if (reblogPrivate) { reblogTitle = intl.formatMessage(messages.reblog_private); + reblogIconComponent = RepeatPrivateIcon; } else { reblogTitle = intl.formatMessage(messages.cannot_reblog); + reblogIconComponent = RepeatDisabledIcon; } return ( <div className='detailed-status__action-bar'> <div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.reply)} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} iconComponent={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? ReplyIcon : replyIconComponent} onClick={this.handleReplyClick} /></div> - <div className='detailed-status__button'><IconButton className={classNames({ reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={RepeatIcon} onClick={this.handleReblogClick} /></div> + <div className='detailed-status__button'><IconButton className={classNames({ reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={reblogIconComponent} onClick={this.handleReblogClick} /></div> <div className='detailed-status__button'><IconButton className='star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={status.get('favourited') ? StarIcon : StarBorderIcon} onClick={this.handleFavouriteClick} /></div> <div className='detailed-status__button'><IconButton className='bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' iconComponent={status.get('bookmarked') ? BookmarkIcon : BookmarkBorderIcon} onClick={this.handleBookmarkClick} /></div> diff --git a/app/javascript/svg-icons/repeat_disabled.svg b/app/javascript/svg-icons/repeat_disabled.svg new file mode 100755 index 000000000..3157f660e --- /dev/null +++ b/app/javascript/svg-icons/repeat_disabled.svg @@ -0,0 +1,5 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M19 13V17.8787L17 15.8787V13H19Z"/> +<path d="M2.41421 2.70711L1 4.12132L5 8.12132V11H7V10.1213L13.8787 17H6.85L8.4 15.45L7 14L3 18L7 22L8.4 20.55L6.85 19H15.8787L19.3848 22.5061L20.799 21.0919L2.41421 2.70711Z"/> +<path d="M17.15 7H8.12132L6.12132 5H17.15L15.6 3.45L17 2L21 6L17 10L15.6 8.55L17.15 7Z"/> +</svg> diff --git a/app/javascript/svg-icons/repeat_private.svg b/app/javascript/svg-icons/repeat_private.svg new file mode 100755 index 000000000..a65be532a --- /dev/null +++ b/app/javascript/svg-icons/repeat_private.svg @@ -0,0 +1,5 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M8.4 15.45L7 14L3 18L7 22L8.4 20.55L6.85 19H13.5V18C13.5 17.6567 13.5638 17.3171 13.6988 17H6.85L8.4 15.45Z"/> +<path d="M5 5V11H7V7H17.15L15.6 8.55L17 10L21 6L17 2L15.6 3.45L17.15 5H5Z"/> +<path d="M16 22C15.7167 22 15.475 21.9083 15.275 21.725C15.0917 21.525 15 21.2833 15 21V18C15 17.7167 15.0917 17.4833 15.275 17.3C15.475 17.1 15.7167 17 16 17V16C16 15.45 16.1917 14.9833 16.575 14.6C16.975 14.2 17.45 14 18 14C18.55 14 19.0167 14.2 19.4 14.6C19.8 14.9833 20 15.45 20 16V17C20.2833 17 20.5167 17.1 20.7 17.3C20.9 17.4833 21 17.7167 21 18V21C21 21.2833 20.9 21.525 20.7 21.725C20.5167 21.9083 20.2833 22 20 22H16ZM17 17H19V16C19 15.7167 18.9 15.4833 18.7 15.3C18.5167 15.1 18.2833 15 18 15C17.7167 15 17.475 15.1 17.275 15.3C17.0917 15.4833 17 15.7167 17 16V17Z"/> +</svg> diff --git a/config/webpack/rules/material_icons.js b/config/webpack/rules/material_icons.js index 7ac1072b0..048e198ef 100644 --- a/config/webpack/rules/material_icons.js +++ b/config/webpack/rules/material_icons.js @@ -1,6 +1,6 @@ module.exports = { test: /\.svg$/, - include: /node_modules\/@material-symbols/, + include: [/node_modules\/@material-symbols/, /svg-icons/], issuer: /\.[jt]sx?$/, use: [ { From a1b48460e4e22cb552b35776a5cc03961c619d9f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 15:40:10 +0100 Subject: [PATCH 12/63] Update DefinitelyTyped types (non-major) (#27830) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 178 +++++++++++++++++++++++++-------------------------- 2 files changed, 90 insertions(+), 90 deletions(-) diff --git a/package.json b/package.json index 96bfb1571..6f20b98bc 100644 --- a/package.json +++ b/package.json @@ -176,7 +176,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.36", + "@types/react-motion": "^0.0.37", "@types/react-overlays": "^3.1.0", "@types/react-router": "^5.1.20", "@types/react-router-dom": "^5.3.3", diff --git a/yarn.lock b/yarn.lock index 891eb59fe..b05b2bfbc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2384,7 +2384,7 @@ __metadata: "@types/react-dom": "npm:^18.2.4" "@types/react-helmet": "npm:^6.1.6" "@types/react-immutable-proptypes": "npm:^2.1.0" - "@types/react-motion": "npm:^0.0.36" + "@types/react-motion": "npm:^0.0.37" "@types/react-overlays": "npm:^3.1.0" "@types/react-router": "npm:^5.1.20" "@types/react-router-dom": "npm:^5.3.3" @@ -3006,15 +3006,15 @@ __metadata: linkType: hard "@types/babel__core@npm:*, @types/babel__core@npm:^7.1.12, @types/babel__core@npm:^7.1.14, @types/babel__core@npm:^7.1.7, @types/babel__core@npm:^7.20.1": - version: 7.20.3 - resolution: "@types/babel__core@npm:7.20.3" + version: 7.20.4 + resolution: "@types/babel__core@npm:7.20.4" dependencies: "@babel/parser": "npm:^7.20.7" "@babel/types": "npm:^7.20.7" "@types/babel__generator": "npm:*" "@types/babel__template": "npm:*" "@types/babel__traverse": "npm:*" - checksum: 5b5f9de4df7f995c2f06f3fdad39b58bc30121d1f2daceb97dd423c9b5dcbd5c464959338824e0dbee0c758bf55c4e9fe46fafd13bd29c1834afad04f291c588 + checksum: 2adc7ec49de5f922271ce087cedee000de468a3e13f92b7b6254016bd8357298cb98e6d2b3c9defc69bb6e38e0c134ffe80776a8ce4e9fb167bbffcb4d7613b7 languageName: node linkType: hard @@ -3075,18 +3075,18 @@ __metadata: linkType: hard "@types/emoji-mart@npm:^3.0.9": - version: 3.0.11 - resolution: "@types/emoji-mart@npm:3.0.11" + version: 3.0.12 + resolution: "@types/emoji-mart@npm:3.0.12" dependencies: "@types/react": "npm:*" - checksum: 1080ee6d0286248310b1f29ed7e21f15bdb587dec924dc2f714ab60f9ecc4567dde84fb73e41336d72a3e77afd2546f3025f886474391dd5efe1884a6a8ef034 + checksum: 146abafe3ce9f4954c7f6ed3603bdc5a897b2d6b99dd9da6065ef597a9a6a59fb914e907decbda29b661216ddf3da8bb34a28d50f3d6929efce2b3c42e73b085 languageName: node linkType: hard "@types/escape-html@npm:^1.0.2": - version: 1.0.3 - resolution: "@types/escape-html@npm:1.0.3" - checksum: 980ec83f07f91389ffc9d7043da40db49dc40879a57f743a422bf30aa1d730f33c880baab4b31aacce550f2b195a866ec95d66a14c517ea1416d03e9b462df9d + version: 1.0.4 + resolution: "@types/escape-html@npm:1.0.4" + checksum: 5cdae9d38e97b1ad61180528ef7ca66bf6be96e875cc560c3e064d6ca75ccf2adaf2fa3b7bdd4f7494013e4357a2fb0bb62f2e59ca53097d5c45c7519d0ce9c3 languageName: node linkType: hard @@ -3127,14 +3127,14 @@ __metadata: linkType: hard "@types/express@npm:^4.17.17": - version: 4.17.20 - resolution: "@types/express@npm:4.17.20" + version: 4.17.21 + resolution: "@types/express@npm:4.17.21" dependencies: "@types/body-parser": "npm:*" "@types/express-serve-static-core": "npm:^4.17.33" "@types/qs": "npm:*" "@types/serve-static": "npm:*" - checksum: f73f5f92bd0a0fa4697598be3122c89522caa9e3bcb14c28b5e6d58a8e47f0301027478997153ae9ee4cf3d432576fb3fb0918ea0db521cc1204f8b759828a32 + checksum: 12e562c4571da50c7d239e117e688dc434db1bac8be55613294762f84fd77fbd0658ccd553c7d3ab02408f385bc93980992369dd30e2ecd2c68c358e6af8fabf languageName: node linkType: hard @@ -3165,12 +3165,12 @@ __metadata: linkType: hard "@types/hoist-non-react-statics@npm:^3.3.1": - version: 3.3.4 - resolution: "@types/hoist-non-react-statics@npm:3.3.4" + version: 3.3.5 + resolution: "@types/hoist-non-react-statics@npm:3.3.5" dependencies: "@types/react": "npm:*" hoist-non-react-statics: "npm:^3.3.0" - checksum: 677ddcacf72f2fb79f65599189d4741fa3d815aa5e50c7fd6c3c9be92298aecf05b044e7dc445cf2bb0877652293949a7f0e364e63d9266b242cd9cb9bf53fa7 + checksum: 2a3b64bf3d9817d7830afa60ee314493c475fb09570a64e7737084cd482d2177ebdddf888ce837350bac51741278b077683facc9541f052d4bbe8487b4e3e618 languageName: node linkType: hard @@ -3182,18 +3182,18 @@ __metadata: linkType: hard "@types/http-link-header@npm:^1.0.3": - version: 1.0.4 - resolution: "@types/http-link-header@npm:1.0.4" + version: 1.0.5 + resolution: "@types/http-link-header@npm:1.0.5" dependencies: "@types/node": "npm:*" - checksum: 65035f11a49a2c4ea75a0a8de284a8808264e0a1b03e6be5cd8df8637b283be154f723d2eef091715ad3e98b8d252bc261aea109559e43702dfecae03e26263e + checksum: adeb13381b38c3625478149820772924c154b4a7250dca62c346810a8378f8968fc7f3a9a4f55ec61de5d06083637540f862c8a920f6a710310c9645d19a077d languageName: node linkType: hard "@types/intl@npm:^1.2.0": - version: 1.2.1 - resolution: "@types/intl@npm:1.2.1" - checksum: b1c81eade8a9b7ee33b7642283b7d024c263d34c5efae24a517822bd87b8a01c1a2a8c71ade948346c785abad522858ec389247031b5404307c06ba51da52385 + version: 1.2.2 + resolution: "@types/intl@npm:1.2.2" + checksum: f465c320139c01dfc9ae1382406259fd23f6a455aad31517f61b7fd79bdde493e854d6666c2198ae644d8cf6e147e78831ea810f83a787f65b765bc56834f259 languageName: node linkType: hard @@ -3223,19 +3223,19 @@ __metadata: linkType: hard "@types/jest@npm:^29.5.2": - version: 29.5.7 - resolution: "@types/jest@npm:29.5.7" + version: 29.5.8 + resolution: "@types/jest@npm:29.5.8" dependencies: expect: "npm:^29.0.0" pretty-format: "npm:^29.0.0" - checksum: 231c873f3d1ddac973b8f8f2ad7760677d941d85fb52d1c5dc4a311bafba4c2c1658a1040fd7054a51f4d1841f51c6ca4cabf70675ee4fa9e10fc5b8066e1de1 + checksum: a28e7827ea7e1a2aace6a386868fa6b8402c162d6c71570aed2c29d3745ddc22ceef6899a20643071817905d3c57b670a7992fc8760bff65939351fd4dc481cf languageName: node linkType: hard "@types/js-yaml@npm:^4.0.5": - version: 4.0.8 - resolution: "@types/js-yaml@npm:4.0.8" - checksum: 171a5c54d5b5c86a89300d14a004c49321f1a290fd2f625e2ef682e100ce78715a0eb8eac1ff09114dadaec8ccdb98251ddb5e06f1f3d6aa2ec83930e7a16039 + version: 4.0.9 + resolution: "@types/js-yaml@npm:4.0.9" + checksum: 24de857aa8d61526bbfbbaa383aa538283ad17363fcd5bb5148e2c7f604547db36646440e739d78241ed008702a8920665d1add5618687b6743858fae00da211 languageName: node linkType: hard @@ -3272,9 +3272,9 @@ __metadata: linkType: hard "@types/lodash@npm:^4.14.195": - version: 4.14.200 - resolution: "@types/lodash@npm:4.14.200" - checksum: 7a8dac6dc866f10d1888846d6189d1faeb4f65adb139f0837a005fd1adcde62e60d7e7abb1a2733d13fc57bebb337d74182d8ad3dbd1d211dcd0c310c47e81bc + version: 4.14.201 + resolution: "@types/lodash@npm:4.14.201" + checksum: 14dc43787296c429433d7d034ed47c5ac24b92217056f80a0e6c990449120b9c9c1058918188945fb88353c0c8333c5c36dccc40c51edbd39b05d2169ab2e0ad languageName: node linkType: hard @@ -3330,18 +3330,18 @@ __metadata: linkType: hard "@types/npmlog@npm:^4.1.4": - version: 4.1.5 - resolution: "@types/npmlog@npm:4.1.5" + version: 4.1.6 + resolution: "@types/npmlog@npm:4.1.6" dependencies: "@types/node": "npm:*" - checksum: 84b01941e0bc9c4e1ee145ee1278731e4a1d4236a88c62f94640a741aa323df7a20ff20344432f574184fa2b219ca7f7b549f724684cab4bc8dada7b3a13941c + checksum: 432bfb14b29a383e095e099b2ddff4266051b43bc6c7ea242d10194acde2f1181a1e967bbb543f07979dd77743ead1954abac1128ec78cc2b86a5f7ea841ddcb languageName: node linkType: hard "@types/object-assign@npm:^4.0.30": - version: 4.0.32 - resolution: "@types/object-assign@npm:4.0.32" - checksum: 25a75c2c1f4b20ce95443ab247b153dfb00e4bf08b4716f01efae7063f6e49a8af96f4c752dc3a44d7da381b9730249b3ee97900b6336bf5221f242c9532e8c5 + version: 4.0.33 + resolution: "@types/object-assign@npm:4.0.33" + checksum: 7fbc399aa1140beff35a152e206bfb336dd880721f4a13cc1ea01d964ab376fa4ca2c19059145cbd777c9d3eaf724008faec8cf3becff97353c69560196af086 languageName: node linkType: hard @@ -3353,13 +3353,13 @@ __metadata: linkType: hard "@types/pg@npm:^8.6.6": - version: 8.10.7 - resolution: "@types/pg@npm:8.10.7" + version: 8.10.9 + resolution: "@types/pg@npm:8.10.9" dependencies: "@types/node": "npm:*" pg-protocol: "npm:*" pg-types: "npm:^4.0.1" - checksum: beea456e9f4011d07e318b57d8c96af26379c658ad30d242a3194520d41406ca7bfa19a941405b7db36e401aa07f34cd035bdc5ac3d7681712c47a48df3cd09e + checksum: 6b3bec7230d09da6459636a66dfd6fb538378e466ffff0a0bcd07d67aa4ddce49c73afc7442f53adec92a49dbf9e71d8d847e0075750d7545331735dfd92d22c languageName: node linkType: hard @@ -3371,16 +3371,16 @@ __metadata: linkType: hard "@types/prop-types@npm:*, @types/prop-types@npm:^15.7.5": - version: 15.7.9 - resolution: "@types/prop-types@npm:15.7.9" - checksum: e2a7373b91a8eb30cb4e399ef5b3a14baa7d72eed1667ef5e3cb1e9400edfca9b60c20b845fefdcf7562773829f6ff60ba350b09f6313a8093e70c15b2b88f00 + version: 15.7.10 + resolution: "@types/prop-types@npm:15.7.10" + checksum: 964348d05cdf7399260b3179fbd1462b23d7452dc39fbccb319e54ed6ffafb0a01c0a62c3e6f7c944a635c7a4ad5c99d62c4787c9c4b74e2ec07aebaf6cfedc1 languageName: node linkType: hard "@types/punycode@npm:^2.1.0": - version: 2.1.1 - resolution: "@types/punycode@npm:2.1.1" - checksum: 3e5c3c786790111ac497d7a472c762671c68ec58edce0a88757c503e997eb500af1b8584c948b0a9927394b0f15828faa8f986130eb4ebb0fde0331c2f515630 + version: 2.1.2 + resolution: "@types/punycode@npm:2.1.2" + checksum: 4a748533bde61097f205638b3acc2c3b0e25382d2c35a2e3b60c33c904afbad158ef778ae3e1b3f3c84edd7b04b6d9fa049f2832210c308d3fee77ba7b637cb9 languageName: node linkType: hard @@ -3406,39 +3406,39 @@ __metadata: linkType: hard "@types/react-dom@npm:^18.0.0, @types/react-dom@npm:^18.2.4": - version: 18.2.14 - resolution: "@types/react-dom@npm:18.2.14" + version: 18.2.15 + resolution: "@types/react-dom@npm:18.2.15" dependencies: "@types/react": "npm:*" - checksum: 1f79a7708d038cd651bdb21e01a99c594761bc9a40a565abe98958e1d27facfeb6e9824ddf6ae3504e7a56568f0f3da2380fe52ac18477b5864d2d5cf1386a9e + checksum: 70e86f15f69f89b8f179139ab2e8a8aa9765e742789f5dd5a46fec40d4300ada8fe3349cceda42b9964a018982d7ccb7d791b47f781966c992bfd37da909fbd3 languageName: node linkType: hard "@types/react-helmet@npm:^6.1.6": - version: 6.1.8 - resolution: "@types/react-helmet@npm:6.1.8" + version: 6.1.9 + resolution: "@types/react-helmet@npm:6.1.9" dependencies: "@types/react": "npm:*" - checksum: 56efbf05594437fff87466853b813c0672b33fbed1f995b9eb68e1461875cd5276b36c7fbc171134e9b7b897316123ae59b0263742d5a81e28ead133a4962bd2 + checksum: d1823582903d6e70f1f447c7bec9e844b6f85f5de84cbcde5c8bbeecc064db1394c786ed9b9ded30544afe5c91e57c7e8105171df1643998f64c0aeab9f7f2aa languageName: node linkType: hard "@types/react-immutable-proptypes@npm:^2.1.0": - version: 2.1.2 - resolution: "@types/react-immutable-proptypes@npm:2.1.2" + version: 2.1.3 + resolution: "@types/react-immutable-proptypes@npm:2.1.3" dependencies: "@types/prop-types": "npm:*" immutable: "npm:^3.8.2" - checksum: 8a944de2b4b484e9685495dee7b350a88e4fcba47dcecf97c7b4add6ae66b61fc61c18e877a9b8e8d453e7c4714c481166d5531328f92b2d20d45e11389734ee + checksum: 4dab74a43a2dde9bea6299a999dd600ae82f00082fe2b8865b11e5154e658f72fbb117132fa3753dd9a280dd8032a2574d8e7c94de5e268afdadd50d720086da languageName: node linkType: hard -"@types/react-motion@npm:^0.0.36": - version: 0.0.36 - resolution: "@types/react-motion@npm:0.0.36" +"@types/react-motion@npm:^0.0.37": + version: 0.0.37 + resolution: "@types/react-motion@npm:0.0.37" dependencies: "@types/react": "npm:*" - checksum: b039d9b6773a08292253f1343ee7c1c5ad2885499685aef2a0274a84a7bbe4e198b7aafa62b9c269fb37df9e076ae46ee9717fc53f0c26ca182342aaf1653139 + checksum: 387f60636d9bdd2e765ce94db969cf762a62495e32807f88380748a74e9beeb3d8e17c3ec334dab8040244ea62e7954d5f4d4bdbdd0ecc8985eb4a6ce465b61c languageName: node linkType: hard @@ -3482,29 +3482,29 @@ __metadata: linkType: hard "@types/react-sparklines@npm:^1.7.2": - version: 1.7.4 - resolution: "@types/react-sparklines@npm:1.7.4" + version: 1.7.5 + resolution: "@types/react-sparklines@npm:1.7.5" dependencies: "@types/react": "npm:*" - checksum: 1f50232fbd26d2b81508f68dbe1865503b1f9e053aae4ea2d610c771da219af4e6fa86e58fda542e2762f533042f83acab2d977ae2cb1b518ed94ba31aac8504 + checksum: acb0937ebc06019921ec5254fb125356f206038f5e2f244663eb849c692b6f6413f75ce3ee84be91d8c659ae43c8f743dd5c4397cdea65749cd601a495491242 languageName: node linkType: hard "@types/react-swipeable-views@npm:^0.13.1": - version: 0.13.4 - resolution: "@types/react-swipeable-views@npm:0.13.4" + version: 0.13.5 + resolution: "@types/react-swipeable-views@npm:0.13.5" dependencies: "@types/react": "npm:*" - checksum: 673e96a66443b07c5640c94b214fd0780846ba9e983c821f7cc7c8bdc0ab512e65b6a2af7e4aeb0f565ee5a63f30cf2bdf4d9da7a4d9d3dfe2e27d2d8f6c1200 + checksum: d1dcc78d862f37d30a43d79d915fdb388e05dce0b2ac07462ca4f1b00e0eef37cb41d75997f5685dec79bcce1ffee0dfbc744f20d5266dd3090658def5b4e193 languageName: node linkType: hard "@types/react-test-renderer@npm:^18.0.0": - version: 18.0.5 - resolution: "@types/react-test-renderer@npm:18.0.5" + version: 18.0.6 + resolution: "@types/react-test-renderer@npm:18.0.6" dependencies: "@types/react": "npm:*" - checksum: bd98abad08d04081bcf335fa71c450b9f0535ccba2c1395d11254427e1007a228d481d6868879d23b6239c0fede790de77ef6bf31afc735241e3edc63bb0e865 + checksum: f490d4379e8d095f8fa91700ceb37d0fe5a96d7cc0c51e9d7127fc3d2dc4e37a382dd6215b295b300037985cb8938cb5088130102ad14b79e4622e7e520c5a3b languageName: node linkType: hard @@ -3518,11 +3518,11 @@ __metadata: linkType: hard "@types/react-toggle@npm:^4.0.3": - version: 4.0.4 - resolution: "@types/react-toggle@npm:4.0.4" + version: 4.0.5 + resolution: "@types/react-toggle@npm:4.0.5" dependencies: "@types/react": "npm:*" - checksum: 86015101f1ddfd2c31396a8f4fff7b2b442af886ed1301b4470d864b41bec91c46046d3a4e1d97a838c3069138db2f9628f885a7d9f95199bfa27a46d9df33de + checksum: f557b85c96715b145bcc3beb2903f88ee3a6045ef85da0f80561c7cc2ecdc531e2d4ae121ed8ec3a1761264de25b8410653744093f37abf042201587add7ffa6 languageName: node linkType: hard @@ -3536,30 +3536,30 @@ __metadata: linkType: hard "@types/react@npm:*, @types/react@npm:16 || 17 || 18, @types/react@npm:>=16.9.11, @types/react@npm:^18.2.7": - version: 18.2.36 - resolution: "@types/react@npm:18.2.36" + version: 18.2.37 + resolution: "@types/react@npm:18.2.37" dependencies: "@types/prop-types": "npm:*" "@types/scheduler": "npm:*" csstype: "npm:^3.0.2" - checksum: 02b593041e9c25aaf519b5e4f87282aff559c0f3db214d7db68fee714d8286c09ab1fced68184fbe814e061019024bb479bbcd38b07985e3e794d98c96c49123 + checksum: 79dd5d23da05bec54e7423ca17096e345eb8fd80a3bf8dd916bb5cdd60677d27c298523aa5b245d090fcc4ec100cfd58c1af4631fbac709d0a9d8be75f9d78a9 languageName: node linkType: hard "@types/redux-immutable@npm:^4.0.3": - version: 4.0.5 - resolution: "@types/redux-immutable@npm:4.0.5" + version: 4.0.6 + resolution: "@types/redux-immutable@npm:4.0.6" dependencies: immutable: "npm:^4.0.0-rc.1" redux: "npm:^4.0.0" - checksum: 1fd808b86506b8d32745159397172fcc9e890766b4330a001c35254d695edeb142d2a17c7ad6592853dfccea1840e632634651bcd0af882f445699d1238813dd + checksum: 19972c307f2bbd31a201b28844224ce06e34917823cf2ded3c303cffcced273a2107c4186cc37af9db4f948204fc48c461e609b9f3d2719963049dedae3ebf82 languageName: node linkType: hard "@types/requestidlecallback@npm:^0.3.5": - version: 0.3.6 - resolution: "@types/requestidlecallback@npm:0.3.6" - checksum: 5a3df7a028fc6894446c14a67c93186f7978cf97d41a0da74a56d4c95ca9580593eb7baaaa385fa54d46f3c93f5789b21548a3708a5a0cc08302aa7e7f037e30 + version: 0.3.7 + resolution: "@types/requestidlecallback@npm:0.3.7" + checksum: aa5d1d981d7ddc98b9212c75c89d2ddb2e521077d6c0c5e285d944a8c6ae8baeec30d4d201aec31716d668d3435f884e80e768e28d929a5b87a55097bc21a5e1 languageName: node linkType: hard @@ -3659,9 +3659,9 @@ __metadata: linkType: hard "@types/uuid@npm:^9.0.0": - version: 9.0.6 - resolution: "@types/uuid@npm:9.0.6" - checksum: 8fb6b3a583a035b8e917192eeadaadadfbfd29315094aafd3478e11f11a986cb118ee0f388b15035fda063d9f1a32fa62e7a791215b762fe1e2c177929ca7146 + version: 9.0.7 + resolution: "@types/uuid@npm:9.0.7" + checksum: b329ebd4f9d1d8e08d4f2cc211be4922d70d1149f73d5772630e4a3acfb5170c6d37b3d7a39a0412f1a56e86e8a844c7f297c798b082f90380608bf766688787 languageName: node linkType: hard @@ -3684,8 +3684,8 @@ __metadata: linkType: hard "@types/webpack@npm:^4.41.33": - version: 4.41.35 - resolution: "@types/webpack@npm:4.41.35" + version: 4.41.36 + resolution: "@types/webpack@npm:4.41.36" dependencies: "@types/node": "npm:*" "@types/tapable": "npm:^1" @@ -3693,7 +3693,7 @@ __metadata: "@types/webpack-sources": "npm:*" anymatch: "npm:^3.0.0" source-map: "npm:^0.6.0" - checksum: ec6b9fda027ff6f7f9efeb4bcfb37607a15fe1e2f2f0883e3500fe474ba0a127ea62783cd19859bbef921cfad22cd680e50bcc2c1963482c6486263886877130 + checksum: 9e9021049b8f7482ec7c45e95d7c1da3604ee04481d7550421ed58f201f66edb8eb6c26b85be94ce3f761874cdbb8b570827becdaf5acdb1897aba9b7f226252 languageName: node linkType: hard @@ -3705,11 +3705,11 @@ __metadata: linkType: hard "@types/yargs@npm:^17.0.24, @types/yargs@npm:^17.0.8": - version: 17.0.29 - resolution: "@types/yargs@npm:17.0.29" + version: 17.0.31 + resolution: "@types/yargs@npm:17.0.31" dependencies: "@types/yargs-parser": "npm:*" - checksum: d8c965c101f7ee3e2f301c02a83dfd5680e4d999d3503c788c13f336868f03ee1498f019552e7d357635a1a36912cbe6751a563e9c339075d30cd131dc361c98 + checksum: 1e04df99bd0ad8ac8b3748b6ac0e99a9a4efe20b9cd8eab69ac9503fe87ab9bec312ad56982e969cdb0e2c0679431434ad571f6934049adb15fa35b22810c867 languageName: node linkType: hard From da5940752055369451e545c664aecdfb3821960b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 15:40:50 +0100 Subject: [PATCH 13/63] Update eslint (non-major) (#27831) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 337 +++++++++++++++++++++------------------------------ 2 files changed, 140 insertions(+), 199 deletions(-) diff --git a/package.json b/package.json index 6f20b98bc..2230b20ea 100644 --- a/package.json +++ b/package.json @@ -200,7 +200,7 @@ "eslint-plugin-formatjs": "^4.10.1", "eslint-plugin-import": "~2.29.0", "eslint-plugin-jsdoc": "^46.1.0", - "eslint-plugin-jsx-a11y": "~6.7.1", + "eslint-plugin-jsx-a11y": "~6.8.0", "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-promise": "~6.1.1", "eslint-plugin-react": "~7.33.0", diff --git a/yarn.lock b/yarn.lock index b05b2bfbc..e8fe28fb4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1482,7 +1482,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.22.3, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.3, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": version: 7.23.2 resolution: "@babel/runtime@npm:7.23.2" dependencies: @@ -1697,14 +1697,14 @@ __metadata: languageName: node linkType: hard -"@es-joy/jsdoccomment@npm:~0.40.1": - version: 0.40.1 - resolution: "@es-joy/jsdoccomment@npm:0.40.1" +"@es-joy/jsdoccomment@npm:~0.41.0": + version: 0.41.0 + resolution: "@es-joy/jsdoccomment@npm:0.41.0" dependencies: - comment-parser: "npm:1.4.0" + comment-parser: "npm:1.4.1" esquery: "npm:^1.5.0" jsdoc-type-pratt-parser: "npm:~4.0.0" - checksum: e66b861c55cf26d22c0facef911d65abbbbf633a9fc47cbf0f0faa4226e495cbce5133f4e69f555cd4c018a13dabb37f8a36d631ba768b9297913154b06a04af + checksum: 1fa27531eba32e4699664da53a0865aeeda1f7e83ac156fe53b7a6b09d2f3816baa94a34845ff019c10289b09572bda5519ec917e3e241088975477fa880f72d languageName: node linkType: hard @@ -1790,16 +1790,6 @@ __metadata: languageName: node linkType: hard -"@formatjs/ecma402-abstract@npm:1.17.2": - version: 1.17.2 - resolution: "@formatjs/ecma402-abstract@npm:1.17.2" - dependencies: - "@formatjs/intl-localematcher": "npm:0.4.2" - tslib: "npm:^2.4.0" - checksum: 7086962b6f6fd517188e9640e8439062e125e7bd60852ae91ceadb259fceaa3f73f7403a77411a7c82b1ae60be4bf4b27f793acc4059214adc91b00682d880fe - languageName: node - linkType: hard - "@formatjs/ecma402-abstract@npm:1.17.4": version: 1.17.4 resolution: "@formatjs/ecma402-abstract@npm:1.17.4" @@ -1819,17 +1809,6 @@ __metadata: languageName: node linkType: hard -"@formatjs/icu-messageformat-parser@npm:2.7.0": - version: 2.7.0 - resolution: "@formatjs/icu-messageformat-parser@npm:2.7.0" - dependencies: - "@formatjs/ecma402-abstract": "npm:1.17.2" - "@formatjs/icu-skeleton-parser": "npm:1.6.2" - tslib: "npm:^2.4.0" - checksum: f671d3dfcfa8ada17d14388f21be4cf1c535cbad8aba9cd6a3132d3120424cb7fb090f67b27e44ffbd0c7d4bba9f20e76b74cfca87f4ab945939e7ea3acb878c - languageName: node - linkType: hard - "@formatjs/icu-messageformat-parser@npm:2.7.2": version: 2.7.2 resolution: "@formatjs/icu-messageformat-parser@npm:2.7.2" @@ -1841,16 +1820,6 @@ __metadata: languageName: node linkType: hard -"@formatjs/icu-skeleton-parser@npm:1.6.2": - version: 1.6.2 - resolution: "@formatjs/icu-skeleton-parser@npm:1.6.2" - dependencies: - "@formatjs/ecma402-abstract": "npm:1.17.2" - tslib: "npm:^2.4.0" - checksum: 2a2a56f49a15e8122b37237d5e09a817e01149ae353e1b5fe8721d1789dbaee85995c897d3aa9e5b400e3ee05b5fd4c0721b3ad49b1d128954dfed873a793153 - languageName: node - linkType: hard - "@formatjs/icu-skeleton-parser@npm:1.6.4": version: 1.6.4 resolution: "@formatjs/icu-skeleton-parser@npm:1.6.4" @@ -1883,15 +1852,6 @@ __metadata: languageName: node linkType: hard -"@formatjs/intl-localematcher@npm:0.4.2": - version: 0.4.2 - resolution: "@formatjs/intl-localematcher@npm:0.4.2" - dependencies: - tslib: "npm:^2.4.0" - checksum: 2521fa48a95a80e3bedc0d444fb2ef67e1215e0bf9e6d16020c4a22af6973849a71c7a29a10cb74fc67b818967e9f8672062760e808e70873132277830e0ec67 - languageName: node - linkType: hard - "@formatjs/intl-localematcher@npm:0.5.1": version: 0.5.1 resolution: "@formatjs/intl-localematcher@npm:0.5.1" @@ -1932,26 +1892,6 @@ __metadata: languageName: node linkType: hard -"@formatjs/ts-transformer@npm:3.13.6": - version: 3.13.6 - resolution: "@formatjs/ts-transformer@npm:3.13.6" - dependencies: - "@formatjs/icu-messageformat-parser": "npm:2.7.0" - "@types/json-stable-stringify": "npm:^1.0.32" - "@types/node": "npm:14 || 16 || 17" - chalk: "npm:^4.0.0" - json-stable-stringify: "npm:^1.0.1" - tslib: "npm:^2.4.0" - typescript: "npm:5" - peerDependencies: - ts-jest: ">=27" - peerDependenciesMeta: - ts-jest: - optional: true - checksum: 76cd99713a974bf63081d9f7b98997dba0fc998205b5bc7cb6249dc8572549408b62a42c394b6d589905368203b37862e0c9248eef5aaa94b8de0b423fa8b508 - languageName: node - linkType: hard - "@formatjs/ts-transformer@npm:3.13.8": version: 3.13.8 resolution: "@formatjs/ts-transformer@npm:3.13.8" @@ -2432,7 +2372,7 @@ __metadata: eslint-plugin-formatjs: "npm:^4.10.1" eslint-plugin-import: "npm:~2.29.0" eslint-plugin-jsdoc: "npm:^46.1.0" - eslint-plugin-jsx-a11y: "npm:~6.7.1" + eslint-plugin-jsx-a11y: "npm:~6.8.0" eslint-plugin-prettier: "npm:^5.0.0" eslint-plugin-promise: "npm:~6.1.1" eslint-plugin-react: "npm:~7.33.0" @@ -3714,14 +3654,14 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^6.0.0": - version: 6.9.1 - resolution: "@typescript-eslint/eslint-plugin@npm:6.9.1" + version: 6.10.0 + resolution: "@typescript-eslint/eslint-plugin@npm:6.10.0" dependencies: "@eslint-community/regexpp": "npm:^4.5.1" - "@typescript-eslint/scope-manager": "npm:6.9.1" - "@typescript-eslint/type-utils": "npm:6.9.1" - "@typescript-eslint/utils": "npm:6.9.1" - "@typescript-eslint/visitor-keys": "npm:6.9.1" + "@typescript-eslint/scope-manager": "npm:6.10.0" + "@typescript-eslint/type-utils": "npm:6.10.0" + "@typescript-eslint/utils": "npm:6.10.0" + "@typescript-eslint/visitor-keys": "npm:6.10.0" debug: "npm:^4.3.4" graphemer: "npm:^1.4.0" ignore: "npm:^5.2.4" @@ -3734,44 +3674,44 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: f2455fe74f8c90d82df97801ee5e17bb3d81b1a59d23eedc7cad95b6eed30180339107bef0460578d5cc587033bb388fd112bae48b6b85f504fe4521f365ac69 + checksum: f50b17cb753afbfc99549d38585eba8558949b977eb4661dd584e73ee946b3dbe944c9e3b12a233fa06b5e1c7d101730ac88a00c7a91b0a7f1e2c37a98e13c7a languageName: node linkType: hard "@typescript-eslint/parser@npm:^6.0.0": - version: 6.9.1 - resolution: "@typescript-eslint/parser@npm:6.9.1" + version: 6.10.0 + resolution: "@typescript-eslint/parser@npm:6.10.0" dependencies: - "@typescript-eslint/scope-manager": "npm:6.9.1" - "@typescript-eslint/types": "npm:6.9.1" - "@typescript-eslint/typescript-estree": "npm:6.9.1" - "@typescript-eslint/visitor-keys": "npm:6.9.1" + "@typescript-eslint/scope-manager": "npm:6.10.0" + "@typescript-eslint/types": "npm:6.10.0" + "@typescript-eslint/typescript-estree": "npm:6.10.0" + "@typescript-eslint/visitor-keys": "npm:6.10.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: a6896655b2005a55e15dd3bb8b8239e1cb1bb0379037f6af2409e910c426cf9cda5490d45cd1857a3ca7fe2727acc8250d8196770147a4dc274e4c700ead9d9c + checksum: fd86c31dfdde03636393a3a9cf16716856bb506923069f34d87af14fac363a33578f47476a15d272e4d7a764de00fd905ee11361cc06b81b302a9fa8ebe4c23c languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.9.1": - version: 6.9.1 - resolution: "@typescript-eslint/scope-manager@npm:6.9.1" +"@typescript-eslint/scope-manager@npm:6.10.0": + version: 6.10.0 + resolution: "@typescript-eslint/scope-manager@npm:6.10.0" dependencies: - "@typescript-eslint/types": "npm:6.9.1" - "@typescript-eslint/visitor-keys": "npm:6.9.1" - checksum: 53fa7c3813d22b119e464f9b6d7d23407dfe103ee8ad2dcacf9ad6d656fda20e2bb3346df39e62b0e6b6ce71572ce5838071c5d2cca6daa4e0ce117ff22eafe5 + "@typescript-eslint/types": "npm:6.10.0" + "@typescript-eslint/visitor-keys": "npm:6.10.0" + checksum: a5fbee770d763852a7f426b950d495529139f1629fdcb30136c93f787acd82236db4272f78dff1d05a3a10a6406472ae95ae94ab75cfb618a06d75b8cc536cbf languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.9.1": - version: 6.9.1 - resolution: "@typescript-eslint/type-utils@npm:6.9.1" +"@typescript-eslint/type-utils@npm:6.10.0": + version: 6.10.0 + resolution: "@typescript-eslint/type-utils@npm:6.10.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:6.9.1" - "@typescript-eslint/utils": "npm:6.9.1" + "@typescript-eslint/typescript-estree": "npm:6.10.0" + "@typescript-eslint/utils": "npm:6.10.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.0.1" peerDependencies: @@ -3779,23 +3719,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 9373c32c9bce736527e01baabc1dbee4b7f43774ebdcbbe20ee9cf61d1b01e7faab3d5df1ebbe75241308c52eabbc9500dd7826701f95caee4054ca659420304 + checksum: f7c425d4da4d53d78b3d6630216dc1f2809f8dcaed62dc3cf12252102a53103a2aa39a160b310ca1cedebf87b8c339013be0c2360710c7c836b775374730c10e languageName: node linkType: hard -"@typescript-eslint/types@npm:6.9.1": - version: 6.9.1 - resolution: "@typescript-eslint/types@npm:6.9.1" - checksum: 4ba21ba18e256da210a4caedfbc5d4927cf8cb4f2c4d74f8ccc865576f3659b974e79119d3c94db2b68a4cec9cd687e43971d355450b7082d6d1736a5dd6db85 +"@typescript-eslint/types@npm:6.10.0": + version: 6.10.0 + resolution: "@typescript-eslint/types@npm:6.10.0" + checksum: 30f47de625405b3729db6d26a0376d98628bd966c70ca01fab1adcef91bba810d27ce643d844e42d1cc77bb2c6277e62efe278a090da63ba748dfe5710c4757b languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.9.1": - version: 6.9.1 - resolution: "@typescript-eslint/typescript-estree@npm:6.9.1" +"@typescript-eslint/typescript-estree@npm:6.10.0": + version: 6.10.0 + resolution: "@typescript-eslint/typescript-estree@npm:6.10.0" dependencies: - "@typescript-eslint/types": "npm:6.9.1" - "@typescript-eslint/visitor-keys": "npm:6.9.1" + "@typescript-eslint/types": "npm:6.10.0" + "@typescript-eslint/visitor-keys": "npm:6.10.0" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" @@ -3804,34 +3744,34 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 850b1865a90107879186c3f2969968a2c08fc6fcc56d146483c297cf5be376e33d505ac81533ba8e8103ca4d2edfea7d21b178de9e52217f7ee2922f51a445fa + checksum: ca28ca5a55e2d431c649ad093e4a4302f2b37c430bbeebbe622b05c727fd14dab136aead5a96848499d3ff4d187889733f8871b8dd5205d19bed4a260ad74544 languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.9.1, @typescript-eslint/utils@npm:^6.5.0": - version: 6.9.1 - resolution: "@typescript-eslint/utils@npm:6.9.1" +"@typescript-eslint/utils@npm:6.10.0, @typescript-eslint/utils@npm:^6.5.0": + version: 6.10.0 + resolution: "@typescript-eslint/utils@npm:6.10.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" "@types/json-schema": "npm:^7.0.12" "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:6.9.1" - "@typescript-eslint/types": "npm:6.9.1" - "@typescript-eslint/typescript-estree": "npm:6.9.1" + "@typescript-eslint/scope-manager": "npm:6.10.0" + "@typescript-eslint/types": "npm:6.10.0" + "@typescript-eslint/typescript-estree": "npm:6.10.0" semver: "npm:^7.5.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: 3d329d54c3d155ed29e2b456a602aef76bda1b88dfcf847145849362e4ddefabe5c95de236de750d08d5da9bedcfb2131bdfd784ce4eb87cf82728f0b6662033 + checksum: 809a1d08b154f76ed7a99edddf872369f6ed93987cea19a18cb9f12b8390bddcff9138d9d94955545da54488d59e0001054bec13baf6d858a1761b059480b887 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.9.1": - version: 6.9.1 - resolution: "@typescript-eslint/visitor-keys@npm:6.9.1" +"@typescript-eslint/visitor-keys@npm:6.10.0": + version: 6.10.0 + resolution: "@typescript-eslint/visitor-keys@npm:6.10.0" dependencies: - "@typescript-eslint/types": "npm:6.9.1" + "@typescript-eslint/types": "npm:6.10.0" eslint-visitor-keys: "npm:^3.4.1" - checksum: ac5f375a177add30489e5b63cafa8d82a196b33624bb36418422ebe0d7973b3ba550dc7e0dda36ea75a94cf9b200b4fb5f5fb4d77c027fd801201c1a269d343b + checksum: f9223c148655ce00bb17db8aa92ee964e62c75d15095893e0b4d653c60a4033f456329b06de3eab4b404d8df359904f0dd6e3c8c842885c6d130e28ccd95ce03 languageName: node linkType: hard @@ -4347,7 +4287,7 @@ __metadata: languageName: node linkType: hard -"aria-query@npm:^5.0.0, aria-query@npm:^5.1.3": +"aria-query@npm:^5.0.0, aria-query@npm:^5.3.0": version: 5.3.0 resolution: "aria-query@npm:5.3.0" dependencies: @@ -4565,10 +4505,10 @@ __metadata: languageName: node linkType: hard -"ast-types-flow@npm:^0.0.7": - version: 0.0.7 - resolution: "ast-types-flow@npm:0.0.7" - checksum: f381529f2da535949ba6cceddbdfaa33b4d5105842e147ec63582f560ea9ecc1a08f66457664f3109841d3053641fa8b9fa94ba607f1ea9f6c804fe5dee44a1d +"ast-types-flow@npm:^0.0.8": + version: 0.0.8 + resolution: "ast-types-flow@npm:0.0.8" + checksum: f2a0ba8055353b743c41431974521e5e852a9824870cd6fce2db0e538ac7bf4da406bbd018d109af29ff3f8f0993f6a730c9eddbd0abd031fbcb29ca75c1014e languageName: node linkType: hard @@ -4675,10 +4615,10 @@ __metadata: languageName: node linkType: hard -"axe-core@npm:^4.6.2": - version: 4.7.2 - resolution: "axe-core@npm:4.7.2" - checksum: 8dfc61f038fbd9623ae8a264c8a475d887113a027fb440a2b377b82ffd300e71d1a0bcf042ff13b517a8d548b34c44b4159eff693725c5d7cde240d0aa68feac +"axe-core@npm:=4.7.0": + version: 4.7.0 + resolution: "axe-core@npm:4.7.0" + checksum: 89ac5712b5932ac7d23398b4cb5ba081c394a086e343acc68ba49c83472706e18e0799804e8388c779dcdacc465377deb29f2714241d3fbb389cf3a6b275c9ba languageName: node linkType: hard @@ -4693,7 +4633,7 @@ __metadata: languageName: node linkType: hard -"axobject-query@npm:^3.1.1": +"axobject-query@npm:^3.2.1": version: 3.2.1 resolution: "axobject-query@npm:3.2.1" dependencies: @@ -5807,10 +5747,10 @@ __metadata: languageName: node linkType: hard -"comment-parser@npm:1.4.0": - version: 1.4.0 - resolution: "comment-parser@npm:1.4.0" - checksum: c87ba95d5ff9ae380ed7aab2aa8490303652d535c0cff5b1f16a97be0633d0827d689b5e854b0003fbb6341ce22caf000a03eb1badcdfbb142d7aea8f921c12b +"comment-parser@npm:1.4.1": + version: 1.4.1 + resolution: "comment-parser@npm:1.4.1" + checksum: d6c4be3f5be058f98b24f2d557f745d8fe1cc9eb75bebbdccabd404a0e1ed41563171b16285f593011f8b6a5ec81f564fb1f2121418ac5cbf0f49255bf0840dd languageName: node linkType: hard @@ -6614,13 +6554,14 @@ __metadata: languageName: node linkType: hard -"define-properties@npm:^1.1.3, define-properties@npm:^1.1.4, define-properties@npm:^1.2.0": - version: 1.2.0 - resolution: "define-properties@npm:1.2.0" +"define-properties@npm:^1.1.3, define-properties@npm:^1.1.4, define-properties@npm:^1.2.0, define-properties@npm:^1.2.1": + version: 1.2.1 + resolution: "define-properties@npm:1.2.1" dependencies: + define-data-property: "npm:^1.0.1" has-property-descriptors: "npm:^1.0.0" object-keys: "npm:^1.1.1" - checksum: 34b58cae4651936a3c8c720310ce393a3227f5123640ab5402e7d6e59bb44f8295b789cb5d74e7513682b2e60ff20586d6f52b726d964d617abffa3da76344e0 + checksum: 88a152319ffe1396ccc6ded510a3896e77efac7a1bfbaa174a7b00414a1747377e0bb525d303794a47cf30e805c2ec84e575758512c6e44a993076d29fd4e6c3 languageName: node linkType: hard @@ -7160,7 +7101,7 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.17.2, es-abstract@npm:^1.20.4, es-abstract@npm:^1.21.2, es-abstract@npm:^1.21.3, es-abstract@npm:^1.22.1": +"es-abstract@npm:^1.17.2, es-abstract@npm:^1.20.4, es-abstract@npm:^1.21.2, es-abstract@npm:^1.22.1": version: 1.22.3 resolution: "es-abstract@npm:1.22.3" dependencies: @@ -7231,14 +7172,14 @@ __metadata: languageName: node linkType: hard -"es-iterator-helpers@npm:^1.0.12": - version: 1.0.13 - resolution: "es-iterator-helpers@npm:1.0.13" +"es-iterator-helpers@npm:^1.0.12, es-iterator-helpers@npm:^1.0.15": + version: 1.0.15 + resolution: "es-iterator-helpers@npm:1.0.15" dependencies: asynciterator.prototype: "npm:^1.0.0" call-bind: "npm:^1.0.2" - define-properties: "npm:^1.2.0" - es-abstract: "npm:^1.21.3" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.22.1" es-set-tostringtag: "npm:^2.0.1" function-bind: "npm:^1.1.1" get-intrinsic: "npm:^1.2.1" @@ -7247,9 +7188,9 @@ __metadata: has-proto: "npm:^1.0.1" has-symbols: "npm:^1.0.3" internal-slot: "npm:^1.0.5" - iterator.prototype: "npm:^1.1.0" - safe-array-concat: "npm:^1.0.0" - checksum: e6109017c432376294d5d6849cd0a5f8d9bcf5819eea612026e4401bb362d798c01e7a8984702b87d9d689c07b1146a31a99f17a761ca4e7e6470d9e8db9bea8 + iterator.prototype: "npm:^1.1.2" + safe-array-concat: "npm:^1.0.1" + checksum: b4c83f94bfe624260d5238092de3173989f76f1416b1d02c388aea3b2024174e5f5f0e864057311ac99790b57e836ca3545b6e77256b26066dac944519f5e6d6 languageName: node linkType: hard @@ -7390,11 +7331,11 @@ __metadata: linkType: hard "eslint-plugin-formatjs@npm:^4.10.1": - version: 4.11.0 - resolution: "eslint-plugin-formatjs@npm:4.11.0" + version: 4.11.2 + resolution: "eslint-plugin-formatjs@npm:4.11.2" dependencies: - "@formatjs/icu-messageformat-parser": "npm:2.7.0" - "@formatjs/ts-transformer": "npm:3.13.6" + "@formatjs/icu-messageformat-parser": "npm:2.7.2" + "@formatjs/ts-transformer": "npm:3.13.8" "@types/eslint": "npm:7 || 8" "@types/picomatch": "npm:^2.3.0" "@typescript-eslint/utils": "npm:^6.5.0" @@ -7406,7 +7347,7 @@ __metadata: unicode-emoji-utils: "npm:^1.1.1" peerDependencies: eslint: 7 || 8 - checksum: 4b1afb99d1d46e5e2200669a3918c0238b075c89ac9790f3339d9231f86c093cdd0560b7bfa79e81f99b390b9beab68b3dc8cb8dac57285db2f9b120e7f2667a + checksum: f5a6bffd9c65b9ce765be74d384618e543388720036b070d69d93c00b8c2bfded543141affc7793bf402f2c9177e2bbc395a7d1e8b806a40bfde1744c282a13c languageName: node linkType: hard @@ -7438,12 +7379,12 @@ __metadata: linkType: hard "eslint-plugin-jsdoc@npm:^46.1.0": - version: 46.8.2 - resolution: "eslint-plugin-jsdoc@npm:46.8.2" + version: 46.9.0 + resolution: "eslint-plugin-jsdoc@npm:46.9.0" dependencies: - "@es-joy/jsdoccomment": "npm:~0.40.1" + "@es-joy/jsdoccomment": "npm:~0.41.0" are-docs-informative: "npm:^0.0.2" - comment-parser: "npm:1.4.0" + comment-parser: "npm:1.4.1" debug: "npm:^4.3.4" escape-string-regexp: "npm:^4.0.0" esquery: "npm:^1.5.0" @@ -7452,33 +7393,33 @@ __metadata: spdx-expression-parse: "npm:^3.0.1" peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: ccf38567ddd73d7c57bf144d0fe9c1fe4a54e407353b3577bf036e9919a8ef96e7e385834ee383b64c7c2090b15a0f84a55b9dc5f50539ff399a3f7b91d26b48 + checksum: 4566b0f9bda54b446c813cf5ea93ae6d5866cbc4d448cb957b9ce2563f934d3ed2ed4e665e5a870750860a57137a1714c38599c35c60be16dce0f8e5a75b6ff6 languageName: node linkType: hard -"eslint-plugin-jsx-a11y@npm:~6.7.1": - version: 6.7.1 - resolution: "eslint-plugin-jsx-a11y@npm:6.7.1" +"eslint-plugin-jsx-a11y@npm:~6.8.0": + version: 6.8.0 + resolution: "eslint-plugin-jsx-a11y@npm:6.8.0" dependencies: - "@babel/runtime": "npm:^7.20.7" - aria-query: "npm:^5.1.3" - array-includes: "npm:^3.1.6" - array.prototype.flatmap: "npm:^1.3.1" - ast-types-flow: "npm:^0.0.7" - axe-core: "npm:^4.6.2" - axobject-query: "npm:^3.1.1" + "@babel/runtime": "npm:^7.23.2" + aria-query: "npm:^5.3.0" + array-includes: "npm:^3.1.7" + array.prototype.flatmap: "npm:^1.3.2" + ast-types-flow: "npm:^0.0.8" + axe-core: "npm:=4.7.0" + axobject-query: "npm:^3.2.1" damerau-levenshtein: "npm:^1.0.8" emoji-regex: "npm:^9.2.2" - has: "npm:^1.0.3" - jsx-ast-utils: "npm:^3.3.3" - language-tags: "npm:=1.0.5" + es-iterator-helpers: "npm:^1.0.15" + hasown: "npm:^2.0.0" + jsx-ast-utils: "npm:^3.3.5" + language-tags: "npm:^1.0.9" minimatch: "npm:^3.1.2" - object.entries: "npm:^1.1.6" - object.fromentries: "npm:^2.0.6" - semver: "npm:^6.3.0" + object.entries: "npm:^1.1.7" + object.fromentries: "npm:^2.0.7" peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - checksum: 41ad3d0c8036b36cd475685c1ad639157f403b16e8ac23c07f1dbe0226ccf8458f2805cbd5cc8e56856a5d8a356f3276e3139274d819476ccad80c41b9245502 + checksum: 199b883e526e6f9d7c54cb3f094abc54f11a1ec816db5fb6cae3b938eb0e503acc10ccba91ca7451633a9d0b9abc0ea03601844a8aba5fe88c5e8897c9ac8f49 languageName: node linkType: hard @@ -10039,16 +9980,16 @@ __metadata: languageName: node linkType: hard -"iterator.prototype@npm:^1.1.0": - version: 1.1.0 - resolution: "iterator.prototype@npm:1.1.0" +"iterator.prototype@npm:^1.1.2": + version: 1.1.2 + resolution: "iterator.prototype@npm:1.1.2" dependencies: - define-properties: "npm:^1.1.4" - get-intrinsic: "npm:^1.1.3" + define-properties: "npm:^1.2.1" + get-intrinsic: "npm:^1.2.1" has-symbols: "npm:^1.0.3" - has-tostringtag: "npm:^1.0.0" - reflect.getprototypeof: "npm:^1.0.3" - checksum: fd641c4cc8cf85a1f99c772722589393b6b59562c7b73cae6bea26e0814b9bdd095d40818f061b85a4f386ecebee92f9a01ba79a70951d72bd3dd3e01a6c624c + reflect.getprototypeof: "npm:^1.0.4" + set-function-name: "npm:^2.0.1" + checksum: a32151326095e916f306990d909f6bbf23e3221999a18ba686419535dcd1749b10ded505e89334b77dc4c7a58a8508978f0eb16c2c8573e6d412eb7eb894ea79 languageName: node linkType: hard @@ -10807,7 +10748,7 @@ __metadata: languageName: node linkType: hard -"jsx-ast-utils@npm:^2.4.1 || ^3.0.0, jsx-ast-utils@npm:^3.3.3": +"jsx-ast-utils@npm:^2.4.1 || ^3.0.0, jsx-ast-utils@npm:^3.3.5": version: 3.3.5 resolution: "jsx-ast-utils@npm:3.3.5" dependencies: @@ -10870,19 +10811,19 @@ __metadata: languageName: node linkType: hard -"language-subtag-registry@npm:~0.3.2": +"language-subtag-registry@npm:^0.3.20": version: 0.3.22 resolution: "language-subtag-registry@npm:0.3.22" checksum: d1e09971260a7cd3b9fdeb190d33af0b6e99c8697013537d9aaa15f7856d9d83aee128ba8078e219df0a7cf4b8dd18d1a0c188f6543b500d92a2689d2d114b70 languageName: node linkType: hard -"language-tags@npm:=1.0.5": - version: 1.0.5 - resolution: "language-tags@npm:1.0.5" +"language-tags@npm:^1.0.9": + version: 1.0.9 + resolution: "language-tags@npm:1.0.9" dependencies: - language-subtag-registry: "npm:~0.3.2" - checksum: 04215e821af9a8f1bc6c99ab5aa0a316c3fe1912ca3337eb28596316064bddd8edd22f2883d866069ebdf01b2002e504a760a336b2c728b6d30514e86744f76c + language-subtag-registry: "npm:^0.3.20" + checksum: 9ab911213c4bd8bd583c850201c17794e52cb0660d1ab6e32558aadc8324abebf6844e46f92b80a5d600d0fbba7eface2c207bfaf270a1c7fd539e4c3a880bff languageName: node linkType: hard @@ -12133,14 +12074,14 @@ __metadata: languageName: node linkType: hard -"object.entries@npm:^1.1.6": - version: 1.1.6 - resolution: "object.entries@npm:1.1.6" +"object.entries@npm:^1.1.6, object.entries@npm:^1.1.7": + version: 1.1.7 + resolution: "object.entries@npm:1.1.7" dependencies: call-bind: "npm:^1.0.2" - define-properties: "npm:^1.1.4" - es-abstract: "npm:^1.20.4" - checksum: 8782c71db3a068ccbae9e0541e6b4ac2c25dc67c63f97b7e6ad3c88271d7820197e7398e37747f96542ed47c27f0b81148cdf14c42df15dc22f64818ae7bb5bf + define-properties: "npm:^1.2.0" + es-abstract: "npm:^1.22.1" + checksum: 3ad1899cc7bf14546bf28f4a9b363ae8690b90948fcfbcac4c808395435d760f26193d9cae95337ce0e3c1e5c1f4fa45f7b46b31b68d389e9e117fce38775d86 languageName: node linkType: hard @@ -14193,17 +14134,17 @@ __metadata: languageName: node linkType: hard -"reflect.getprototypeof@npm:^1.0.3": - version: 1.0.3 - resolution: "reflect.getprototypeof@npm:1.0.3" +"reflect.getprototypeof@npm:^1.0.4": + version: 1.0.4 + resolution: "reflect.getprototypeof@npm:1.0.4" dependencies: call-bind: "npm:^1.0.2" - define-properties: "npm:^1.1.4" - es-abstract: "npm:^1.20.4" - get-intrinsic: "npm:^1.1.1" + define-properties: "npm:^1.2.0" + es-abstract: "npm:^1.22.1" + get-intrinsic: "npm:^1.2.1" globalthis: "npm:^1.0.3" which-builtin-type: "npm:^1.1.3" - checksum: 6300460adb743c5e710f3d0b9c2f49206a4f2a8cc61640e58565d13df3659747e82a88758666f5d32ed449ac3647cfcf0bbd48b574ceed8cb2ea14f20a719580 + checksum: 02104cdd22658b637efe6b1df73658edab539268347327c8250a72d0cb273dcdf280c284e2d94155d22601d022d16be1a816a8616d679e447cbcbde9860d15cb languageName: node linkType: hard @@ -14921,7 +14862,7 @@ __metadata: languageName: node linkType: hard -"set-function-name@npm:^2.0.0": +"set-function-name@npm:^2.0.0, set-function-name@npm:^2.0.1": version: 2.0.1 resolution: "set-function-name@npm:2.0.1" dependencies: From 0945e25b8f4ac46f6522277a3461044f37c7139b Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 13 Nov 2023 09:53:22 -0500 Subject: [PATCH 14/63] Add `Api::V1::Statuses::BaseController` base controller class (#27794) --- .../api/v1/statuses/base_controller.rb | 16 ++++++++++++++++ .../api/v1/statuses/bookmarks_controller.rb | 15 ++------------- .../favourited_by_accounts_controller.rb | 12 +----------- .../api/v1/statuses/favourites_controller.rb | 15 ++------------- .../api/v1/statuses/histories_controller.rb | 12 +----------- .../api/v1/statuses/mutes_controller.rb | 12 +----------- .../api/v1/statuses/pins_controller.rb | 9 +-------- .../statuses/reblogged_by_accounts_controller.rb | 12 +----------- .../api/v1/statuses/reblogs_controller.rb | 4 ++-- .../api/v1/statuses/sources_controller.rb | 14 +------------- .../api/v1/statuses/translations_controller.rb | 12 +----------- 11 files changed, 29 insertions(+), 104 deletions(-) create mode 100644 app/controllers/api/v1/statuses/base_controller.rb diff --git a/app/controllers/api/v1/statuses/base_controller.rb b/app/controllers/api/v1/statuses/base_controller.rb new file mode 100644 index 000000000..3f56b68bc --- /dev/null +++ b/app/controllers/api/v1/statuses/base_controller.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class Api::V1::Statuses::BaseController < Api::BaseController + include Authorization + + before_action :set_status + + private + + def set_status + @status = Status.find(params[:status_id]) + authorize @status, :show? + rescue Mastodon::NotPermittedError + not_found + end +end diff --git a/app/controllers/api/v1/statuses/bookmarks_controller.rb b/app/controllers/api/v1/statuses/bookmarks_controller.rb index 19963c002..109b12f46 100644 --- a/app/controllers/api/v1/statuses/bookmarks_controller.rb +++ b/app/controllers/api/v1/statuses/bookmarks_controller.rb @@ -1,11 +1,9 @@ # frozen_string_literal: true -class Api::V1::Statuses::BookmarksController < Api::BaseController - include Authorization - +class Api::V1::Statuses::BookmarksController < Api::V1::Statuses::BaseController before_action -> { doorkeeper_authorize! :write, :'write:bookmarks' } before_action :require_user! - before_action :set_status, only: [:create] + skip_before_action :set_status, only: [:destroy] def create current_account.bookmarks.find_or_create_by!(account: current_account, status: @status) @@ -28,13 +26,4 @@ class Api::V1::Statuses::BookmarksController < Api::BaseController rescue Mastodon::NotPermittedError not_found end - - private - - def set_status - @status = Status.find(params[:status_id]) - authorize @status, :show? - rescue Mastodon::NotPermittedError - not_found - end end diff --git a/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb b/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb index 73eb11e71..3cca246ce 100644 --- a/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb +++ b/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb @@ -1,10 +1,7 @@ # frozen_string_literal: true -class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController - include Authorization - +class Api::V1::Statuses::FavouritedByAccountsController < Api::V1::Statuses::BaseController before_action -> { authorize_if_got_token! :read, :'read:accounts' } - before_action :set_status after_action :insert_pagination_headers def index @@ -61,13 +58,6 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) end - def set_status - @status = Status.find(params[:status_id]) - authorize @status, :show? - rescue Mastodon::NotPermittedError - not_found - end - def pagination_params(core_params) params.slice(:limit).permit(:limit).merge(core_params) end diff --git a/app/controllers/api/v1/statuses/favourites_controller.rb b/app/controllers/api/v1/statuses/favourites_controller.rb index f3428e3df..dbc75a036 100644 --- a/app/controllers/api/v1/statuses/favourites_controller.rb +++ b/app/controllers/api/v1/statuses/favourites_controller.rb @@ -1,11 +1,9 @@ # frozen_string_literal: true -class Api::V1::Statuses::FavouritesController < Api::BaseController - include Authorization - +class Api::V1::Statuses::FavouritesController < Api::V1::Statuses::BaseController before_action -> { doorkeeper_authorize! :write, :'write:favourites' } before_action :require_user! - before_action :set_status, only: [:create] + skip_before_action :set_status, only: [:destroy] def create FavouriteService.new.call(current_account, @status) @@ -30,13 +28,4 @@ class Api::V1::Statuses::FavouritesController < Api::BaseController rescue Mastodon::NotPermittedError not_found end - - private - - def set_status - @status = Status.find(params[:status_id]) - authorize @status, :show? - rescue Mastodon::NotPermittedError - not_found - end end diff --git a/app/controllers/api/v1/statuses/histories_controller.rb b/app/controllers/api/v1/statuses/histories_controller.rb index 2913472b0..dcb21ef04 100644 --- a/app/controllers/api/v1/statuses/histories_controller.rb +++ b/app/controllers/api/v1/statuses/histories_controller.rb @@ -1,10 +1,7 @@ # frozen_string_literal: true -class Api::V1::Statuses::HistoriesController < Api::BaseController - include Authorization - +class Api::V1::Statuses::HistoriesController < Api::V1::Statuses::BaseController before_action -> { authorize_if_got_token! :read, :'read:statuses' } - before_action :set_status def show cache_if_unauthenticated! @@ -16,11 +13,4 @@ class Api::V1::Statuses::HistoriesController < Api::BaseController def status_edits @status.edits.includes(:account, status: [:account]).to_a.presence || [@status.build_snapshot(at_time: @status.edited_at || @status.created_at)] end - - def set_status - @status = Status.find(params[:status_id]) - authorize @status, :show? - rescue Mastodon::NotPermittedError - not_found - end end diff --git a/app/controllers/api/v1/statuses/mutes_controller.rb b/app/controllers/api/v1/statuses/mutes_controller.rb index 87071a2b9..26b92bb8a 100644 --- a/app/controllers/api/v1/statuses/mutes_controller.rb +++ b/app/controllers/api/v1/statuses/mutes_controller.rb @@ -1,11 +1,8 @@ # frozen_string_literal: true -class Api::V1::Statuses::MutesController < Api::BaseController - include Authorization - +class Api::V1::Statuses::MutesController < Api::V1::Statuses::BaseController before_action -> { doorkeeper_authorize! :write, :'write:mutes' } before_action :require_user! - before_action :set_status before_action :set_conversation def create @@ -24,13 +21,6 @@ class Api::V1::Statuses::MutesController < Api::BaseController private - def set_status - @status = Status.find(params[:status_id]) - authorize @status, :show? - rescue Mastodon::NotPermittedError - not_found - end - def set_conversation @conversation = @status.conversation raise Mastodon::ValidationError if @conversation.nil? diff --git a/app/controllers/api/v1/statuses/pins_controller.rb b/app/controllers/api/v1/statuses/pins_controller.rb index 51b1621b6..7107890af 100644 --- a/app/controllers/api/v1/statuses/pins_controller.rb +++ b/app/controllers/api/v1/statuses/pins_controller.rb @@ -1,11 +1,8 @@ # frozen_string_literal: true -class Api::V1::Statuses::PinsController < Api::BaseController - include Authorization - +class Api::V1::Statuses::PinsController < Api::V1::Statuses::BaseController before_action -> { doorkeeper_authorize! :write, :'write:accounts' } before_action :require_user! - before_action :set_status def create StatusPin.create!(account: current_account, status: @status) @@ -26,10 +23,6 @@ class Api::V1::Statuses::PinsController < Api::BaseController private - def set_status - @status = Status.find(params[:status_id]) - end - def distribute_add_activity! json = ActiveModelSerializers::SerializableResource.new( @status, diff --git a/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb b/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb index 41672e753..dd3e60846 100644 --- a/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb +++ b/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb @@ -1,10 +1,7 @@ # frozen_string_literal: true -class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController - include Authorization - +class Api::V1::Statuses::RebloggedByAccountsController < Api::V1::Statuses::BaseController before_action -> { authorize_if_got_token! :read, :'read:accounts' } - before_action :set_status after_action :insert_pagination_headers def index @@ -57,13 +54,6 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) end - def set_status - @status = Status.find(params[:status_id]) - authorize @status, :show? - rescue Mastodon::NotPermittedError - not_found - end - def pagination_params(core_params) params.slice(:limit).permit(:limit).merge(core_params) end diff --git a/app/controllers/api/v1/statuses/reblogs_controller.rb b/app/controllers/api/v1/statuses/reblogs_controller.rb index 3ca623117..971b054c5 100644 --- a/app/controllers/api/v1/statuses/reblogs_controller.rb +++ b/app/controllers/api/v1/statuses/reblogs_controller.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true -class Api::V1::Statuses::ReblogsController < Api::BaseController - include Authorization +class Api::V1::Statuses::ReblogsController < Api::V1::Statuses::BaseController include Redisable include Lockable before_action -> { doorkeeper_authorize! :write, :'write:statuses' } before_action :require_user! before_action :set_reblog, only: [:create] + skip_before_action :set_status override_rate_limit_headers :create, family: :statuses diff --git a/app/controllers/api/v1/statuses/sources_controller.rb b/app/controllers/api/v1/statuses/sources_controller.rb index 434086451..5ceda4c7e 100644 --- a/app/controllers/api/v1/statuses/sources_controller.rb +++ b/app/controllers/api/v1/statuses/sources_controller.rb @@ -1,21 +1,9 @@ # frozen_string_literal: true -class Api::V1::Statuses::SourcesController < Api::BaseController - include Authorization - +class Api::V1::Statuses::SourcesController < Api::V1::Statuses::BaseController before_action -> { doorkeeper_authorize! :read, :'read:statuses' } - before_action :set_status def show render json: @status, serializer: REST::StatusSourceSerializer end - - private - - def set_status - @status = Status.find(params[:status_id]) - authorize @status, :show? - rescue Mastodon::NotPermittedError - not_found - end end diff --git a/app/controllers/api/v1/statuses/translations_controller.rb b/app/controllers/api/v1/statuses/translations_controller.rb index ec5ea5b85..7d406b0a3 100644 --- a/app/controllers/api/v1/statuses/translations_controller.rb +++ b/app/controllers/api/v1/statuses/translations_controller.rb @@ -1,10 +1,7 @@ # frozen_string_literal: true -class Api::V1::Statuses::TranslationsController < Api::BaseController - include Authorization - +class Api::V1::Statuses::TranslationsController < Api::V1::Statuses::BaseController before_action -> { doorkeeper_authorize! :read, :'read:statuses' } - before_action :set_status before_action :set_translation rescue_from TranslationService::NotConfiguredError, with: :not_found @@ -24,13 +21,6 @@ class Api::V1::Statuses::TranslationsController < Api::BaseController private - def set_status - @status = Status.find(params[:status_id]) - authorize @status, :show? - rescue Mastodon::NotPermittedError - not_found - end - def set_translation @translation = TranslateStatusService.new.call(@status, content_locale) end From 49ba5a9f94eb2b595f9bb04e29ba4e7e1d99889a Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 13 Nov 2023 11:01:24 -0500 Subject: [PATCH 15/63] Use `hash_including` to check `AccountFilter` setup in `admin/accounts` controller spec (#27838) --- .../controllers/admin/accounts_controller_spec.rb | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/spec/controllers/admin/accounts_controller_spec.rb b/spec/controllers/admin/accounts_controller_spec.rb index ba03ec85a..3b8fa2f71 100644 --- a/spec/controllers/admin/accounts_controller_spec.rb +++ b/spec/controllers/admin/accounts_controller_spec.rb @@ -20,8 +20,7 @@ RSpec.describe Admin::AccountsController do it 'filters with parameters' do account_filter = instance_double(AccountFilter, results: Account.all) allow(AccountFilter).to receive(:new).and_return(account_filter) - - get :index, params: { + params = { origin: 'local', by_domain: 'domain', status: 'active', @@ -31,17 +30,9 @@ RSpec.describe Admin::AccountsController do ip: '0.0.0.42', } - expect(AccountFilter).to have_received(:new) do |params| - h = params.to_h + get :index, params: params - expect(h[:origin]).to eq 'local' - expect(h[:by_domain]).to eq 'domain' - expect(h[:status]).to eq 'active' - expect(h[:username]).to eq 'username' - expect(h[:display_name]).to eq 'display name' - expect(h[:email]).to eq 'local-part@domain' - expect(h[:ip]).to eq '0.0.0.42' - end + expect(AccountFilter).to have_received(:new).with(hash_including(params)) end it 'paginates accounts' do From bac9e0b55d77257b9b9d8ce602ca79c3885a9d18 Mon Sep 17 00:00:00 2001 From: Claire <claire.github-309c@sitedethib.com> Date: Mon, 13 Nov 2023 17:17:05 +0100 Subject: [PATCH 16/63] Add variable delay before link verification of remote account links (#27774) --- app/services/activitypub/process_account_service.rb | 2 +- app/services/update_account_service.rb | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index 1304ca824..8fc0989a3 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -180,7 +180,7 @@ class ActivityPub::ProcessAccountService < BaseService end def check_links! - VerifyAccountLinksWorker.perform_async(@account.id) + VerifyAccountLinksWorker.perform_in(rand(10.minutes.to_i), @account.id) end def process_duplicate_accounts! diff --git a/app/services/update_account_service.rb b/app/services/update_account_service.rb index a98f4d31e..1bbcfce3e 100644 --- a/app/services/update_account_service.rb +++ b/app/services/update_account_service.rb @@ -30,11 +30,7 @@ class UpdateAccountService < BaseService def check_links(account) return unless account.fields.any?(&:requires_verification?) - if account.local? - VerifyAccountLinksWorker.perform_async(account.id) - else - VerifyAccountLinksWorker.perform_in(rand(10.minutes.to_i), account.id) - end + VerifyAccountLinksWorker.perform_async(account.id) end def process_hashtags(account) From b7807f3d84983ab46c3345d076576ffa8433dab4 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 13 Nov 2023 17:47:44 -0500 Subject: [PATCH 17/63] Use `normalizes` to prepare `Webhook#events` value (#27605) --- app/models/webhook.rb | 16 ++++++++-------- spec/models/webhook_spec.rb | 31 +++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/app/models/webhook.rb b/app/models/webhook.rb index 044097921..304b2b1f1 100644 --- a/app/models/webhook.rb +++ b/app/models/webhook.rb @@ -33,11 +33,11 @@ class Webhook < ApplicationRecord validates :secret, presence: true, length: { minimum: 12 } validates :events, presence: true - validate :validate_events + validate :events_validation_error, if: :invalid_events? validate :validate_permissions validate :validate_template - before_validation :strip_events + normalizes :events, with: ->(events) { events.filter_map { |event| event.strip.presence } } before_validation :generate_secret def rotate_secret! @@ -69,8 +69,12 @@ class Webhook < ApplicationRecord private - def validate_events - errors.add(:events, :invalid) if events.any? { |e| EVENTS.exclude?(e) } + def events_validation_error + errors.add(:events, :invalid) + end + + def invalid_events? + events.blank? || events.difference(EVENTS).any? end def validate_permissions @@ -88,10 +92,6 @@ class Webhook < ApplicationRecord end end - def strip_events - self.events = events.filter_map { |str| str.strip.presence } if events.present? - end - def generate_secret self.secret = SecureRandom.hex(20) if secret.blank? end diff --git a/spec/models/webhook_spec.rb b/spec/models/webhook_spec.rb index 715dd7574..effaf92e9 100644 --- a/spec/models/webhook_spec.rb +++ b/spec/models/webhook_spec.rb @@ -5,6 +5,37 @@ require 'rails_helper' RSpec.describe Webhook do let(:webhook) { Fabricate(:webhook) } + describe 'Validations' do + it 'requires presence of events' do + record = described_class.new(events: nil) + record.valid? + + expect(record).to model_have_error_on_field(:events) + end + + it 'requires non-empty events value' do + record = described_class.new(events: []) + record.valid? + + expect(record).to model_have_error_on_field(:events) + end + + it 'requires valid events value from EVENTS' do + record = described_class.new(events: ['account.invalid']) + record.valid? + + expect(record).to model_have_error_on_field(:events) + end + end + + describe 'Normalizations' do + it 'cleans up events values' do + record = described_class.new(events: ['account.approved', 'account.created ', '']) + + expect(record.events).to eq(%w(account.approved account.created)) + end + end + describe '#rotate_secret!' do it 'changes the secret' do previous_value = webhook.secret From 4aa06cbdbfbd4a4f9508c988c012951024b561e8 Mon Sep 17 00:00:00 2001 From: Brian Holley <brian.holley@hotmail.com> Date: Mon, 13 Nov 2023 16:39:54 -0800 Subject: [PATCH 18/63] Fix "Hide these posts from home" list setting not refreshing when switching lists (#27763) --- app/javascript/mastodon/features/list_timeline/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/list_timeline/index.jsx b/app/javascript/mastodon/features/list_timeline/index.jsx index aadb6ecd5..55579c2fd 100644 --- a/app/javascript/mastodon/features/list_timeline/index.jsx +++ b/app/javascript/mastodon/features/list_timeline/index.jsx @@ -204,7 +204,7 @@ class ListTimeline extends PureComponent { </div> <div className='setting-toggle'> - <Toggle id={`list-${id}-exclusive`} defaultChecked={isExclusive} onChange={this.onExclusiveToggle} /> + <Toggle id={`list-${id}-exclusive`} checked={isExclusive} onChange={this.onExclusiveToggle} /> <label htmlFor={`list-${id}-exclusive`} className='setting-toggle__label'> <FormattedMessage id='lists.exclusive' defaultMessage='Hide these posts from home' /> </label> From 5e2ecc736ddf275e57c91348a7b0a485f9b2462d Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Tue, 14 Nov 2023 05:29:33 -0500 Subject: [PATCH 19/63] Remove double `subject` in api/v1/accounts/relationships spec (#27839) --- .../api/v1/accounts/relationships_spec.rb | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/spec/requests/api/v1/accounts/relationships_spec.rb b/spec/requests/api/v1/accounts/relationships_spec.rb index bb78e3b3e..5011352c6 100644 --- a/spec/requests/api/v1/accounts/relationships_spec.rb +++ b/spec/requests/api/v1/accounts/relationships_spec.rb @@ -102,17 +102,25 @@ describe 'GET /api/v1/accounts/relationships' do end end - it 'returns JSON with correct data on cached requests too' do - subject - subject + it 'returns JSON with correct data on previously cached requests' do + # Initial request including multiple accounts in params + get '/api/v1/accounts/relationships', headers: headers, params: { id: [simon.id, lewis.id] } + expect(body_as_json.size).to eq(2) + + # Subsequent request with different id, should override cache from first request + get '/api/v1/accounts/relationships', headers: headers, params: { id: [simon.id] } expect(response).to have_http_status(200) - json = body_as_json - - expect(json).to be_a Enumerable - expect(json.first[:following]).to be true - expect(json.first[:showing_reblogs]).to be true + expect(body_as_json) + .to be_an(Enumerable) + .and have_attributes( + size: 1, + first: hash_including( + following: true, + showing_reblogs: true + ) + ) end it 'returns JSON with correct data after change too' do From 373aa95dddd7119725c4f11d3af402066e2288cf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 14 Nov 2023 11:30:39 +0100 Subject: [PATCH 20/63] Update formatjs monorepo (#27849) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 154 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 107 insertions(+), 47 deletions(-) diff --git a/yarn.lock b/yarn.lock index e8fe28fb4..1cb5a42d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1800,6 +1800,16 @@ __metadata: languageName: node linkType: hard +"@formatjs/ecma402-abstract@npm:1.18.0": + version: 1.18.0 + resolution: "@formatjs/ecma402-abstract@npm:1.18.0" + dependencies: + "@formatjs/intl-localematcher": "npm:0.5.2" + tslib: "npm:^2.4.0" + checksum: bbdad0aee8e48baad6bfe6b2c27caf3befe35e658b922ee2f84417a819f0bdc7e849a8c0c782db8b53f5666bf19669d2b10a1104257c08796d198c87766bfc92 + languageName: node + linkType: hard + "@formatjs/fast-memoize@npm:2.2.0": version: 2.2.0 resolution: "@formatjs/fast-memoize@npm:2.2.0" @@ -1820,6 +1830,17 @@ __metadata: languageName: node linkType: hard +"@formatjs/icu-messageformat-parser@npm:2.7.3": + version: 2.7.3 + resolution: "@formatjs/icu-messageformat-parser@npm:2.7.3" + dependencies: + "@formatjs/ecma402-abstract": "npm:1.18.0" + "@formatjs/icu-skeleton-parser": "npm:1.7.0" + tslib: "npm:^2.4.0" + checksum: 2a51038813e5cff7e2df767e1227373d228e907adb7268fc3744b3d82c4fa69d4aa9f6020a62de2c468cf724600e9372ac07ae43a4480ed066fe34e224e80e4a + languageName: node + linkType: hard + "@formatjs/icu-skeleton-parser@npm:1.6.4": version: 1.6.4 resolution: "@formatjs/icu-skeleton-parser@npm:1.6.4" @@ -1830,25 +1851,35 @@ __metadata: languageName: node linkType: hard -"@formatjs/intl-displaynames@npm:6.6.3": - version: 6.6.3 - resolution: "@formatjs/intl-displaynames@npm:6.6.3" +"@formatjs/icu-skeleton-parser@npm:1.7.0": + version: 1.7.0 + resolution: "@formatjs/icu-skeleton-parser@npm:1.7.0" dependencies: - "@formatjs/ecma402-abstract": "npm:1.17.4" - "@formatjs/intl-localematcher": "npm:0.5.1" + "@formatjs/ecma402-abstract": "npm:1.18.0" tslib: "npm:^2.4.0" - checksum: b0520cb744a51290fbcde80860f39ed9c9df9b81beae98986e1fc089ef635f7699c750631fa42a559f3678d1dd02b14904614e70360477d18e68d3eba6592390 + checksum: 2e4db815247ddb10f7990bbb501c85b854ee951ee45143673eb91b4392b11d0a8312327adb8b624c6a2fdafab12083904630d6d22475503d025f1612da4dcaee languageName: node linkType: hard -"@formatjs/intl-listformat@npm:7.5.2": - version: 7.5.2 - resolution: "@formatjs/intl-listformat@npm:7.5.2" +"@formatjs/intl-displaynames@npm:6.6.4": + version: 6.6.4 + resolution: "@formatjs/intl-displaynames@npm:6.6.4" dependencies: - "@formatjs/ecma402-abstract": "npm:1.17.4" - "@formatjs/intl-localematcher": "npm:0.5.1" + "@formatjs/ecma402-abstract": "npm:1.18.0" + "@formatjs/intl-localematcher": "npm:0.5.2" tslib: "npm:^2.4.0" - checksum: 54fa03da4ea45504681d6d87d72d1cac574809ce43f965fa4b845e83be3072d92324c58cec57ad386827087fb1d6ecae438d29576f30176bf52eb212e454bce2 + checksum: 009e443dd0d10776b8573d0181407d4c0d6c7a2ff537a5ea1e36413d1b08db9c21dfef272eabab8efabd01a58b64f663a30e4d584fd761df3fd68a5d23fe444b + languageName: node + linkType: hard + +"@formatjs/intl-listformat@npm:7.5.3": + version: 7.5.3 + resolution: "@formatjs/intl-listformat@npm:7.5.3" + dependencies: + "@formatjs/ecma402-abstract": "npm:1.18.0" + "@formatjs/intl-localematcher": "npm:0.5.2" + tslib: "npm:^2.4.0" + checksum: de741ce84b16fed57016afbfe446ebd57cd23a046859a9353f5d455f8bc9114493bf83b9e18429268c7ce8f77bc54516a9b8190baf09fbb25c9b06cfc80101d4 languageName: node linkType: hard @@ -1861,34 +1892,43 @@ __metadata: languageName: node linkType: hard -"@formatjs/intl-pluralrules@npm:^5.2.2": - version: 5.2.9 - resolution: "@formatjs/intl-pluralrules@npm:5.2.9" +"@formatjs/intl-localematcher@npm:0.5.2": + version: 0.5.2 + resolution: "@formatjs/intl-localematcher@npm:0.5.2" dependencies: - "@formatjs/ecma402-abstract": "npm:1.17.4" - "@formatjs/intl-localematcher": "npm:0.5.1" tslib: "npm:^2.4.0" - checksum: a6ca5c498ce542facacf8ce8640d4ba068f9119b758547a23614b50611eb385a46abd386ff88fa423211355ec463cf102c2c908b74f6e23a5bc9e2a23873dc29 + checksum: 4b3ae75470e3e53ffa39b2d46e65a2a4c9c4becbc0aac989b0694370e10c6687643660a045512d676509bc29b257fe5726fbb028de12f889be02c2d20b6527e6 languageName: node linkType: hard -"@formatjs/intl@npm:2.9.8": - version: 2.9.8 - resolution: "@formatjs/intl@npm:2.9.8" +"@formatjs/intl-pluralrules@npm:^5.2.2": + version: 5.2.10 + resolution: "@formatjs/intl-pluralrules@npm:5.2.10" dependencies: - "@formatjs/ecma402-abstract": "npm:1.17.4" + "@formatjs/ecma402-abstract": "npm:1.18.0" + "@formatjs/intl-localematcher": "npm:0.5.2" + tslib: "npm:^2.4.0" + checksum: 1050416613e80bff2c58546c80c8d52ed97847d13c90535a53d058e44969369b50e1cfdb464e9e9ef4802c934c84ea0e656c3f4e3b4d5ac7496b722c759da4cf + languageName: node + linkType: hard + +"@formatjs/intl@npm:2.9.9": + version: 2.9.9 + resolution: "@formatjs/intl@npm:2.9.9" + dependencies: + "@formatjs/ecma402-abstract": "npm:1.18.0" "@formatjs/fast-memoize": "npm:2.2.0" - "@formatjs/icu-messageformat-parser": "npm:2.7.2" - "@formatjs/intl-displaynames": "npm:6.6.3" - "@formatjs/intl-listformat": "npm:7.5.2" - intl-messageformat: "npm:10.5.7" + "@formatjs/icu-messageformat-parser": "npm:2.7.3" + "@formatjs/intl-displaynames": "npm:6.6.4" + "@formatjs/intl-listformat": "npm:7.5.3" + intl-messageformat: "npm:10.5.8" tslib: "npm:^2.4.0" peerDependencies: typescript: 5 peerDependenciesMeta: typescript: optional: true - checksum: 6341f4bfb56a0e14373395b1232e1eeb8e64588a8c3d4614cd2b06f71d4e65dbd4a79e3a1c07e1b6c20c48e399ac2385977b01a559e1d2bd1a1d226e0eae3058 + checksum: b26904da605ab309535dfbbfbd403a3bb33d51d3c969c548b88fa04755be3aff60b1bddd1c453514a84048c7432271cef507ac66de32dcfa66b3f842a1ddb977 languageName: node linkType: hard @@ -1912,6 +1952,26 @@ __metadata: languageName: node linkType: hard +"@formatjs/ts-transformer@npm:3.13.9": + version: 3.13.9 + resolution: "@formatjs/ts-transformer@npm:3.13.9" + dependencies: + "@formatjs/icu-messageformat-parser": "npm:2.7.3" + "@types/json-stable-stringify": "npm:^1.0.32" + "@types/node": "npm:14 || 16 || 17" + chalk: "npm:^4.0.0" + json-stable-stringify: "npm:^1.0.1" + tslib: "npm:^2.4.0" + typescript: "npm:5" + peerDependencies: + ts-jest: ">=27" + peerDependenciesMeta: + ts-jest: + optional: true + checksum: 4e313b967e45aae79246174c3181d31cc7cd297380d3a880a98fc0be16d76b783868712151e840ea16d22e2fbec0388b1005f688b6d4cb74ee4411b43f6d33f4 + languageName: node + linkType: hard + "@gamestdio/websocket@npm:^0.3.2": version: 0.3.2 resolution: "@gamestdio/websocket@npm:0.3.2" @@ -4675,21 +4735,21 @@ __metadata: linkType: hard "babel-plugin-formatjs@npm:^10.5.1": - version: 10.5.9 - resolution: "babel-plugin-formatjs@npm:10.5.9" + version: 10.5.10 + resolution: "babel-plugin-formatjs@npm:10.5.10" dependencies: "@babel/core": "npm:^7.10.4" "@babel/helper-plugin-utils": "npm:^7.10.4" "@babel/plugin-syntax-jsx": "npm:7" "@babel/traverse": "npm:7" "@babel/types": "npm:^7.12.11" - "@formatjs/icu-messageformat-parser": "npm:2.7.2" - "@formatjs/ts-transformer": "npm:3.13.8" + "@formatjs/icu-messageformat-parser": "npm:2.7.3" + "@formatjs/ts-transformer": "npm:3.13.9" "@types/babel__core": "npm:^7.1.7" "@types/babel__helper-plugin-utils": "npm:^7.10.0" "@types/babel__traverse": "npm:^7.1.7" tslib: "npm:^2.4.0" - checksum: 5e4127cf7b4b9b3306a9d0ab5b029831712d22db5e2117225ce706b55d222d09a7eba1f3720fdad7a99f61843b5cba107296fc11ae00a6f0941217d9322aa02e + checksum: bff65cd2a88a0ae00eabab1d022ffc44c4385b7e529cac42375bb1828c678c7a71a78f644512e5d1dd8cd532d418c16acdbabcef2bf6670e24404f4f164a74ce languageName: node linkType: hard @@ -9243,15 +9303,15 @@ __metadata: languageName: node linkType: hard -"intl-messageformat@npm:10.5.7, intl-messageformat@npm:^10.3.5": - version: 10.5.7 - resolution: "intl-messageformat@npm:10.5.7" +"intl-messageformat@npm:10.5.8, intl-messageformat@npm:^10.3.5": + version: 10.5.8 + resolution: "intl-messageformat@npm:10.5.8" dependencies: - "@formatjs/ecma402-abstract": "npm:1.17.4" + "@formatjs/ecma402-abstract": "npm:1.18.0" "@formatjs/fast-memoize": "npm:2.2.0" - "@formatjs/icu-messageformat-parser": "npm:2.7.2" + "@formatjs/icu-messageformat-parser": "npm:2.7.3" tslib: "npm:^2.4.0" - checksum: 7f341b3eb5b3d402167c99ca7fb98720c7ad553bed8a490b2210bd90ea9009a09f9030939307fecb111fce1454f31b4298b4f0a346999af627c86f8164a5c547 + checksum: 1d2854aae8471ec48165ca265760d6c5b1814eca831c88db698eb29b5ed20bee21ca8533090c9d28d9c6f1d844dda210b0bc58a2e036446158fae0845e5eed4f languageName: node linkType: hard @@ -13643,18 +13703,18 @@ __metadata: linkType: hard "react-intl@npm:^6.4.2": - version: 6.5.4 - resolution: "react-intl@npm:6.5.4" + version: 6.5.5 + resolution: "react-intl@npm:6.5.5" dependencies: - "@formatjs/ecma402-abstract": "npm:1.17.4" - "@formatjs/icu-messageformat-parser": "npm:2.7.2" - "@formatjs/intl": "npm:2.9.8" - "@formatjs/intl-displaynames": "npm:6.6.3" - "@formatjs/intl-listformat": "npm:7.5.2" + "@formatjs/ecma402-abstract": "npm:1.18.0" + "@formatjs/icu-messageformat-parser": "npm:2.7.3" + "@formatjs/intl": "npm:2.9.9" + "@formatjs/intl-displaynames": "npm:6.6.4" + "@formatjs/intl-listformat": "npm:7.5.3" "@types/hoist-non-react-statics": "npm:^3.3.1" "@types/react": "npm:16 || 17 || 18" hoist-non-react-statics: "npm:^3.3.2" - intl-messageformat: "npm:10.5.7" + intl-messageformat: "npm:10.5.8" tslib: "npm:^2.4.0" peerDependencies: react: ^16.6.0 || 17 || 18 @@ -13662,7 +13722,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 1117a7f866b103abf88a4087f5fe8b854d9c069c69444c592f8431e7d28c9b90423f7b50e550be0f2f173b7563e943bcc9238e80f6747181f81861275f6e2ce7 + checksum: 9ff6200f195557804b735d618ee593aed7848e84213ac4eb9c57708f55c0d93232e0dd338c990348ba3b1d73dca071502a2051d4a2790838d962c3ccde87fa6c languageName: node linkType: hard From 1f8173ac5aa2dbdab66e6c426b137f4fcc162462 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Tue, 14 Nov 2023 05:31:59 -0500 Subject: [PATCH 21/63] Extract private methods in api/v1/instances/domain_blocks (#27844) --- .../api/v1/instances/domain_blocks_controller.rb | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/v1/instances/domain_blocks_controller.rb b/app/controllers/api/v1/instances/domain_blocks_controller.rb index 566764dbf..8fb90305a 100644 --- a/app/controllers/api/v1/instances/domain_blocks_controller.rb +++ b/app/controllers/api/v1/instances/domain_blocks_controller.rb @@ -13,7 +13,7 @@ class Api::V1::Instances::DomainBlocksController < Api::V1::Instances::BaseContr cache_if_unauthenticated! end - render json: @domain_blocks, each_serializer: REST::DomainBlockSerializer, with_comment: (Setting.show_domain_blocks_rationale == 'all' || (Setting.show_domain_blocks_rationale == 'users' && user_signed_in?)) + render json: @domain_blocks, each_serializer: REST::DomainBlockSerializer, with_comment: show_rationale_in_response? end private @@ -25,4 +25,16 @@ class Api::V1::Instances::DomainBlocksController < Api::V1::Instances::BaseContr def set_domain_blocks @domain_blocks = DomainBlock.with_user_facing_limitations.by_severity end + + def show_rationale_in_response? + always_show_rationale? || show_rationale_for_user? + end + + def always_show_rationale? + Setting.show_domain_blocks_rationale == 'all' + end + + def show_rationale_for_user? + Setting.show_domain_blocks_rationale == 'users' && user_signed_in? + end end From c1e071f6343a0b5c6383e6a336fd1aaf8dc97100 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 14 Nov 2023 11:53:47 +0100 Subject: [PATCH 22/63] New Crowdin Translations (automated) (#27848) Co-authored-by: GitHub Actions <noreply@github.com> --- app/javascript/mastodon/locales/af.json | 4 ++++ app/javascript/mastodon/locales/be.json | 6 +++--- app/javascript/mastodon/locales/ca.json | 1 + app/javascript/mastodon/locales/fy.json | 1 + app/javascript/mastodon/locales/lt.json | 9 ++++++++- app/javascript/mastodon/locales/sl.json | 1 + app/javascript/mastodon/locales/sr-Latn.json | 1 + app/javascript/mastodon/locales/sr.json | 1 + config/locales/activerecord.af.yml | 4 ++++ config/locales/af.yml | 17 +++++++++++++++++ config/locales/be.yml | 1 + config/locales/ca.yml | 1 + config/locales/cy.yml | 1 + config/locales/da.yml | 5 +++++ config/locales/de.yml | 1 + config/locales/devise.be.yml | 2 +- config/locales/doorkeeper.af.yml | 1 + config/locales/es-AR.yml | 1 + config/locales/es-MX.yml | 1 + config/locales/es.yml | 1 + config/locales/eu.yml | 1 + config/locales/fa.yml | 1 + config/locales/fo.yml | 1 + config/locales/fr-QC.yml | 1 + config/locales/fr.yml | 1 + config/locales/fy.yml | 1 + config/locales/gl.yml | 1 + config/locales/he.yml | 5 +++-- config/locales/hu.yml | 1 + config/locales/is.yml | 1 + config/locales/it.yml | 1 + config/locales/ko.yml | 1 + config/locales/lt.yml | 1 + config/locales/nl.yml | 1 + config/locales/nn.yml | 1 + config/locales/no.yml | 1 + config/locales/pl.yml | 1 + config/locales/pt-BR.yml | 1 + config/locales/pt-PT.yml | 1 + config/locales/simple_form.be.yml | 2 +- config/locales/sk.yml | 8 ++++++++ config/locales/sl.yml | 1 + config/locales/sq.yml | 1 + config/locales/sr-Latn.yml | 1 + config/locales/sr.yml | 1 + config/locales/sv.yml | 1 + config/locales/th.yml | 1 + config/locales/tr.yml | 1 + config/locales/uk.yml | 1 + config/locales/zh-CN.yml | 1 + config/locales/zh-HK.yml | 1 + config/locales/zh-TW.yml | 1 + 52 files changed, 96 insertions(+), 8 deletions(-) diff --git a/app/javascript/mastodon/locales/af.json b/app/javascript/mastodon/locales/af.json index 7e842b5dd..6f7f355fc 100644 --- a/app/javascript/mastodon/locales/af.json +++ b/app/javascript/mastodon/locales/af.json @@ -14,6 +14,7 @@ "account.badges.group": "Groep", "account.block": "Blokkeer @{name}", "account.block_domain": "Blokkeer domein {domain}", + "account.block_short": "Blokkeer", "account.blocked": "Geblokkeer", "account.browse_more_on_origin_server": "Verken die oorspronklike profiel", "account.cancel_follow_request": "Herroep volgversoek", @@ -45,6 +46,7 @@ "account.posts_with_replies": "Plasings en antwoorde", "account.report": "Rapporteer @{name}", "account.requested": "Wag op goedkeuring. Klik om volgversoek te kanselleer", + "account.requested_follow": "{name} het versoek om jou te volg", "account.share": "Deel @{name} se profiel", "account.show_reblogs": "Wys aangestuurde plasings van @{name}", "account.statuses_counter": "{count, plural, one {{counter} Plaas} other {{counter} Plasings}}", @@ -82,6 +84,7 @@ "column.community": "Plaaslike tydlyn", "column.directory": "Blaai deur profiele", "column.domain_blocks": "Geblokkeerde domeine", + "column.favourites": "Gunstelinge", "column.follow_requests": "Volgversoeke", "column.home": "Tuis", "column.lists": "Lyste", @@ -271,6 +274,7 @@ "privacy.unlisted.short": "Ongelys", "privacy_policy.last_updated": "Laaste bywerking op {date}", "privacy_policy.title": "Privaatheidsbeleid", + "regeneration_indicator.sublabel": "Jou tuis-voer word voorberei!", "reply_indicator.cancel": "Kanselleer", "report.placeholder": "Type or paste additional comments", "report.submit": "Submit report", diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json index 7c2d652b6..e8a52ee29 100644 --- a/app/javascript/mastodon/locales/be.json +++ b/app/javascript/mastodon/locales/be.json @@ -201,7 +201,7 @@ "disabled_account_banner.text": "Ваш уліковы запіс {disabledAccount} часова адключаны.", "dismissable_banner.community_timeline": "Гэта самыя апошнія допісы ад людзей, уліковыя запісы якіх размяшчаюцца на {domain}.", "dismissable_banner.dismiss": "Адхіліць", - "dismissable_banner.explore_links": "Гэтыя навіны абмяркоўваюцца прама зараз на гэтым і іншых серверах дэцэнтралізаванай сеткі.", + "dismissable_banner.explore_links": "Гэтыя навіны абмяркоўваюцца цяпер на гэтым і іншых серверах дэцэнтралізаванай сеткі.", "dismissable_banner.explore_statuses": "Допісы з гэтага і іншых сервераў дэцэнтралізаванай сеткі, якія набіраюць папулярнасць прама зараз.", "dismissable_banner.explore_tags": "Гэтыя хэштэгі зараз набіраюць папулярнасць сярод людзей на гэтым і іншых серверах дэцэнтралізаванай сеткі", "dismissable_banner.public_timeline": "Гэта апошнія публічныя допісы людзей з усей сеткі, за якімі сочаць карыстальнікі {domain}.", @@ -482,7 +482,7 @@ "onboarding.share.lead": "Дайце людзям ведаць, як яны могуць знайсці вас на Mastodon!", "onboarding.share.message": "Я {username} на #Mastodon! Сачыце за мной на {url}", "onboarding.share.next_steps": "Магчымыя наступныя крокі:", - "onboarding.share.title": "Падзяліцеся сваім профілем", + "onboarding.share.title": "Абагульце свой профіль", "onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:", "onboarding.start.skip": "Want to skip right ahead?", "onboarding.start.title": "Вы зрабілі гэта!", @@ -493,7 +493,7 @@ "onboarding.steps.setup_profile.body": "Others are more likely to interact with you with a filled out profile.", "onboarding.steps.setup_profile.title": "Customize your profile", "onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!", - "onboarding.steps.share_profile.title": "Share your profile", + "onboarding.steps.share_profile.title": "Абагульць ваш профіль у Mastodon", "onboarding.tips.2fa": "<strong>Ці вы ведаеце?</strong> Вы можаце абараніць свой уліковы запіс, усталяваўшы двухфактарную аўтэнтыфікацыю ў наладах уліковага запісу. Яна працуе з любой праграмай TOTP на ваш выбар, нумар тэлефона не патрэбны!", "onboarding.tips.accounts_from_other_servers": "<strong>Ці вы ведаеце?</strong> Паколькі Mastodon дэцэнтралізаваны, некаторыя профілі, якія вам трапляюцца, будуць размяшчацца на іншых серверах, адрозных ад вашага. І ўсё ж вы можаце бесперашкодна ўзаемадзейнічаць з імі! Іх сервер пазначаны ў другой палове імя карыстальніка!", "onboarding.tips.migration": "<strong>Ці вы ведаеце?</strong> Калі вы адчуваеце, што {domain} не з'яўляецца для вас лепшым выбарам у будучыні, вы можаце перайсці на іншы сервер Mastodon, не губляючы сваіх падпісчыкаў. Вы нават можаце стварыць свой уласны сервер!", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 433b9b47b..99cae584b 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -222,6 +222,7 @@ "emoji_button.search_results": "Resultats de la cerca", "emoji_button.symbols": "Símbols", "emoji_button.travel": "Viatges i llocs", + "empty_column.account_hides_collections": "Aquest usuari ha elegit no mostrar aquesta informació", "empty_column.account_suspended": "Compte suspès", "empty_column.account_timeline": "No hi ha tuts aquí!", "empty_column.account_unavailable": "Perfil no disponible", diff --git a/app/javascript/mastodon/locales/fy.json b/app/javascript/mastodon/locales/fy.json index 12365c879..9d3b41606 100644 --- a/app/javascript/mastodon/locales/fy.json +++ b/app/javascript/mastodon/locales/fy.json @@ -222,6 +222,7 @@ "emoji_button.search_results": "Sykresultaten", "emoji_button.symbols": "Symboalen", "emoji_button.travel": "Reizgje en lokaasjes", + "empty_column.account_hides_collections": "Dizze brûker hat derfoar keazen dizze ynformaasje net beskikber te meitsjen", "empty_column.account_suspended": "Account beskoattele", "empty_column.account_timeline": "Hjir binne gjin berjochten!", "empty_column.account_unavailable": "Profyl net beskikber", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index 75f4a239e..5cdc575de 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -41,6 +41,8 @@ "account.languages": "Keisti prenumeruojamas kalbas", "account.locked_info": "Šios paskyros privatumo būsena nustatyta kaip užrakinta. Savininkas (-ė) rankiniu būdu peržiūri, kas gali sekti.", "account.media": "Medija", + "account.mention": "Paminėti @{name}", + "account.moved_to": "{name} nurodė, kad dabar jų nauja paskyra yra:", "account.mute": "Užtildyti @{name}", "account.muted": "Užtildytas", "account.posts": "Toots", @@ -53,10 +55,15 @@ "account.unfollow": "Nebesekti", "account.unmute_short": "Atitildyti", "account_note.placeholder": "Click to add a note", - "alert.unexpected.title": "Oi!", + "alert.unexpected.message": "Įvyko netikėta klaida.", + "alert.unexpected.title": "Ups!", "announcement.announcement": "Skelbimas", + "attachments_list.unprocessed": "(neapdorotas)", "audio.hide": "Slėpti garsą", "autosuggest_hashtag.per_week": "{count} per savaitę", + "boost_modal.combo": "Gali spausti {combo}, kad praleisti kitą kartą", + "bundle_column_error.copy_stacktrace": "Kopijuoti klaidos ataskaitą", + "bundle_column_error.error.body": "Užklausos puslapio nepavyko atvaizduoti. Tai gali būti dėl mūsų kodo klaidos arba naršyklės suderinamumo problemos.", "bundle_column_error.error.title": "O, ne!", "column.domain_blocks": "Hidden domains", "column.lists": "Sąrašai", diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json index 63507be79..f16a91d65 100644 --- a/app/javascript/mastodon/locales/sl.json +++ b/app/javascript/mastodon/locales/sl.json @@ -222,6 +222,7 @@ "emoji_button.search_results": "Rezultati iskanja", "emoji_button.symbols": "Simboli", "emoji_button.travel": "Potovanja in kraji", + "empty_column.account_hides_collections": "Ta uporabnik se je odločil, da te informacije ne bo dal na voljo", "empty_column.account_suspended": "Račun je suspendiran", "empty_column.account_timeline": "Tukaj ni objav!", "empty_column.account_unavailable": "Profil ni na voljo", diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json index f64952f7b..aa948b1f0 100644 --- a/app/javascript/mastodon/locales/sr-Latn.json +++ b/app/javascript/mastodon/locales/sr-Latn.json @@ -222,6 +222,7 @@ "emoji_button.search_results": "Rezultati pretrage", "emoji_button.symbols": "Simboli", "emoji_button.travel": "Putovanja i mesta", + "empty_column.account_hides_collections": "Ovaj korisnik je odlučio da ove informacije ne učini dostupnim", "empty_column.account_suspended": "Nalog je suspendovan", "empty_column.account_timeline": "Nema objava ovde!", "empty_column.account_unavailable": "Profil je nedostupan", diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json index ec2c76e8f..9e6716927 100644 --- a/app/javascript/mastodon/locales/sr.json +++ b/app/javascript/mastodon/locales/sr.json @@ -222,6 +222,7 @@ "emoji_button.search_results": "Резултати претраге", "emoji_button.symbols": "Симболи", "emoji_button.travel": "Путовања и места", + "empty_column.account_hides_collections": "Овај корисник је одлучио да ове информације не учини доступним", "empty_column.account_suspended": "Налог је суспендован", "empty_column.account_timeline": "Нема објава овде!", "empty_column.account_unavailable": "Профил је недоступан", diff --git a/config/locales/activerecord.af.yml b/config/locales/activerecord.af.yml index c0810999d..91980644e 100644 --- a/config/locales/activerecord.af.yml +++ b/config/locales/activerecord.af.yml @@ -53,3 +53,7 @@ af: position: elevated: kan nie hoër as jou huidige rol wees nie own_role: kan nie verander word met jou huidige rol nie + webhook: + attributes: + events: + invalid_permissions: geleenthede waartoe jy nie toegang het nie mag nie ingesluit word nie diff --git a/config/locales/af.yml b/config/locales/af.yml index 1dbf99afe..74d349591 100644 --- a/config/locales/af.yml +++ b/config/locales/af.yml @@ -5,7 +5,23 @@ af: contact_unavailable: NVT hosted_on: Mastodon gehuisves op %{domain} title: Aangaande + accounts: + follow: Volg + followers: + one: Volgeling + other: Volgelinge + following: Volg + nothing_here: Daar is niks hier nie! + posts: + one: Plasing + other: Plasings + posts_tab_heading: Plasings admin: + account_actions: + action: Voer aksie uit + title: Voer modereer aksie uit op %{acct} + account_moderation_notes: + create: Los nota accounts: location: local: Plaaslik @@ -102,6 +118,7 @@ af: types: bookmarks: Boekmerke invites: + invalid: Hierdie uitnodiging is nie geldig nie title: Nooi ander login_activities: description_html: Indien jy onbekende aktiwiteite gewaar, oorweeg dit om jou wagwoord te verander en tweefaktorverifikasie te aktiveer. diff --git a/config/locales/be.yml b/config/locales/be.yml index 275ef7a82..96a272012 100644 --- a/config/locales/be.yml +++ b/config/locales/be.yml @@ -1418,6 +1418,7 @@ be: '86400': 1 дзень expires_in_prompt: Ніколі generate: Стварыць запрашальную спасылку + invalid: Гэта запрашэнне несапраўднае invited_by: 'Вас запрасіў(-ла):' max_uses: few: "%{count} выкарыстанні" diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 2cdf87d8f..03b3ff3c2 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -1358,6 +1358,7 @@ ca: '86400': 1 dia expires_in_prompt: Mai generate: Genera + invalid: Aquesta invitació no és vàlida invited_by: 'Has estat invitat per:' max_uses: one: 1 ús diff --git a/config/locales/cy.yml b/config/locales/cy.yml index 846ce41de..c9d5b8828 100644 --- a/config/locales/cy.yml +++ b/config/locales/cy.yml @@ -1468,6 +1468,7 @@ cy: '86400': 1 diwrnod expires_in_prompt: Byth generate: Cynhyrchu dolen wahoddiad + invalid: Nid yw'r gwahoddiad hwn yn ddilys invited_by: 'Cawsoch eich gwahodd gan:' max_uses: few: "%{count} defnydd" diff --git a/config/locales/da.yml b/config/locales/da.yml index c5d639185..13010e1ad 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -1110,6 +1110,7 @@ da: functional: Din konto er fuldt funktionel. pending: Din ansøgning afventer gennemgang af vores medarbejdere. Dette kan tage noget tid. Du modtager en e-mail, hvis din ansøgning godkendes. redirecting_to: Din konto er inaktiv, da den pt. er omdirigerer til %{acct}. + self_destruct: Da %{domain} er under nedlukning, vil kontoadgangen være begrænset. view_strikes: Se tidligere anmeldelser af din konto too_fast: Formularen indsendt for hurtigt, forsøg igen. use_security_key: Brug sikkerhedsnøgle @@ -1367,6 +1368,7 @@ da: '86400': 1 dag expires_in_prompt: Aldrig generate: Generér invitationslink + invalid: Denne invitation er ikke gyldig invited_by: 'Du blev inviteret af:' max_uses: one: 1 benyttelse @@ -1579,6 +1581,9 @@ da: over_daily_limit: Den daglige grænse på %{limit} planlagte indlæg er nået over_total_limit: Grænsen på %{limit} planlagte indlæg er nået too_soon: Den planlagte dato skal være i fremtiden + self_destruct: + lead_html: Desværre lukker <strong>%{domain}</strong> permanent. Har man en konto dér, vil fortsat brug heraf ikke være mulig. Man kan dog stadig anmode om en sikkerhedskopi af sine data. + title: Denne server er under nedlukning sessions: activity: Seneste aktivitet browser: Browser diff --git a/config/locales/de.yml b/config/locales/de.yml index 2ab90611f..81fa4b57f 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -1368,6 +1368,7 @@ de: '86400': 1 Tag expires_in_prompt: Nie generate: Einladungslink erstellen + invalid: Diese Einladung ist ungültig invited_by: 'Du wurdest eingeladen von:' max_uses: one: Eine Verwendung diff --git a/config/locales/devise.be.yml b/config/locales/devise.be.yml index 3bf35daed..e5e8aea91 100644 --- a/config/locales/devise.be.yml +++ b/config/locales/devise.be.yml @@ -18,7 +18,7 @@ be: unconfirmed: Вы павінны пацвердзіць свой адрас электроннай пошты, перш чым працягнуць mailer: confirmation_instructions: - action: Пацвердзіце адрас электроннай пошты + action: Пацвердзіць адрас электроннай пошты action_with_app: Пацвердзіць і вярнуцца да %{app} explanation: Вы стварылі ўліковы запіс на %{host} з гэтым адрасам электроннай пошты. Вам спатрэбіцца ўсяго адзін клік, каб пацвердзіць яго. Калі гэта былі не вы, то проста праігнаруйце гэты ліст. explanation_when_pending: Вы падалі заяўку на запрашэнне на %{host} з гэтым адрасам электроннай пошты. Як толькі вы пацвердзіце свой адрас электроннай пошты, мы разгледзім вашу заяўку. Вы можаце ўвайсці, каб змяніць свае дадзеныя або выдаліць свой уліковы запіс, але вы не можаце атрымаць доступ да большасці функцый, пакуль ваш уліковы запіс не будзе зацверджаны. Калі ваша заяўка будзе адхілена, вашы даныя будуць выдалены, таму ад вас не спатрэбіцца ніякіх дадатковых дзеянняў. Калі гэта былі не вы, ігнаруйце гэты ліст diff --git a/config/locales/doorkeeper.af.yml b/config/locales/doorkeeper.af.yml index 504c7f507..9e05f403f 100644 --- a/config/locales/doorkeeper.af.yml +++ b/config/locales/doorkeeper.af.yml @@ -149,6 +149,7 @@ af: write:blocks: blokkeer rekeninge en domeine write:bookmarks: laat ’n boekmerk by plasings write:conversations: doof en wis gesprekke uit + write:favourites: gunsteling plasings write:filters: skep filters write:follows: volg mense write:lists: skep lyste diff --git a/config/locales/es-AR.yml b/config/locales/es-AR.yml index 127f1262a..9175a1fc1 100644 --- a/config/locales/es-AR.yml +++ b/config/locales/es-AR.yml @@ -1368,6 +1368,7 @@ es-AR: '86400': 1 día expires_in_prompt: Nunca generate: Generar enlace de invitación + invalid: Esta invitación no es válida invited_by: 'Fuiste invitado por:' max_uses: one: 1 uso diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml index ad2fb184e..75d329b0a 100644 --- a/config/locales/es-MX.yml +++ b/config/locales/es-MX.yml @@ -1368,6 +1368,7 @@ es-MX: '86400': 1 día expires_in_prompt: Nunca generate: Generar + invalid: Esta invitación no es válida invited_by: 'Fuiste invitado por:' max_uses: one: 1 uso diff --git a/config/locales/es.yml b/config/locales/es.yml index d071e19a1..34d1c85dc 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1368,6 +1368,7 @@ es: '86400': 1 día expires_in_prompt: Nunca generate: Generar + invalid: Esta invitación no es válida invited_by: 'Fuiste invitado por:' max_uses: one: 1 uso diff --git a/config/locales/eu.yml b/config/locales/eu.yml index 3b7bba6ce..dd7575c57 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -1363,6 +1363,7 @@ eu: '86400': Egun 1 expires_in_prompt: Inoiz ez generate: Sortu + invalid: Gonbidapen hau ez da baliozkoa invited_by: 'Honek gonbidatu zaitu:' max_uses: one: Erabilera 1 diff --git a/config/locales/fa.yml b/config/locales/fa.yml index 8569d2e37..04fb52e75 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -1157,6 +1157,7 @@ fa: '86400': ۱ روز expires_in_prompt: هیچ وقت generate: ساختن + invalid: این دعوتنامه معتبر نیست invited_by: 'دعوتکنندهٔ شما:' max_uses: one: ۱ بار diff --git a/config/locales/fo.yml b/config/locales/fo.yml index 8e98cc864..ffa54f588 100644 --- a/config/locales/fo.yml +++ b/config/locales/fo.yml @@ -1368,6 +1368,7 @@ fo: '86400': 1 dag expires_in_prompt: Ongantíð generate: Ger innbjóðingarleinki + invalid: Henda innbjóðing er ikki gildug invited_by: 'Tú var bjóðað/ur av:' max_uses: one: 1 brúk diff --git a/config/locales/fr-QC.yml b/config/locales/fr-QC.yml index 41b71f569..f7425ea32 100644 --- a/config/locales/fr-QC.yml +++ b/config/locales/fr-QC.yml @@ -1368,6 +1368,7 @@ fr-QC: '86400': 1 jour expires_in_prompt: Jamais generate: Générer un lien d'invitation + invalid: Cette invitation n’est pas valide invited_by: 'Vous avez été invité·e par :' max_uses: one: 1 utilisation diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 79d8b92c2..289afb226 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -1368,6 +1368,7 @@ fr: '86400': 1 jour expires_in_prompt: Jamais generate: Générer un lien d'invitation + invalid: Cette invitation n’est pas valide invited_by: 'Vous avez été invité·e par :' max_uses: one: 1 utilisation diff --git a/config/locales/fy.yml b/config/locales/fy.yml index 011899d15..de609a14d 100644 --- a/config/locales/fy.yml +++ b/config/locales/fy.yml @@ -1368,6 +1368,7 @@ fy: '86400': 1 dei expires_in_prompt: Nea generate: Utnûgingskeppeling generearje + invalid: Dizze útnûging is net jildich invited_by: 'Jo binne útnûge troch:' max_uses: one: 1 kear diff --git a/config/locales/gl.yml b/config/locales/gl.yml index f86b4fc20..0075c6592 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -1368,6 +1368,7 @@ gl: '86400': 1 día expires_in_prompt: Nunca generate: Xerar + invalid: Este convite non é válido invited_by: 'Convidoute:' max_uses: one: 1 uso diff --git a/config/locales/he.yml b/config/locales/he.yml index ea2ab8f29..11e5db453 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -113,8 +113,8 @@ he: previous_strikes_description_html: many: לחשבון הזה יש <strong>%{count}</strong> פסילות. one: לחשבון הזה פסילה <strong>אחת</strong>. - other: לחשבון הזה <strong>%{count}</strong> פסילות. - two: לחשבון הזה <strong>%{count}</strong> פסילות. + other: לחשבון הזה יש <strong>%{count}</strong> פסילות. + two: לחשבון הזה יש <strong>שתי</strong> פסילות. promote: להעלות בדרגה protocol: פרטיכל public: פומבי @@ -1418,6 +1418,7 @@ he: '86400': יום אחד expires_in_prompt: לעולם לא generate: יצירת קישור להזמנה + invalid: הזמנה זו אינה תקפה invited_by: הוזמנת ע"י max_uses: many: "%{count} שימושים" diff --git a/config/locales/hu.yml b/config/locales/hu.yml index c01f38a9a..48f9d5b9d 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -1368,6 +1368,7 @@ hu: '86400': 1 nap expires_in_prompt: Soha generate: Generálás + invalid: Ez a meghívó nem érvényes invited_by: 'Téged meghívott:' max_uses: one: 1 használat diff --git a/config/locales/is.yml b/config/locales/is.yml index e0eb95569..390ce0ac0 100644 --- a/config/locales/is.yml +++ b/config/locales/is.yml @@ -1372,6 +1372,7 @@ is: '86400': 1 dagur expires_in_prompt: Aldrei generate: Útbúa boðstengil + invalid: Þetta boð er ekki gilt invited_by: 'Þér var boðið af:' max_uses: one: 1 afnot diff --git a/config/locales/it.yml b/config/locales/it.yml index e62ea620c..f35e9e42b 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -1370,6 +1370,7 @@ it: '86400': 1 giorno expires_in_prompt: Mai generate: Genera + invalid: Questo invito non è valido invited_by: 'Sei stato invitato da:' max_uses: one: un uso diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 72eafc8fb..fb193c75f 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -1345,6 +1345,7 @@ ko: '86400': 1 일 expires_in_prompt: 영원히 generate: 초대 링크 생성하기 + invalid: 이 초대는 올바르지 않습니다 invited_by: '당신을 초대한 사람:' max_uses: other: "%{count}회" diff --git a/config/locales/lt.yml b/config/locales/lt.yml index 2f4d813c9..529eb5a44 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -383,6 +383,7 @@ lt: '86400': 1 dienos expires_in_prompt: Niekada generate: Generuoti + invalid: Šis kvietimas negalioja. invited_by: 'Jus pakvietė:' max_uses: few: "%{count} panaudojimai" diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 2a8a59d76..94a1f29f7 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -1368,6 +1368,7 @@ nl: '86400': 1 dag expires_in_prompt: Nooit generate: Uitnodigingslink genereren + invalid: Deze uitnodiging is niet geldig invited_by: 'Jij bent uitgenodigd door:' max_uses: one: 1 keer diff --git a/config/locales/nn.yml b/config/locales/nn.yml index acd6b206e..4925d4463 100644 --- a/config/locales/nn.yml +++ b/config/locales/nn.yml @@ -1368,6 +1368,7 @@ nn: '86400': 1 dag expires_in_prompt: Aldri generate: Lag innbydingslenkje + invalid: Denne invitasjonen er ikkje gyldig invited_by: 'Du vart innboden av:' max_uses: one: 1 bruk diff --git a/config/locales/no.yml b/config/locales/no.yml index 75085fa5a..a1058bf9f 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -1368,6 +1368,7 @@ '86400': 1 dag expires_in_prompt: Aldri generate: Generer + invalid: Denne invitasjonen er ikke gyldig invited_by: 'Du ble invitert av:' max_uses: one: 1 bruk diff --git a/config/locales/pl.yml b/config/locales/pl.yml index e02cad039..608599724 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -1418,6 +1418,7 @@ pl: '86400': dobie expires_in_prompt: Nigdy generate: Wygeneruj + invalid: Niepoprawne zaproszenie invited_by: 'Zostałeś(-aś) zaproszony(-a) przez:' max_uses: few: "%{count} użycia" diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index d99c265fe..3eb2950bd 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -1368,6 +1368,7 @@ pt-BR: '86400': 1 dia expires_in_prompt: Nunca generate: Gerar convite + invalid: Este convite não é válido invited_by: 'Você recebeu convite de:' max_uses: one: 1 uso diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml index a40ac02f4..ce7479aa8 100644 --- a/config/locales/pt-PT.yml +++ b/config/locales/pt-PT.yml @@ -1368,6 +1368,7 @@ pt-PT: '86400': 1 dia expires_in_prompt: Nunca generate: Gerar hiperligação de convite + invalid: Este convite não é válido invited_by: 'Foi convidado por:' max_uses: one: 1 uso diff --git a/config/locales/simple_form.be.yml b/config/locales/simple_form.be.yml index 1ed6c8084..7ad87cdd9 100644 --- a/config/locales/simple_form.be.yml +++ b/config/locales/simple_form.be.yml @@ -53,7 +53,7 @@ be: password: Не менш за 8 сімвалаў phrase: Параўнанне адбудзецца нягледзячы на рэгістр тэксту і папярэджанні аб змесціве допісу scopes: Якімі API праграм будзе дазволена карыстацца. Калі вы абярэце найвышэйшы ўзровень, не трэба абіраць асобныя. - setting_aggregate_reblogs: Не паказваць новыя пашырэнні для допісаў, якія нядаўна пашырылі(уплывае выключна на будучыя пашырэнні) + setting_aggregate_reblogs: Не паказваць новыя пашырэнні для допісаў, якія пашырылі нядаўна (закранае толькі нядаўнія пашырэнні) setting_always_send_emails: Звычайна лісты з апавяшчэннямі не будуць дасылацца, калі вы актыўна карыстаецеся Mastodon setting_default_sensitive: Далікатныя медыя прадвызначана схаваныя. Іх можна адкрыць адзіным клікам setting_display_media_default: Хаваць медыя пазначаныя як далікатныя diff --git a/config/locales/sk.yml b/config/locales/sk.yml index 954ef745d..63779e5bd 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -592,6 +592,8 @@ sk: title: Ohľadom appearance: title: Vzhľad + content_retention: + title: Ponechanie obsahu discovery: follow_recommendations: Odporúčania pre nasledovanie profile_directory: Katalóg profilov @@ -616,6 +618,7 @@ sk: delete: Vymaž nahratý súbor destroyed_msg: Nahratie bolo zo stránky úspešne vymazané! software_updates: + critical_update: Kritické — prosím aktualizuj rýchlo documentation_link: Zisti viac title: Dostupné aktualizácie types: @@ -646,6 +649,10 @@ sk: appeal_approved: Namietnuté appeal_rejected: Námietka zamietnutá system_checks: + elasticsearch_preset: + action: Pozri dokumentáciu + elasticsearch_preset_single_node: + action: Pozri dokumentáciu rules_check: action: Spravuj serverové pravidlá message_html: Neurčil/a si žiadne serverové pravidlá. @@ -925,6 +932,7 @@ sk: '86400': 1 deň expires_in_prompt: Nikdy generate: Vygeneruj + invalid: Táto pozvánka je neplatná invited_by: 'Bol/a si pozvaný/á užívateľom:' max_uses: few: "%{count} využití" diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 77ddfd6e2..b31fe118a 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -1418,6 +1418,7 @@ sl: '86400': 1 dan expires_in_prompt: Nikoli generate: Ustvari + invalid: To povabilo ni veljavno invited_by: 'Povabil/a vas je:' max_uses: few: "%{count} uporabe" diff --git a/config/locales/sq.yml b/config/locales/sq.yml index e4fb811ce..bd01a8089 100644 --- a/config/locales/sq.yml +++ b/config/locales/sq.yml @@ -1362,6 +1362,7 @@ sq: '86400': 1 ditë expires_in_prompt: Kurrë generate: Prodho lidhje ftese + invalid: Kjo ftesë s’është e vlefshme invited_by: 'Qetë ftuar nga:' max_uses: one: 1 përdorim diff --git a/config/locales/sr-Latn.yml b/config/locales/sr-Latn.yml index cba4354f0..6ccec7fd9 100644 --- a/config/locales/sr-Latn.yml +++ b/config/locales/sr-Latn.yml @@ -1393,6 +1393,7 @@ sr-Latn: '86400': 1 dan expires_in_prompt: Nikad generate: Generiši + invalid: Ova pozivnica nije važeća invited_by: 'Pozvao Vas je:' max_uses: few: "%{count} korišćenja" diff --git a/config/locales/sr.yml b/config/locales/sr.yml index 902f13ad6..407908517 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -1393,6 +1393,7 @@ sr: '86400': 1 дан expires_in_prompt: Никад generate: Генериши + invalid: Ова позивница није важећа invited_by: 'Позвао Вас је:' max_uses: few: "%{count} коришћења" diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 736d839f9..8126455f4 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -1368,6 +1368,7 @@ sv: '86400': 1 dag expires_in_prompt: Aldrig generate: Skapa + invalid: Ogiltig inbjudan invited_by: 'Du blev inbjuden av:' max_uses: one: 1 användning diff --git a/config/locales/th.yml b/config/locales/th.yml index 33d8a898e..661899896 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -1343,6 +1343,7 @@ th: '86400': 1 วัน expires_in_prompt: ไม่เลย generate: สร้างลิงก์เชิญ + invalid: คำเชิญนี้ไม่ถูกต้อง invited_by: 'คุณได้รับเชิญโดย:' max_uses: other: "%{count} การใช้งาน" diff --git a/config/locales/tr.yml b/config/locales/tr.yml index c9adfd913..9d4d95a83 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -1368,6 +1368,7 @@ tr: '86400': 1 gün expires_in_prompt: Asla generate: Davet bağlantısı oluştur + invalid: Bu davet geçerli değil invited_by: 'Davet edildiniz:' max_uses: one: 1 kullanım diff --git a/config/locales/uk.yml b/config/locales/uk.yml index eb947a7d0..2261c647b 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -1418,6 +1418,7 @@ uk: '86400': 1 день expires_in_prompt: Ніколи generate: Згенерувати + invalid: Це запрошення не дійсне invited_by: 'Вас запросив:' max_uses: few: "%{count} використання" diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index 7902cea4d..b98193065 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -1343,6 +1343,7 @@ zh-CN: '86400': 1 天后 expires_in_prompt: 永不过期 generate: 生成邀请链接 + invalid: 此邀请无效 invited_by: 你的邀请人是: max_uses: other: "%{count} 次" diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml index 58aeac841..f13cedad6 100644 --- a/config/locales/zh-HK.yml +++ b/config/locales/zh-HK.yml @@ -1343,6 +1343,7 @@ zh-HK: '86400': 1 天後 expires_in_prompt: 永不過期 generate: 生成邀請連結 + invalid: 此邀請無效 invited_by: 你的邀請人是: max_uses: other: "%{count} 次" diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 7bcc13396..3063b7afd 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -1345,6 +1345,7 @@ zh-TW: '86400': 1 天後 expires_in_prompt: 永不過期 generate: 建立邀請連結 + invalid: 此邀請是無效的 invited_by: 您的邀請人是: max_uses: other: "%{count} 則" From 4eb4e8b22c564222d8bc69cca754cbfb9a524611 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 14 Nov 2023 12:07:13 +0100 Subject: [PATCH 23/63] Update Yarn to v4.0.2 (#27851) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2230b20ea..7c73d17c1 100644 --- a/package.json +++ b/package.json @@ -242,5 +242,5 @@ "*.{js,jsx,ts,tsx}": "eslint --fix", "*.{css,scss}": "stylelint --fix" }, - "packageManager": "yarn@4.0.1" + "packageManager": "yarn@4.0.2" } From d562fb84596f7cf2380e7c1cc465e1d4d478d759 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Tue, 14 Nov 2023 09:34:30 -0500 Subject: [PATCH 24/63] Specs for minimal CSP policy in `Api::` controllers (#27845) --- app/controllers/api/base_controller.rb | 21 +-------- .../concerns/api/content_security_policy.rb | 27 ++++++++++++ spec/requests/api/v1/csp_spec.rb | 43 +++++++++++++++++++ 3 files changed, 71 insertions(+), 20 deletions(-) create mode 100644 app/controllers/concerns/api/content_security_policy.rb create mode 100644 spec/requests/api/v1/csp_spec.rb diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index c764b4510..135c57565 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -7,6 +7,7 @@ class Api::BaseController < ApplicationController include RateLimitHeaders include AccessTokenTrackingConcern include ApiCachingConcern + include Api::ContentSecurityPolicy skip_before_action :require_functional!, unless: :limited_federation_mode? @@ -17,26 +18,6 @@ class Api::BaseController < ApplicationController protect_from_forgery with: :null_session - content_security_policy do |p| - # Set every directive that does not have a fallback - p.default_src :none - p.frame_ancestors :none - p.form_action :none - - # Disable every directive with a fallback to cut on response size - p.base_uri false - p.font_src false - p.img_src false - p.style_src false - p.media_src false - p.frame_src false - p.manifest_src false - p.connect_src false - p.script_src false - p.child_src false - p.worker_src false - end - rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e| render json: { error: e.to_s }, status: 422 end diff --git a/app/controllers/concerns/api/content_security_policy.rb b/app/controllers/concerns/api/content_security_policy.rb new file mode 100644 index 000000000..8116dca57 --- /dev/null +++ b/app/controllers/concerns/api/content_security_policy.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Api::ContentSecurityPolicy + extend ActiveSupport::Concern + + included do + content_security_policy do |policy| + # Set every directive that does not have a fallback + policy.default_src :none + policy.frame_ancestors :none + policy.form_action :none + + # Disable every directive with a fallback to cut on response size + policy.base_uri false + policy.font_src false + policy.img_src false + policy.style_src false + policy.media_src false + policy.frame_src false + policy.manifest_src false + policy.connect_src false + policy.script_src false + policy.child_src false + policy.worker_src false + end + end +end diff --git a/spec/requests/api/v1/csp_spec.rb b/spec/requests/api/v1/csp_spec.rb new file mode 100644 index 000000000..2db52ac72 --- /dev/null +++ b/spec/requests/api/v1/csp_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'API namespace minimal Content-Security-Policy' do + before { stub_tests_controller } + + after { Rails.application.reload_routes! } + + it 'returns the correct CSP headers' do + get '/api/v1/tests' + + expect(response).to have_http_status(200) + expect(response.headers['Content-Security-Policy']).to eq(minimal_csp_headers) + end + + private + + def stub_tests_controller + stub_const('Api::V1::TestsController', api_tests_controller) + + Rails.application.routes.draw do + get '/api/v1/tests', to: 'api/v1/tests#index' + end + end + + def api_tests_controller + Class.new(Api::BaseController) do + def index + head 200 + end + + private + + def user_signed_in? = false + def current_user = nil + end + end + + def minimal_csp_headers + "default-src 'none'; frame-ancestors 'none'; form-action 'none'" + end +end From b2c5b20ef27edd948eca8d6bd2014b7a5efaec11 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Tue, 14 Nov 2023 09:52:59 -0500 Subject: [PATCH 25/63] Fix `RSpec/AnyInstance` cop (#27810) --- .rubocop_todo.yml | 17 ---------- .../activitypub/inboxes_controller_spec.rb | 2 +- .../admin/accounts_controller_spec.rb | 3 +- .../admin/resets_controller_spec.rb | 18 ++++++++--- .../auth/sessions_controller_spec.rb | 6 ++-- .../confirmations_controller_spec.rb | 31 +++++++++---------- .../recovery_codes_controller_spec.rb | 6 ++-- spec/lib/request_spec.rb | 5 ++- spec/lib/status_filter_spec.rb | 6 ++-- spec/models/account_spec.rb | 14 +++++++-- spec/models/setting_spec.rb | 6 ++-- .../process_collection_service_spec.rb | 9 ++++-- .../activitypub/delivery_worker_spec.rb | 3 +- .../web/push_notification_worker_spec.rb | 4 +-- 14 files changed, 70 insertions(+), 60 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index f9d14fd55..167204604 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -41,23 +41,6 @@ Metrics/CyclomaticComplexity: Metrics/PerceivedComplexity: Max: 27 -RSpec/AnyInstance: - Exclude: - - 'spec/controllers/activitypub/inboxes_controller_spec.rb' - - 'spec/controllers/admin/accounts_controller_spec.rb' - - 'spec/controllers/admin/resets_controller_spec.rb' - - 'spec/controllers/auth/sessions_controller_spec.rb' - - 'spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb' - - 'spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb' - - 'spec/lib/request_spec.rb' - - 'spec/lib/status_filter_spec.rb' - - 'spec/models/account_spec.rb' - - 'spec/models/setting_spec.rb' - - 'spec/services/activitypub/process_collection_service_spec.rb' - - 'spec/validators/follow_limit_validator_spec.rb' - - 'spec/workers/activitypub/delivery_worker_spec.rb' - - 'spec/workers/web/push_notification_worker_spec.rb' - # Configuration parameters: CountAsOne. RSpec/ExampleLength: Max: 22 diff --git a/spec/controllers/activitypub/inboxes_controller_spec.rb b/spec/controllers/activitypub/inboxes_controller_spec.rb index 030a30326..feca543cb 100644 --- a/spec/controllers/activitypub/inboxes_controller_spec.rb +++ b/spec/controllers/activitypub/inboxes_controller_spec.rb @@ -58,7 +58,7 @@ RSpec.describe ActivityPub::InboxesController do before do allow(ActivityPub::FollowersSynchronizationWorker).to receive(:perform_async).and_return(nil) - allow_any_instance_of(Account).to receive(:local_followers_hash).and_return('somehash') + allow(remote_account).to receive(:local_followers_hash).and_return('somehash') request.headers['Collection-Synchronization'] = synchronization_header post :create, body: '{}' diff --git a/spec/controllers/admin/accounts_controller_spec.rb b/spec/controllers/admin/accounts_controller_spec.rb index 3b8fa2f71..307e81950 100644 --- a/spec/controllers/admin/accounts_controller_spec.rb +++ b/spec/controllers/admin/accounts_controller_spec.rb @@ -227,7 +227,8 @@ RSpec.describe Admin::AccountsController do let(:account) { Fabricate(:account, domain: 'example.com') } before do - allow_any_instance_of(ResolveAccountService).to receive(:call) + service = instance_double(ResolveAccountService, call: nil) + allow(ResolveAccountService).to receive(:new).and_return(service) end context 'when user is admin' do diff --git a/spec/controllers/admin/resets_controller_spec.rb b/spec/controllers/admin/resets_controller_spec.rb index 16adb8a12..14826973c 100644 --- a/spec/controllers/admin/resets_controller_spec.rb +++ b/spec/controllers/admin/resets_controller_spec.rb @@ -13,12 +13,20 @@ describe Admin::ResetsController do describe 'POST #create' do it 'redirects to admin accounts page' do - expect_any_instance_of(User).to receive(:send_reset_password_instructions) do |value| - expect(value.account_id).to eq account.id - end - - post :create, params: { account_id: account.id } + expect do + post :create, params: { account_id: account.id } + end.to change(Devise.mailer.deliveries, :size).by(2) + expect(Devise.mailer.deliveries).to have_attributes( + first: have_attributes( + to: include(account.user.email), + subject: I18n.t('devise.mailer.password_change.subject') + ), + last: have_attributes( + to: include(account.user.email), + subject: I18n.t('devise.mailer.reset_password_instructions.subject') + ) + ) expect(response).to redirect_to(admin_account_path(account.id)) end end diff --git a/spec/controllers/auth/sessions_controller_spec.rb b/spec/controllers/auth/sessions_controller_spec.rb index 049190e2e..f341d75b7 100644 --- a/spec/controllers/auth/sessions_controller_spec.rb +++ b/spec/controllers/auth/sessions_controller_spec.rb @@ -126,7 +126,7 @@ RSpec.describe Auth::SessionsController do let!(:previous_login) { Fabricate(:login_activity, user: user, ip: previous_ip) } before do - allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return(current_ip) + allow(controller.request).to receive(:remote_ip).and_return(current_ip) user.update(current_sign_in_at: 1.month.ago) post :create, params: { user: { email: user.email, password: user.password } } end @@ -279,7 +279,9 @@ RSpec.describe Auth::SessionsController do context 'when the server has an decryption error' do before do - allow_any_instance_of(User).to receive(:validate_and_consume_otp!).and_raise(OpenSSL::Cipher::CipherError) + allow(user).to receive(:validate_and_consume_otp!).with(user.current_otp).and_raise(OpenSSL::Cipher::CipherError) + allow(User).to receive(:find_by).with(id: user.id).and_return(user) + post :create, params: { user: { otp_attempt: user.current_otp } }, session: { attempt_user_id: user.id, attempt_user_updated_at: user.updated_at.to_s } end diff --git a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb index 37381fe1f..a5a35e91d 100644 --- a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb +++ b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb @@ -61,6 +61,7 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do it 'renders page with success' do prepare_user_otp_generation prepare_user_otp_consumption + allow(controller).to receive(:current_user).and_return(user) expect do post :create, @@ -75,30 +76,28 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do end def prepare_user_otp_generation - expect_any_instance_of(User).to receive(:generate_otp_backup_codes!) do |value| - expect(value).to eq user - otp_backup_codes - end + allow(user) + .to receive(:generate_otp_backup_codes!) + .and_return(otp_backup_codes) end def prepare_user_otp_consumption - expect_any_instance_of(User).to receive(:validate_and_consume_otp!) do |value, code, options| - expect(value).to eq user - expect(code).to eq '123456' - expect(options).to eq({ otp_secret: 'thisisasecretforthespecofnewview' }) - true - end + options = { otp_secret: 'thisisasecretforthespecofnewview' } + allow(user) + .to receive(:validate_and_consume_otp!) + .with('123456', options) + .and_return(true) end end describe 'when creation fails' do subject do - expect_any_instance_of(User).to receive(:validate_and_consume_otp!) do |value, code, options| - expect(value).to eq user - expect(code).to eq '123456' - expect(options).to eq({ otp_secret: 'thisisasecretforthespecofnewview' }) - false - end + options = { otp_secret: 'thisisasecretforthespecofnewview' } + allow(user) + .to receive(:validate_and_consume_otp!) + .with('123456', options) + .and_return(false) + allow(controller).to receive(:current_user).and_return(user) expect do post :create, diff --git a/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb b/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb index 630cec428..28a40e138 100644 --- a/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb +++ b/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb @@ -9,10 +9,8 @@ describe Settings::TwoFactorAuthentication::RecoveryCodesController do it 'updates the codes and shows them on a view when signed in' do user = Fabricate(:user) otp_backup_codes = user.generate_otp_backup_codes! - expect_any_instance_of(User).to receive(:generate_otp_backup_codes!) do |value| - expect(value).to eq user - otp_backup_codes - end + allow(user).to receive(:generate_otp_backup_codes!).and_return(otp_backup_codes) + allow(controller).to receive(:current_user).and_return(user) sign_in user, scope: :user post :create, session: { challenge_passed_at: Time.now.utc } diff --git a/spec/lib/request_spec.rb b/spec/lib/request_spec.rb index f0861376b..c7620cf9b 100644 --- a/spec/lib/request_spec.rb +++ b/spec/lib/request_spec.rb @@ -64,8 +64,11 @@ describe Request do end it 'closes underlying connection' do - expect_any_instance_of(HTTP::Client).to receive(:close) + allow(subject.send(:http_client)).to receive(:close) + expect { |block| subject.perform(&block) }.to yield_control + + expect(subject.send(:http_client)).to have_received(:close) end it 'returns response which implements body_with_limit' do diff --git a/spec/lib/status_filter_spec.rb b/spec/lib/status_filter_spec.rb index c994ad419..cf6f3c795 100644 --- a/spec/lib/status_filter_spec.rb +++ b/spec/lib/status_filter_spec.rb @@ -23,7 +23,8 @@ describe StatusFilter do context 'when status policy does not allow show' do it 'filters the status' do - allow_any_instance_of(StatusPolicy).to receive(:show?).and_return(false) + policy = instance_double(StatusPolicy, show?: false) + allow(StatusPolicy).to receive(:new).and_return(policy) expect(filter).to be_filtered end @@ -74,7 +75,8 @@ describe StatusFilter do context 'when status policy does not allow show' do it 'filters the status' do - allow_any_instance_of(StatusPolicy).to receive(:show?).and_return(false) + policy = instance_double(StatusPolicy, show?: false) + allow(StatusPolicy).to receive(:new).and_return(policy) expect(filter).to be_filtered end diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index b5d942412..f77ecb055 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -209,9 +209,13 @@ RSpec.describe Account do expect(account.refresh!).to be_nil end - it 'calls not ResolveAccountService#call' do - expect_any_instance_of(ResolveAccountService).to_not receive(:call).with(acct) + it 'does not call ResolveAccountService#call' do + service = instance_double(ResolveAccountService, call: nil) + allow(ResolveAccountService).to receive(:new).and_return(service) + account.refresh! + + expect(service).to_not have_received(:call).with(acct) end end @@ -219,8 +223,12 @@ RSpec.describe Account do let(:domain) { 'example.com' } it 'calls ResolveAccountService#call' do - expect_any_instance_of(ResolveAccountService).to receive(:call).with(acct).once + service = instance_double(ResolveAccountService, call: nil) + allow(ResolveAccountService).to receive(:new).and_return(service) + account.refresh! + + expect(service).to have_received(:call).with(acct).once end end end diff --git a/spec/models/setting_spec.rb b/spec/models/setting_spec.rb index b08136a1c..e98062810 100644 --- a/spec/models/setting_spec.rb +++ b/spec/models/setting_spec.rb @@ -52,7 +52,8 @@ RSpec.describe Setting do before do allow(RailsSettings::Settings).to receive(:object).with(key).and_return(object) allow(described_class).to receive(:default_settings).and_return(default_settings) - allow_any_instance_of(Settings::ScopedSettings).to receive(:thing_scoped).and_return(records) + settings_double = instance_double(Settings::ScopedSettings, thing_scoped: records) + allow(Settings::ScopedSettings).to receive(:new).and_return(settings_double) Rails.cache.delete(cache_key) end @@ -128,7 +129,8 @@ RSpec.describe Setting do describe '.all_as_records' do before do - allow_any_instance_of(Settings::ScopedSettings).to receive(:thing_scoped).and_return(records) + settings_double = instance_double(Settings::ScopedSettings, thing_scoped: records) + allow(Settings::ScopedSettings).to receive(:new).and_return(settings_double) allow(described_class).to receive(:default_settings).and_return(default_settings) end diff --git a/spec/services/activitypub/process_collection_service_spec.rb b/spec/services/activitypub/process_collection_service_spec.rb index ede9f5c04..df526daf3 100644 --- a/spec/services/activitypub/process_collection_service_spec.rb +++ b/spec/services/activitypub/process_collection_service_spec.rb @@ -76,7 +76,8 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do let(:forwarder) { Fabricate(:account, domain: 'example.com', uri: 'http://example.com/other_account') } it 'does not process payload if no signature exists' do - allow_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_actor!).and_return(nil) + signature_double = instance_double(ActivityPub::LinkedDataSignature, verify_actor!: nil) + allow(ActivityPub::LinkedDataSignature).to receive(:new).and_return(signature_double) allow(ActivityPub::Activity).to receive(:factory) subject.call(json, forwarder) @@ -87,7 +88,8 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do it 'processes payload with actor if valid signature exists' do payload['signature'] = { 'type' => 'RsaSignature2017' } - allow_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_actor!).and_return(actor) + signature_double = instance_double(ActivityPub::LinkedDataSignature, verify_actor!: actor) + allow(ActivityPub::LinkedDataSignature).to receive(:new).and_return(signature_double) allow(ActivityPub::Activity).to receive(:factory).with(instance_of(Hash), actor, instance_of(Hash)) subject.call(json, forwarder) @@ -98,7 +100,8 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do it 'does not process payload if invalid signature exists' do payload['signature'] = { 'type' => 'RsaSignature2017' } - allow_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_actor!).and_return(nil) + signature_double = instance_double(ActivityPub::LinkedDataSignature, verify_actor!: nil) + allow(ActivityPub::LinkedDataSignature).to receive(:new).and_return(signature_double) allow(ActivityPub::Activity).to receive(:factory) subject.call(json, forwarder) diff --git a/spec/workers/activitypub/delivery_worker_spec.rb b/spec/workers/activitypub/delivery_worker_spec.rb index d39393d50..efce610ae 100644 --- a/spec/workers/activitypub/delivery_worker_spec.rb +++ b/spec/workers/activitypub/delivery_worker_spec.rb @@ -11,7 +11,8 @@ describe ActivityPub::DeliveryWorker do let(:payload) { 'test' } before do - allow_any_instance_of(Account).to receive(:remote_followers_hash).with('https://example.com/api').and_return('somehash') + allow(sender).to receive(:remote_followers_hash).with('https://example.com/api').and_return('somehash') + allow(Account).to receive(:find).with(sender.id).and_return(sender) end describe 'perform' do diff --git a/spec/workers/web/push_notification_worker_spec.rb b/spec/workers/web/push_notification_worker_spec.rb index 822ef5257..637206a40 100644 --- a/spec/workers/web/push_notification_worker_spec.rb +++ b/spec/workers/web/push_notification_worker_spec.rb @@ -23,8 +23,8 @@ describe Web::PushNotificationWorker do describe 'perform' do before do - allow_any_instance_of(subscription.class).to receive(:contact_email).and_return(contact_email) - allow_any_instance_of(subscription.class).to receive(:vapid_key).and_return(vapid_key) + allow(subscription).to receive_messages(contact_email: contact_email, vapid_key: vapid_key) + allow(Web::PushSubscription).to receive(:find).with(subscription.id).and_return(subscription) allow(Webpush::Encryption).to receive(:encrypt).and_return(payload) allow(JWT).to receive(:encode).and_return('jwt.encoded.payload') From 7e1a77ea51e6dc4aecbf678f8928aa96698fa072 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Tue, 14 Nov 2023 09:53:31 -0500 Subject: [PATCH 26/63] Add base class for `api/v1/timelines/*` controllers (#27840) --- .../api/v1/timelines/base_controller.rb | 33 +++++++++++++++++++ .../api/v1/timelines/home_controller.rb | 25 +++----------- .../api/v1/timelines/list_controller.rb | 24 +++----------- .../api/v1/timelines/public_controller.rb | 25 +++----------- .../api/v1/timelines/tag_controller.rb | 25 +++----------- 5 files changed, 52 insertions(+), 80 deletions(-) create mode 100644 app/controllers/api/v1/timelines/base_controller.rb diff --git a/app/controllers/api/v1/timelines/base_controller.rb b/app/controllers/api/v1/timelines/base_controller.rb new file mode 100644 index 000000000..173e173cc --- /dev/null +++ b/app/controllers/api/v1/timelines/base_controller.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +class Api::V1::Timelines::BaseController < Api::BaseController + after_action :insert_pagination_headers, unless: -> { @statuses.empty? } + + private + + def insert_pagination_headers + set_pagination_headers(next_path, prev_path) + end + + def pagination_max_id + @statuses.last.id + end + + def pagination_since_id + @statuses.first.id + end + + def next_path_params + permitted_params.merge(max_id: pagination_max_id) + end + + def prev_path_params + permitted_params.merge(min_id: pagination_since_id) + end + + def permitted_params + params + .slice(*self.class::PERMITTED_PARAMS) + .permit(*self.class::PERMITTED_PARAMS) + end +end diff --git a/app/controllers/api/v1/timelines/home_controller.rb b/app/controllers/api/v1/timelines/home_controller.rb index 83b8cb4c6..36fdbea64 100644 --- a/app/controllers/api/v1/timelines/home_controller.rb +++ b/app/controllers/api/v1/timelines/home_controller.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true -class Api::V1::Timelines::HomeController < Api::BaseController +class Api::V1::Timelines::HomeController < Api::V1::Timelines::BaseController before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: [:show] before_action :require_user!, only: [:show] - after_action :insert_pagination_headers, unless: -> { @statuses.empty? } + + PERMITTED_PARAMS = %i(local limit).freeze def show with_read_replica do @@ -40,27 +41,11 @@ class Api::V1::Timelines::HomeController < Api::BaseController HomeFeed.new(current_account) end - def insert_pagination_headers - set_pagination_headers(next_path, prev_path) - end - - def pagination_params(core_params) - params.slice(:local, :limit).permit(:local, :limit).merge(core_params) - end - def next_path - api_v1_timelines_home_url pagination_params(max_id: pagination_max_id) + api_v1_timelines_home_url next_path_params end def prev_path - api_v1_timelines_home_url pagination_params(min_id: pagination_since_id) - end - - def pagination_max_id - @statuses.last.id - end - - def pagination_since_id - @statuses.first.id + api_v1_timelines_home_url prev_path_params end end diff --git a/app/controllers/api/v1/timelines/list_controller.rb b/app/controllers/api/v1/timelines/list_controller.rb index a15eae468..14b884ecd 100644 --- a/app/controllers/api/v1/timelines/list_controller.rb +++ b/app/controllers/api/v1/timelines/list_controller.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true -class Api::V1::Timelines::ListController < Api::BaseController +class Api::V1::Timelines::ListController < Api::V1::Timelines::BaseController before_action -> { doorkeeper_authorize! :read, :'read:lists' } before_action :require_user! before_action :set_list before_action :set_statuses - after_action :insert_pagination_headers, unless: -> { @statuses.empty? } + PERMITTED_PARAMS = %i(limit).freeze def show render json: @statuses, @@ -41,27 +41,11 @@ class Api::V1::Timelines::ListController < Api::BaseController ListFeed.new(@list) end - def insert_pagination_headers - set_pagination_headers(next_path, prev_path) - end - - def pagination_params(core_params) - params.slice(:limit).permit(:limit).merge(core_params) - end - def next_path - api_v1_timelines_list_url params[:id], pagination_params(max_id: pagination_max_id) + api_v1_timelines_list_url params[:id], next_path_params end def prev_path - api_v1_timelines_list_url params[:id], pagination_params(min_id: pagination_since_id) - end - - def pagination_max_id - @statuses.last.id - end - - def pagination_since_id - @statuses.first.id + api_v1_timelines_list_url params[:id], prev_path_params end end diff --git a/app/controllers/api/v1/timelines/public_controller.rb b/app/controllers/api/v1/timelines/public_controller.rb index 5bbd92b9e..35af8dc4b 100644 --- a/app/controllers/api/v1/timelines/public_controller.rb +++ b/app/controllers/api/v1/timelines/public_controller.rb @@ -1,8 +1,9 @@ # frozen_string_literal: true -class Api::V1::Timelines::PublicController < Api::BaseController +class Api::V1::Timelines::PublicController < Api::V1::Timelines::BaseController before_action :require_user!, only: [:show], if: :require_auth? - after_action :insert_pagination_headers, unless: -> { @statuses.empty? } + + PERMITTED_PARAMS = %i(local remote limit only_media).freeze def show cache_if_unauthenticated! @@ -42,27 +43,11 @@ class Api::V1::Timelines::PublicController < Api::BaseController ) end - def insert_pagination_headers - set_pagination_headers(next_path, prev_path) - end - - def pagination_params(core_params) - params.slice(:local, :remote, :limit, :only_media).permit(:local, :remote, :limit, :only_media).merge(core_params) - end - def next_path - api_v1_timelines_public_url pagination_params(max_id: pagination_max_id) + api_v1_timelines_public_url next_path_params end def prev_path - api_v1_timelines_public_url pagination_params(min_id: pagination_since_id) - end - - def pagination_max_id - @statuses.last.id - end - - def pagination_since_id - @statuses.first.id + api_v1_timelines_public_url prev_path_params end end diff --git a/app/controllers/api/v1/timelines/tag_controller.rb b/app/controllers/api/v1/timelines/tag_controller.rb index a79d65c12..4ba439dbb 100644 --- a/app/controllers/api/v1/timelines/tag_controller.rb +++ b/app/controllers/api/v1/timelines/tag_controller.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true -class Api::V1::Timelines::TagController < Api::BaseController +class Api::V1::Timelines::TagController < Api::V1::Timelines::BaseController before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :show, if: :require_auth? before_action :load_tag - after_action :insert_pagination_headers, unless: -> { @statuses.empty? } + + PERMITTED_PARAMS = %i(local limit only_media).freeze def show cache_if_unauthenticated! @@ -51,27 +52,11 @@ class Api::V1::Timelines::TagController < Api::BaseController ) end - def insert_pagination_headers - set_pagination_headers(next_path, prev_path) - end - - def pagination_params(core_params) - params.slice(:local, :limit, :only_media).permit(:local, :limit, :only_media).merge(core_params) - end - def next_path - api_v1_timelines_tag_url params[:id], pagination_params(max_id: pagination_max_id) + api_v1_timelines_tag_url params[:id], next_path_params end def prev_path - api_v1_timelines_tag_url params[:id], pagination_params(min_id: pagination_since_id) - end - - def pagination_max_id - @statuses.last.id - end - - def pagination_since_id - @statuses.first.id + api_v1_timelines_tag_url params[:id], prev_path_params end end From 2b038b4f8951845bee470e18ffb11fab29aacb51 Mon Sep 17 00:00:00 2001 From: ppnplus <54897463+ppnplus@users.noreply.github.com> Date: Tue, 14 Nov 2023 22:33:59 +0700 Subject: [PATCH 27/63] Added Thai diacritics and tone marks in HASHTAG_INVALID_CHARS_RE (#26576) --- app/models/tag.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/tag.rb b/app/models/tag.rb index 8fab98fb5..46e55d74f 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -37,7 +37,7 @@ class Tag < ApplicationRecord HASHTAG_RE = %r{(?<![=/)\w])#(#{HASHTAG_NAME_PAT})}i HASHTAG_NAME_RE = /\A(#{HASHTAG_NAME_PAT})\z/i - HASHTAG_INVALID_CHARS_RE = /[^[:alnum:]#{HASHTAG_SEPARATORS}]/ + HASHTAG_INVALID_CHARS_RE = /[^[:alnum:]\u0E47-\u0E4E#{HASHTAG_SEPARATORS}]/ validates :name, presence: true, format: { with: HASHTAG_NAME_RE } validates :display_name, format: { with: HASHTAG_NAME_RE } From 35b9749b95fc8f61631a8b38f9dad37ec5f36a7a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 14 Nov 2023 17:53:15 +0100 Subject: [PATCH 28/63] Update dependency webpack-bundle-analyzer to v4.10.0 (#27852) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 58 +++++++++++++------------------------------------------ 1 file changed, 13 insertions(+), 45 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1cb5a42d5..addbe638f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6430,6 +6430,13 @@ __metadata: languageName: node linkType: hard +"debounce@npm:^1.2.1": + version: 1.2.1 + resolution: "debounce@npm:1.2.1" + checksum: 6c9320aa0973fc42050814621a7a8a78146c1975799b5b3cc1becf1f77ba9a5aa583987884230da0842a03f385def452fad5d60db97c3d1c8b824e38a8edf500 + languageName: node + linkType: hard + "debug@npm:2.6.9, debug@npm:^2.2.0, debug@npm:^2.3.3": version: 2.6.9 resolution: "debug@npm:2.6.9" @@ -8903,7 +8910,7 @@ __metadata: languageName: node linkType: hard -"html-escaper@npm:^2.0.0": +"html-escaper@npm:^2.0.0, html-escaper@npm:^2.0.2": version: 2.0.2 resolution: "html-escaper@npm:2.0.2" checksum: 208e8a12de1a6569edbb14544f4567e6ce8ecc30b9394fcaa4e7bb1e60c12a7c9a1ed27e31290817157e8626f3a4f29e76c8747030822eb84a6abb15c255f0a0 @@ -11032,20 +11039,6 @@ __metadata: languageName: node linkType: hard -"lodash.escape@npm:^4.0.1": - version: 4.0.1 - resolution: "lodash.escape@npm:4.0.1" - checksum: 90ade409cec05b6869090476952fdfb84d4d87b1ff4a0e03ebd590f980d9a1248d93ba14579f10d80c6429e4d6af13ba137c28db64cae6dadb71442e54a3ad2b - languageName: node - linkType: hard - -"lodash.flatten@npm:^4.4.0": - version: 4.4.0 - resolution: "lodash.flatten@npm:4.4.0" - checksum: 97e8f0d6b61fe4723c02ad0c6e67e51784c4a2c48f56ef283483e556ad01594cf9cec9c773e177bbbdbdb5d19e99b09d2487cb6b6e5dc405c2693e93b125bd3a - languageName: node - linkType: hard - "lodash.get@npm:^4.0": version: 4.4.2 resolution: "lodash.get@npm:4.4.2" @@ -11060,13 +11053,6 @@ __metadata: languageName: node linkType: hard -"lodash.invokemap@npm:^4.6.0": - version: 4.6.0 - resolution: "lodash.invokemap@npm:4.6.0" - checksum: 2bcc5f4b8782a316d55ff139215eb797f576f0f6d3db2755ebba7b35fd6061f8cbe81702a72a30bc6d70073a5dcc461f7570eaddcc9184c2e42ec3023645c6a1 - languageName: node - linkType: hard - "lodash.isarguments@npm:^3.1.0": version: 3.1.0 resolution: "lodash.isarguments@npm:3.1.0" @@ -11109,13 +11095,6 @@ __metadata: languageName: node linkType: hard -"lodash.pullall@npm:^4.2.0": - version: 4.2.0 - resolution: "lodash.pullall@npm:4.2.0" - checksum: b129e8d879258c7db04a7dc1c23dd9e37c52f63a04e105faa8d2ab55e97b5a170d5e15cffbb732a36e7f48c4345c07b6fbddfe50e1f5ec301492b6f64a92040c - languageName: node - linkType: hard - "lodash.sortby@npm:^4.7.0": version: 4.7.0 resolution: "lodash.sortby@npm:4.7.0" @@ -11137,13 +11116,6 @@ __metadata: languageName: node linkType: hard -"lodash.uniqby@npm:^4.7.0": - version: 4.7.0 - resolution: "lodash.uniqby@npm:4.7.0" - checksum: c505c0de20ca759599a2ba38710e8fb95ff2d2028e24d86c901ef2c74be8056518571b9b754bfb75053b2818d30dd02243e4a4621a6940c206bbb3f7626db656 - languageName: node - linkType: hard - "lodash@npm:^4.17.10, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.20, lodash@npm:^4.17.21": version: 4.17.21 resolution: "lodash@npm:4.17.21" @@ -17035,29 +17007,25 @@ __metadata: linkType: hard "webpack-bundle-analyzer@npm:^4.8.0": - version: 4.9.1 - resolution: "webpack-bundle-analyzer@npm:4.9.1" + version: 4.10.0 + resolution: "webpack-bundle-analyzer@npm:4.10.0" dependencies: "@discoveryjs/json-ext": "npm:0.5.7" acorn: "npm:^8.0.4" acorn-walk: "npm:^8.0.0" commander: "npm:^7.2.0" + debounce: "npm:^1.2.1" escape-string-regexp: "npm:^4.0.0" gzip-size: "npm:^6.0.0" + html-escaper: "npm:^2.0.2" is-plain-object: "npm:^5.0.0" - lodash.debounce: "npm:^4.0.8" - lodash.escape: "npm:^4.0.1" - lodash.flatten: "npm:^4.4.0" - lodash.invokemap: "npm:^4.6.0" - lodash.pullall: "npm:^4.2.0" - lodash.uniqby: "npm:^4.7.0" opener: "npm:^1.5.2" picocolors: "npm:^1.0.0" sirv: "npm:^2.0.3" ws: "npm:^7.3.1" bin: webpack-bundle-analyzer: lib/bin/analyzer.js - checksum: dd047c306471e6c389d6d4156ff22402e587140310a065a6191ee380f8251063f73a8ec6ac6d977c1cd634dbb717e2522b5d0b6cc9b0e847d4f15737fd9c65c9 + checksum: f812a8d3c0198ce518baf742bff656526f3eae69fb7a64c7f0c9cff202f6fb3380cabf3baaae965b8d6ffbbb6fb802eacb373fca03a596a38b01b84cfb2e8329 languageName: node linkType: hard From 36d7d1781f99c66c85cbbde75015b622124b1c3e Mon Sep 17 00:00:00 2001 From: Nick Schonning <nschonni@gmail.com> Date: Tue, 14 Nov 2023 11:53:38 -0500 Subject: [PATCH 29/63] Add CodeCov for Ruby coverage reports (#23868) --- .github/workflows/test-ruby.yml | 8 +++++++- Gemfile | 1 + Gemfile.lock | 2 ++ spec/spec_helper.rb | 8 ++++++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml index 07fd25fb1..101de66ac 100644 --- a/.github/workflows/test-ruby.yml +++ b/.github/workflows/test-ruby.yml @@ -94,7 +94,7 @@ jobs: DB_HOST: localhost DB_USER: postgres DB_PASS: postgres - DISABLE_SIMPLECOV: true + DISABLE_SIMPLECOV: ${{ matrix.ruby-version != '.ruby-version' }} RAILS_ENV: test ALLOW_NOPAM: true PAM_ENABLED: true @@ -137,6 +137,12 @@ jobs: - run: bin/rspec + - name: Upload coverage reports to Codecov + if: matrix.ruby-version == '.ruby-version' + uses: codecov/codecov-action@v3 + with: + files: coverage/lcov/mastodon.lcov + test-e2e: name: End to End testing runs-on: ubuntu-latest diff --git a/Gemfile b/Gemfile index 039e13675..add7b3606 100644 --- a/Gemfile +++ b/Gemfile @@ -139,6 +139,7 @@ group :test do # Coverage formatter for RSpec test if DISABLE_SIMPLECOV is false gem 'simplecov', '~> 0.22', require: false + gem 'simplecov-lcov', '~> 0.8', require: false # Stub web requests for specs gem 'webmock', '~> 3.18' diff --git a/Gemfile.lock b/Gemfile.lock index 84ad19b80..20c958e2e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -725,6 +725,7 @@ GEM simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) simplecov-html (0.12.3) + simplecov-lcov (0.8.0) simplecov_json_formatter (0.1.4) smart_properties (1.17.0) sprockets (3.7.2) @@ -936,6 +937,7 @@ DEPENDENCIES simple-navigation (~> 4.4) simple_form (~> 5.2) simplecov (~> 0.22) + simplecov-lcov (~> 0.8) sprockets (~> 3.7.2) sprockets-rails (~> 3.4) stackprof diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 7c97d8595..f5dcefc78 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,13 +2,21 @@ if ENV['DISABLE_SIMPLECOV'] != 'true' require 'simplecov' + require 'simplecov-lcov' + SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true + SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter SimpleCov.start 'rails' do + enable_coverage :branch + enable_coverage_for_eval + add_filter 'lib/linter' add_group 'Policies', 'app/policies' add_group 'Presenters', 'app/presenters' add_group 'Serializers', 'app/serializers' add_group 'Services', 'app/services' add_group 'Validators', 'app/validators' + + add_group 'Libraries', 'lib' end end From 15b2d7eec59c745b418debf63907d8bd08c4a730 Mon Sep 17 00:00:00 2001 From: Emelia Smith <ThisIsMissEm@users.noreply.github.com> Date: Tue, 14 Nov 2023 18:43:20 +0100 Subject: [PATCH 30/63] Split streaming server from web server (#24702) --- Procfile.dev | 2 +- package.json | 27 ++-------- streaming/index.js | 6 ++- streaming/package.json | 39 +++++++++++++++ yarn.lock | 109 ++++++++++++++++++++++------------------- 5 files changed, 108 insertions(+), 75 deletions(-) create mode 100644 streaming/package.json diff --git a/Procfile.dev b/Procfile.dev index fbb2c2de2..f81333b04 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,4 +1,4 @@ web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq -stream: env PORT=4000 yarn run start +stream: env PORT=4000 yarn workspace @mastodon/streaming start webpack: bin/webpack-dev-server diff --git a/package.json b/package.json index 7c73d17c1..bcd91e3fc 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,13 @@ { "name": "@mastodon/mastodon", "license": "AGPL-3.0-or-later", + "packageManager": "yarn@4.0.2", "engines": { "node": ">=18" }, "workspaces": [ - "." + ".", + "streaming" ], "scripts": { "build:development": "cross-env RAILS_ENV=development NODE_ENV=development ./bin/webpack", @@ -71,10 +73,8 @@ "css-loader": "^5.2.7", "cssnano": "^6.0.1", "detect-passive-events": "^2.0.3", - "dotenv": "^16.0.3", "emoji-mart": "npm:emoji-mart-lazyload@latest", "escape-html": "^1.0.3", - "express": "^4.18.2", "file-loader": "^6.2.0", "font-awesome": "^4.7.0", "fuzzysort": "^2.0.4", @@ -85,21 +85,15 @@ "immutable": "^4.3.0", "imports-loader": "^1.2.0", "intl-messageformat": "^10.3.5", - "ioredis": "^5.3.2", "js-yaml": "^4.1.0", - "jsdom": "^22.1.0", "lodash": "^4.17.21", "mark-loader": "^0.1.6", "marky": "^1.2.5", "mini-css-extract-plugin": "^1.6.2", "mkdirp": "^3.0.1", - "npmlog": "^7.0.1", "path-complete-extname": "^1.0.0", - "pg": "^8.5.0", - "pg-connection-string": "^2.6.0", "postcss": "^8.4.24", "postcss-loader": "^4.3.0", - "prom-client": "^15.0.0", "prop-types": "^15.8.1", "punycode": "^2.3.0", "react": "^18.2.0", @@ -138,7 +132,6 @@ "tesseract.js": "^2.1.5", "tiny-queue": "^0.2.1", "twitter-text": "3.1.0", - "uuid": "^9.0.0", "webpack": "^4.47.0", "webpack-assets-manifest": "^4.0.6", "webpack-bundle-analyzer": "^4.8.0", @@ -150,8 +143,7 @@ "workbox-routing": "^7.0.0", "workbox-strategies": "^7.0.0", "workbox-webpack-plugin": "^7.0.0", - "workbox-window": "^7.0.0", - "ws": "^8.12.1" + "workbox-window": "^7.0.0" }, "devDependencies": { "@formatjs/cli": "^6.1.1", @@ -160,16 +152,13 @@ "@types/babel__core": "^7.20.1", "@types/emoji-mart": "^3.0.9", "@types/escape-html": "^1.0.2", - "@types/express": "^4.17.17", "@types/hoist-non-react-statics": "^3.3.1", "@types/http-link-header": "^1.0.3", "@types/intl": "^1.2.0", "@types/jest": "^29.5.2", "@types/js-yaml": "^4.0.5", "@types/lodash": "^4.14.195", - "@types/npmlog": "^4.1.4", "@types/object-assign": "^4.0.30", - "@types/pg": "^8.6.6", "@types/prop-types": "^15.7.5", "@types/punycode": "^2.1.0", "@types/react": "^18.2.7", @@ -188,7 +177,6 @@ "@types/react-toggle": "^4.0.3", "@types/redux-immutable": "^4.0.3", "@types/requestidlecallback": "^0.3.5", - "@types/uuid": "^9.0.0", "@types/webpack": "^4.41.33", "@types/yargs": "^17.0.24", "@typescript-eslint/eslint-plugin": "^6.0.0", @@ -232,15 +220,10 @@ "optional": true } }, - "optionalDependencies": { - "bufferutil": "^4.0.7", - "utf-8-validate": "^6.0.3" - }, "lint-staged": { "*": "prettier --ignore-unknown --write", "Capfile|Gemfile|*.{rb,ruby,ru,rake}": "bundle exec rubocop --force-exclusion -a", "*.{js,jsx,ts,tsx}": "eslint --fix", "*.{css,scss}": "stylelint --fix" - }, - "packageManager": "yarn@4.0.2" + } } diff --git a/streaming/index.js b/streaming/index.js index b3765531c..e3b63b53a 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -2,6 +2,7 @@ const fs = require('fs'); const http = require('http'); +const path = require('path'); const url = require('url'); const dotenv = require('dotenv'); @@ -17,8 +18,11 @@ const WebSocket = require('ws'); const environment = process.env.NODE_ENV || 'development'; +// Correctly detect and load .env or .env.production file based on environment: +const dotenvFile = environment === 'production' ? '.env.production' : '.env'; + dotenv.config({ - path: environment === 'production' ? '.env.production' : '.env', + path: path.resolve(__dirname, path.join('..', dotenvFile)) }); log.level = process.env.LOG_LEVEL || 'verbose'; diff --git a/streaming/package.json b/streaming/package.json new file mode 100644 index 000000000..d3f214432 --- /dev/null +++ b/streaming/package.json @@ -0,0 +1,39 @@ +{ + "name": "@mastodon/streaming", + "license": "AGPL-3.0-or-later", + "packageManager": "yarn@4.0.1", + "engines": { + "node": ">=18" + }, + "description": "Mastodon's Streaming Server", + "private": true, + "repository": { + "type": "git", + "url": "https://github.com/mastodon/mastodon.git" + }, + "scripts": { + "start": "node ./index.js" + }, + "dependencies": { + "dotenv": "^16.0.3", + "express": "^4.18.2", + "ioredis": "^5.3.2", + "jsdom": "^22.1.0", + "npmlog": "^7.0.1", + "pg": "^8.5.0", + "pg-connection-string": "^2.6.0", + "prom-client": "^15.0.0", + "uuid": "^9.0.0", + "ws": "^8.12.1" + }, + "devDependencies": { + "@types/express": "^4.17.17", + "@types/npmlog": "^4.1.4", + "@types/pg": "^8.6.6", + "@types/uuid": "^9.0.0" + }, + "optionalDependencies": { + "bufferutil": "^4.0.7", + "utf-8-validate": "^6.0.3" + } +} diff --git a/yarn.lock b/yarn.lock index addbe638f..c88dd49d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2368,16 +2368,13 @@ __metadata: "@types/babel__core": "npm:^7.20.1" "@types/emoji-mart": "npm:^3.0.9" "@types/escape-html": "npm:^1.0.2" - "@types/express": "npm:^4.17.17" "@types/hoist-non-react-statics": "npm:^3.3.1" "@types/http-link-header": "npm:^1.0.3" "@types/intl": "npm:^1.2.0" "@types/jest": "npm:^29.5.2" "@types/js-yaml": "npm:^4.0.5" "@types/lodash": "npm:^4.14.195" - "@types/npmlog": "npm:^4.1.4" "@types/object-assign": "npm:^4.0.30" - "@types/pg": "npm:^8.6.6" "@types/prop-types": "npm:^15.7.5" "@types/punycode": "npm:^2.1.0" "@types/react": "npm:^18.2.7" @@ -2396,7 +2393,6 @@ __metadata: "@types/react-toggle": "npm:^4.0.3" "@types/redux-immutable": "npm:^4.0.3" "@types/requestidlecallback": "npm:^0.3.5" - "@types/uuid": "npm:^9.0.0" "@types/webpack": "npm:^4.41.33" "@types/yargs": "npm:^17.0.24" "@typescript-eslint/eslint-plugin": "npm:^6.0.0" @@ -2412,7 +2408,6 @@ __metadata: babel-plugin-preval: "npm:^5.1.0" babel-plugin-transform-react-remove-prop-types: "npm:^0.4.24" blurhash: "npm:^2.0.5" - bufferutil: "npm:^4.0.7" circular-dependency-plugin: "npm:^5.2.2" classnames: "npm:^2.3.2" cocoon-js-vanilla: "npm:^1.3.0" @@ -2423,7 +2418,6 @@ __metadata: css-loader: "npm:^5.2.7" cssnano: "npm:^6.0.1" detect-passive-events: "npm:^2.0.3" - dotenv: "npm:^16.0.3" emoji-mart: "npm:emoji-mart-lazyload@latest" escape-html: "npm:^1.0.3" eslint: "npm:^8.41.0" @@ -2437,7 +2431,6 @@ __metadata: eslint-plugin-promise: "npm:~6.1.1" eslint-plugin-react: "npm:~7.33.0" eslint-plugin-react-hooks: "npm:^4.6.0" - express: "npm:^4.18.2" file-loader: "npm:^6.2.0" font-awesome: "npm:^4.7.0" fuzzysort: "npm:^2.0.4" @@ -2449,25 +2442,19 @@ __metadata: immutable: "npm:^4.3.0" imports-loader: "npm:^1.2.0" intl-messageformat: "npm:^10.3.5" - ioredis: "npm:^5.3.2" jest: "npm:^29.5.0" jest-environment-jsdom: "npm:^29.5.0" js-yaml: "npm:^4.1.0" - jsdom: "npm:^22.1.0" lint-staged: "npm:^15.0.0" lodash: "npm:^4.17.21" mark-loader: "npm:^0.1.6" marky: "npm:^1.2.5" mini-css-extract-plugin: "npm:^1.6.2" mkdirp: "npm:^3.0.1" - npmlog: "npm:^7.0.1" path-complete-extname: "npm:^1.0.0" - pg: "npm:^8.5.0" - pg-connection-string: "npm:^2.6.0" postcss: "npm:^8.4.24" postcss-loader: "npm:^4.3.0" prettier: "npm:^3.0.0" - prom-client: "npm:^15.0.0" prop-types: "npm:^15.8.1" punycode: "npm:^2.3.0" react: "npm:^18.2.0" @@ -2510,8 +2497,6 @@ __metadata: tiny-queue: "npm:^0.2.1" twitter-text: "npm:3.1.0" typescript: "npm:^5.0.4" - utf-8-validate: "npm:^6.0.3" - uuid: "npm:^9.0.0" webpack: "npm:^4.47.0" webpack-assets-manifest: "npm:^4.0.6" webpack-bundle-analyzer: "npm:^4.8.0" @@ -2525,13 +2510,7 @@ __metadata: workbox-strategies: "npm:^7.0.0" workbox-webpack-plugin: "npm:^7.0.0" workbox-window: "npm:^7.0.0" - ws: "npm:^8.12.1" yargs: "npm:^17.7.2" - dependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true peerDependenciesMeta: react: optional: true @@ -2542,6 +2521,34 @@ __metadata: languageName: unknown linkType: soft +"@mastodon/streaming@workspace:streaming": + version: 0.0.0-use.local + resolution: "@mastodon/streaming@workspace:streaming" + dependencies: + "@types/express": "npm:^4.17.17" + "@types/npmlog": "npm:^4.1.4" + "@types/pg": "npm:^8.6.6" + "@types/uuid": "npm:^9.0.0" + bufferutil: "npm:^4.0.7" + dotenv: "npm:^16.0.3" + express: "npm:^4.18.2" + ioredis: "npm:^5.3.2" + jsdom: "npm:^22.1.0" + npmlog: "npm:^7.0.1" + pg: "npm:^8.5.0" + pg-connection-string: "npm:^2.6.0" + prom-client: "npm:^15.0.0" + utf-8-validate: "npm:^6.0.3" + uuid: "npm:^9.0.0" + ws: "npm:^8.12.1" + dependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + languageName: unknown + linkType: soft + "@material-symbols/svg-600@npm:^0.14.0": version: 0.14.0 resolution: "@material-symbols/svg-600@npm:0.14.0" @@ -3056,21 +3063,21 @@ __metadata: linkType: hard "@types/body-parser@npm:*": - version: 1.19.4 - resolution: "@types/body-parser@npm:1.19.4" + version: 1.19.5 + resolution: "@types/body-parser@npm:1.19.5" dependencies: "@types/connect": "npm:*" "@types/node": "npm:*" - checksum: bec2b8a97861a960ee415f7ab3c2aeb7f4d779fd364d27ddee46057897ea571735f1f854f5ee41682964315d4e3699f62427998b9c21851d773398ef535f0612 + checksum: aebeb200f25e8818d8cf39cd0209026750d77c9b85381cdd8deeb50913e4d18a1ebe4b74ca9b0b4d21952511eeaba5e9fbbf739b52731a2061e206ec60d568df languageName: node linkType: hard "@types/connect@npm:*": - version: 3.4.37 - resolution: "@types/connect@npm:3.4.37" + version: 3.4.38 + resolution: "@types/connect@npm:3.4.38" dependencies: "@types/node": "npm:*" - checksum: 79fd5c32a8bb5c9548369e6da3221b6a820f3a8c5396d50f6f642712b9f4c1c881ef86bdf48994a4a279e81998563410b8843c5a10dde5521d5ef6a8ae944c3b + checksum: 2e1cdba2c410f25649e77856505cd60223250fa12dff7a503e492208dbfdd25f62859918f28aba95315251fd1f5e1ffbfca1e25e73037189ab85dd3f8d0a148c languageName: node linkType: hard @@ -3115,14 +3122,14 @@ __metadata: linkType: hard "@types/express-serve-static-core@npm:^4.17.33": - version: 4.17.39 - resolution: "@types/express-serve-static-core@npm:4.17.39" + version: 4.17.41 + resolution: "@types/express-serve-static-core@npm:4.17.41" dependencies: "@types/node": "npm:*" "@types/qs": "npm:*" "@types/range-parser": "npm:*" "@types/send": "npm:*" - checksum: b23b005fddd2ba3f7142ec9713f06b5582c7712cdf99c3419d3972364903b348a103c3264d9a761d6497140e3b89bd416454684c4bdeff206b4c59b86e96428a + checksum: dc166cbf4475c00a81fbcab120bf7477c527184be11ae149df7f26d9c1082114c68f8d387a2926fe80291b06477c8bbd9231ff4f5775de328e887695aefce269 languageName: node linkType: hard @@ -3175,9 +3182,9 @@ __metadata: linkType: hard "@types/http-errors@npm:*": - version: 2.0.3 - resolution: "@types/http-errors@npm:2.0.3" - checksum: 717ce3e8f49a1facb7130fed934108fa8a51ab02089a1049c782e353e0e08e79bdfaac054c2a94db14ea400302e523276387363aa820eaf0031af8ba5d2941dc + version: 2.0.4 + resolution: "@types/http-errors@npm:2.0.4" + checksum: 494670a57ad4062fee6c575047ad5782506dd35a6b9ed3894cea65830a94367bd84ba302eb3dde331871f6d70ca287bfedb1b2cf658e6132cd2cbd427ab56836 languageName: node linkType: hard @@ -3279,16 +3286,16 @@ __metadata: linkType: hard "@types/mime@npm:*": - version: 3.0.3 - resolution: "@types/mime@npm:3.0.3" - checksum: cef99f8cdc42af9de698027c2a20ba5df12bc9a89dcf5513e70103ebb55e00c5f5c585d02411f4b42fde0e78488342f1b1d3e3546a59a3da42e95fdc616e01eb + version: 3.0.4 + resolution: "@types/mime@npm:3.0.4" + checksum: db478bc0f99e40f7b3e01d356a9bdf7817060808a294978111340317bcd80ca35382855578c5b60fbc84ae449674bd9bb38427b18417e1f8f19e4f72f8b242cd languageName: node linkType: hard "@types/mime@npm:^1": - version: 1.3.4 - resolution: "@types/mime@npm:1.3.4" - checksum: a0a16d26c0e70a1b133e26e7c46b70b3136b7e894396bdb7de1c642f4ac87fdbbba26bf56cf73f001312289d89de4f1c06ab745d9445850df45a5a802564c4d6 + version: 1.3.5 + resolution: "@types/mime@npm:1.3.5" + checksum: c2ee31cd9b993804df33a694d5aa3fa536511a49f2e06eeab0b484fef59b4483777dbb9e42a4198a0809ffbf698081fdbca1e5c2218b82b91603dfab10a10fbc languageName: node linkType: hard @@ -3392,16 +3399,16 @@ __metadata: linkType: hard "@types/qs@npm:*": - version: 6.9.9 - resolution: "@types/qs@npm:6.9.9" - checksum: aede2a4181a49ae8548a1354bac3f8235cb0c5aab066b10875a3e68e88a199e220f4284e7e2bb75a3c18e5d4ff6abe1a6ce0389ef31b63952cc45e0f4d885ba0 + version: 6.9.10 + resolution: "@types/qs@npm:6.9.10" + checksum: 6be12e5f062d1b41eb037d59bf9cb65bc9410cedd5e6da832dfd7c8e2b3f4c91e81c9b90b51811140770e5052c6c4e8361181bd9437ddcd4515dc128b7c00353 languageName: node linkType: hard "@types/range-parser@npm:*": - version: 1.2.6 - resolution: "@types/range-parser@npm:1.2.6" - checksum: 46e7fffc54cdacc8fb0cd576f8f9a6436453f0176205d6ec55434a460c7677e78e688673426d5db5e480501b2943ba08a16ececa3a354c222093551c7217fb8f + version: 1.2.7 + resolution: "@types/range-parser@npm:1.2.7" + checksum: 361bb3e964ec5133fa40644a0b942279ed5df1949f21321d77de79f48b728d39253e5ce0408c9c17e4e0fd95ca7899da36841686393b9f7a1e209916e9381a3c languageName: node linkType: hard @@ -3587,23 +3594,23 @@ __metadata: linkType: hard "@types/send@npm:*": - version: 0.17.3 - resolution: "@types/send@npm:0.17.3" + version: 0.17.4 + resolution: "@types/send@npm:0.17.4" dependencies: "@types/mime": "npm:^1" "@types/node": "npm:*" - checksum: 773a0cb55ea03eefbe9a0e6d42114e0f84968db30954a131aae9ba7e9ab984a4776915447ebdeab4412d7f11750126614b0b75e99413f75810045bdb3196554a + checksum: 7f17fa696cb83be0a104b04b424fdedc7eaba1c9a34b06027239aba513b398a0e2b7279778af521f516a397ced417c96960e5f50fcfce40c4bc4509fb1a5883c languageName: node linkType: hard "@types/serve-static@npm:*": - version: 1.15.4 - resolution: "@types/serve-static@npm:1.15.4" + version: 1.15.5 + resolution: "@types/serve-static@npm:1.15.5" dependencies: "@types/http-errors": "npm:*" "@types/mime": "npm:*" "@types/node": "npm:*" - checksum: 061b38993bf8f2b5033f57147c8ec90e1d1a0d6f734958ceb531ba7cc31192fd272c999cdbc57ede8672787e3aa171ec142dc65a467c04078e43823e7476eb49 + checksum: 811d1a2f7e74a872195e7a013bcd87a2fb1edf07eaedcb9dcfd20c1eb4bc56ad4ea0d52141c13192c91ccda7c8aeb8a530d8a7e60b9c27f5990d7e62e0fecb03 languageName: node linkType: hard From 998f0684994b9be5ccad986b83308039ff395ef6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 14 Nov 2023 18:52:34 +0100 Subject: [PATCH 31/63] Update Yarn to v4.0.2 (#27857) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- streaming/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/streaming/package.json b/streaming/package.json index d3f214432..a474e6226 100644 --- a/streaming/package.json +++ b/streaming/package.json @@ -1,7 +1,7 @@ { "name": "@mastodon/streaming", "license": "AGPL-3.0-or-later", - "packageManager": "yarn@4.0.1", + "packageManager": "yarn@4.0.2", "engines": { "node": ">=18" }, From 7c72944661c100118555045a87ced7d1fa7cc417 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Wed, 15 Nov 2023 04:11:02 -0500 Subject: [PATCH 32/63] Use `Lcov` simplecov formatter on CI and `HTML` elsewhere (#27859) --- spec/spec_helper.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index f5dcefc78..0bb4f88cf 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,9 +2,14 @@ if ENV['DISABLE_SIMPLECOV'] != 'true' require 'simplecov' - require 'simplecov-lcov' - SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true - SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter + + if ENV['CI'] + require 'simplecov-lcov' + SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true + SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter + else + SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter + end SimpleCov.start 'rails' do enable_coverage :branch enable_coverage_for_eval From b6f29106eab8c5deeb1c22051387292108bbb3c8 Mon Sep 17 00:00:00 2001 From: Renaud Chaput <renchap@gmail.com> Date: Wed, 15 Nov 2023 10:20:24 +0100 Subject: [PATCH 33/63] Improve codecov config (#27860) --- .github/codecov.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/codecov.yml diff --git a/.github/codecov.yml b/.github/codecov.yml new file mode 100644 index 000000000..5532c4961 --- /dev/null +++ b/.github/codecov.yml @@ -0,0 +1,13 @@ +coverage: + status: + project: + default: + # Github status check is not blocking + informational: true + patch: + default: + # Github status check is not blocking + informational: true +comment: + # Only write a comment in PR if there are changes + require_changes: true From 5d75799afaacb5b530d9abdb7464db779d3fcbe0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 15 Nov 2023 09:21:25 +0000 Subject: [PATCH 34/63] Update dependency axios to v1.6.2 (#27861) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index c88dd49d0..e66f58f8b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4690,13 +4690,13 @@ __metadata: linkType: hard "axios@npm:^1.4.0": - version: 1.6.1 - resolution: "axios@npm:1.6.1" + version: 1.6.2 + resolution: "axios@npm:1.6.2" dependencies: follow-redirects: "npm:^1.15.0" form-data: "npm:^4.0.0" proxy-from-env: "npm:^1.1.0" - checksum: ca2c6f56659a7f19e4a99082f549fe151952f6fd8aa72ed148559ab2d6a32ce37cd5dc72ce6d4d3cd91f0c1e2617c7c95c20077e5e244a79f319a6c0ce41204f + checksum: 9b77e030e85e4f9cbcba7bb52fbff67d6ce906c92d213e0bd932346a50140faf83733bf786f55bd58301bd92f9973885c7b87d6348023e10f7eaf286d0791a1d languageName: node linkType: hard From 922f086253c8bfcead9df895f4624580d5b61a9c Mon Sep 17 00:00:00 2001 From: Jeong Arm <kjwonmail@gmail.com> Date: Wed, 15 Nov 2023 18:29:10 +0900 Subject: [PATCH 35/63] Fix open status on media modal (#27867) --- .../mastodon/features/picture_in_picture/components/footer.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/picture_in_picture/components/footer.jsx b/app/javascript/mastodon/features/picture_in_picture/components/footer.jsx index a4ea989fb..9b26e2d75 100644 --- a/app/javascript/mastodon/features/picture_in_picture/components/footer.jsx +++ b/app/javascript/mastodon/features/picture_in_picture/components/footer.jsx @@ -166,7 +166,7 @@ class Footer extends ImmutablePureComponent { onClose(); } - history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`); + this.props.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`); }; render () { From d8074128f9f5f84bd9d5adc416ef67ae922f0289 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 Nov 2023 10:41:24 +0100 Subject: [PATCH 36/63] New Crowdin Translations (automated) (#27866) Co-authored-by: GitHub Actions <noreply@github.com> --- app/javascript/mastodon/locales/cs.json | 1 + app/javascript/mastodon/locales/eu.json | 2 +- app/javascript/mastodon/locales/it.json | 2 +- app/javascript/mastodon/locales/lt.json | 60 +++++++++++--- app/javascript/mastodon/locales/zh-TW.json | 36 ++++----- config/locales/activerecord.lt.yml | 45 +++++++++++ config/locales/be.yml | 6 +- config/locales/cs.yml | 7 ++ config/locales/devise.zh-TW.yml | 10 +-- config/locales/doorkeeper.zh-TW.yml | 2 +- config/locales/pl.yml | 4 +- config/locales/simple_form.zh-TW.yml | 50 ++++++------ config/locales/vi.yml | 1 + config/locales/zh-TW.yml | 92 +++++++++++----------- 14 files changed, 207 insertions(+), 111 deletions(-) diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json index 33c7c31d9..aaf50d67c 100644 --- a/app/javascript/mastodon/locales/cs.json +++ b/app/javascript/mastodon/locales/cs.json @@ -222,6 +222,7 @@ "emoji_button.search_results": "Výsledky hledání", "emoji_button.symbols": "Symboly", "emoji_button.travel": "Cestování a místa", + "empty_column.account_hides_collections": "Tento uživatel se rozhodl nezveřejňovat tuto informaci", "empty_column.account_suspended": "Účet je pozastaven", "empty_column.account_timeline": "Nejsou tu žádné příspěvky!", "empty_column.account_unavailable": "Profil není dostupný", diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json index 7f109ea08..419589aba 100644 --- a/app/javascript/mastodon/locales/eu.json +++ b/app/javascript/mastodon/locales/eu.json @@ -389,7 +389,7 @@ "lists.replies_policy.title": "Erakutsi erantzunak:", "lists.search": "Bilatu jarraitzen dituzun pertsonen artean", "lists.subheading": "Zure zerrendak", - "load_pending": "{count, plural, one {eleentuberri #} other {# elementu berri}}", + "load_pending": "{count, plural, one {elementu berri #} other {# elementu berri}}", "loading_indicator.label": "Kargatzen...", "media_gallery.toggle_visible": "Txandakatu ikusgaitasuna", "moved_to_account_banner.text": "Zure {disabledAccount} kontua desgaituta dago une honetan, {movedToAccount} kontura aldatu zinelako.", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 4a2f41ce6..8ad791bfe 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -252,7 +252,7 @@ "explore.search_results": "Risultati della ricerca", "explore.suggested_follows": "Persone", "explore.title": "Esplora", - "explore.trending_links": "Novità", + "explore.trending_links": "Notizie", "explore.trending_statuses": "Post", "explore.trending_tags": "Hashtag", "filter_modal.added.context_mismatch_explanation": "La categoria di questo filtro non si applica al contesto in cui hai acceduto a questo post. Se desideri che il post sia filtrato anche in questo contesto, dovrai modificare il filtro.", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index 5cdc575de..5675799e7 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -1,7 +1,7 @@ { "about.blocks": "Prižiūrimi serveriai", "about.contact": "Kontaktuoti:", - "about.disclaimer": "Mastodon – nemokama atvirojo šaltinio programa ir Mastodon gGmbH prekės ženklas.", + "about.disclaimer": "Mastodon – nemokama atvirojo kodo programa ir Mastodon gGmbH prekės ženklas.", "about.domain_blocks.no_reason_available": "Priežastis nežinoma", "about.domain_blocks.preamble": "Mastodon paprastai leidžia peržiūrėti turinį ir bendrauti su naudotojais iš bet kurio kito fediverse esančio serverio. Šios yra išimtys, kurios buvo padarytos šiame konkrečiame serveryje.", "about.domain_blocks.silenced.explanation": "Paprastai nematysi profilių ir turinio iš šio serverio, nebent jį aiškiai ieškosi arba pasirinksi jį sekdamas (-a).", @@ -33,28 +33,46 @@ "account.followers.empty": "Šio naudotojo dar niekas neseka.", "account.followers_counter": "{count, plural, one {{counter} sekėjas (-a)} few {{counter} sekėjai} many {{counter} sekėjo} other {{counter} sekėjų}}", "account.following": "Seka", - "account.follows.empty": "Šis naudotojas (-a) dar nieko neseka.", + "account.follows.empty": "Šis (-i) naudotojas (-a) dar nieko neseka.", "account.follows_you": "Seka tave", "account.go_to_profile": "Eiti į profilį", "account.in_memoriam": "Atminimui.", "account.joined_short": "Prisijungė", "account.languages": "Keisti prenumeruojamas kalbas", + "account.link_verified_on": "Šios nuorodos nuosavybė buvo patikrinta {date}", "account.locked_info": "Šios paskyros privatumo būsena nustatyta kaip užrakinta. Savininkas (-ė) rankiniu būdu peržiūri, kas gali sekti.", "account.media": "Medija", "account.mention": "Paminėti @{name}", "account.moved_to": "{name} nurodė, kad dabar jų nauja paskyra yra:", "account.mute": "Užtildyti @{name}", + "account.mute_notifications_short": "Nutildyti pranešimus", + "account.mute_short": "Nutildyti", "account.muted": "Užtildytas", - "account.posts": "Toots", - "account.posts_with_replies": "Toots and replies", - "account.report": "Pranešti apie @{name}", - "account.requested": "Awaiting approval", + "account.no_bio": "Nėra pateikto aprašymo.", + "account.open_original_page": "Atidaryti originalinį tinklalapį", + "account.posts": "Įrašai", + "account.posts_with_replies": "Įrašai ir atsakymai", + "account.report": "Pranešti @{name}", + "account.requested": "Laukiama patvirtinimo. Spausk, kad atšaukti sekimo užklausą.", + "account.requested_follow": "{name} paprašė tave sekti", + "account.share": "Bendrinti @{name} profilį", "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", "account.unblock_domain": "Unhide {domain}", "account.unblock_short": "Atblokuoti", "account.unfollow": "Nebesekti", + "account.unmute": "Atitildyti @{name}", + "account.unmute_notifications_short": "Atitildyti pranešimus", "account.unmute_short": "Atitildyti", - "account_note.placeholder": "Click to add a note", + "account_note.placeholder": "Spausk norėdamas (-a) pridėti pastabą", + "admin.dashboard.retention.average": "Vidurkis", + "admin.dashboard.retention.cohort": "Registravimo mėnuo", + "admin.dashboard.retention.cohort_size": "Nauji naudotojai", + "admin.impact_report.instance_accounts": "Paskyrų profiliai, kuriuos tai ištrintų", + "admin.impact_report.instance_followers": "Sekėjai, kuriuos prarastų mūsų naudotojai", + "admin.impact_report.instance_follows": "Sekėjai, kuriuos prarastų jų naudotojai", + "admin.impact_report.title": "Poveikio apibendrinimas", + "alert.rate_limited.message": "Pabandyk vėliau po {retry_time, time, medium}.", + "alert.rate_limited.title": "Spartos ribojimas", "alert.unexpected.message": "Įvyko netikėta klaida.", "alert.unexpected.title": "Ups!", "announcement.announcement": "Skelbimas", @@ -65,6 +83,14 @@ "bundle_column_error.copy_stacktrace": "Kopijuoti klaidos ataskaitą", "bundle_column_error.error.body": "Užklausos puslapio nepavyko atvaizduoti. Tai gali būti dėl mūsų kodo klaidos arba naršyklės suderinamumo problemos.", "bundle_column_error.error.title": "O, ne!", + "bundle_column_error.network.body": "Bandant užkrauti šį puslapį įvyko klaida. Tai galėjo atsitikti dėl laikinos tavo interneto ryšio arba šio serverio problemos.", + "bundle_column_error.network.title": "Tinklo klaida", + "bundle_column_error.retry": "Bandyti dar kartą", + "bundle_column_error.return": "Grįžti į pradžią", + "bundle_column_error.routing.body": "Prašyto puslapio nepavyko rasti. Ar esi tikras (-a), kad adreso juostoje nurodytas URL adresas yra teisingas?", + "bundle_column_error.routing.title": "404", + "bundle_modal_error.close": "Uždaryti", + "closed_registrations_modal.find_another_server": "Rasti kitą serverį", "column.domain_blocks": "Hidden domains", "column.lists": "Sąrašai", "column.mutes": "Užtildyti vartotojai", @@ -81,18 +107,32 @@ "compose.published.body": "Įrašas paskelbtas.", "compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.", "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.", - "compose_form.placeholder": "What is on your mind?", + "compose_form.placeholder": "Kas tavo mintyse?", + "compose_form.poll.add_option": "Pridėti pasirinkimą", + "compose_form.poll.duration": "Apklausos trukmė", + "compose_form.poll.option_placeholder": "Pasirinkimas {number}", + "compose_form.poll.remove_option": "Pašalinti šį pasirinkimą", + "compose_form.poll.switch_to_multiple": "Keisti apklausą, kad būtų galima pasirinkti kelis pasirinkimus", "compose_form.publish_form": "Publish", "compose_form.sensitive.hide": "{count, plural, one {Žymėti mediją kaip jautrią} few {Žymėti medijas kaip jautrias} many {Žymėti medijos kaip jautrios} other {Žymėti medijų kaip jautrių}}", "compose_form.sensitive.marked": "{count, plural, one {Medija pažymėta kaip jautri} few {Medijos pažymėtos kaip jautrios} many {Medijos pažymėta kaip jautrios} other {Medijų pažymėtos kaip jautrios}}", "compose_form.sensitive.unmarked": "{count, plural, one {Medija nepažymėta kaip jautri} few {Medijos nepažymėtos kaip jautrios} many {Medijos nepažymėta kaip jautri} other {Medijų nepažymėta kaip jautrios}}", "compose_form.spoiler.marked": "Text is hidden behind warning", - "compose_form.spoiler.unmarked": "Text is not hidden", + "compose_form.spoiler.unmarked": "Pridėti turinio įspėjimą", + "compose_form.spoiler_placeholder": "Rašyk savo įspėjimą čia", + "confirmation_modal.cancel": "Atšaukti", + "confirmations.block.block_and_report": "Blokuoti ir pranešti", + "confirmations.block.confirm": "Blokuoti", + "confirmations.block.message": "Ar tikrai nori užblokuoti {name}?", "confirmations.delete.confirm": "Ištrinti", "confirmations.delete.message": "Are you sure you want to delete this status?", "confirmations.discard_edit_media.confirm": "Atmesti", "confirmations.discard_edit_media.message": "Turi neišsaugotų medijos aprašymo ar peržiūros pakeitimų, vis tiek juos atmesti?", "confirmations.domain_block.confirm": "Hide entire domain", + "confirmations.logout.confirm": "Atsijungti", + "confirmations.logout.message": "Ar tikrai nori atsijungti?", + "confirmations.mute.confirm": "Nutildyti", + "confirmations.mute.explanation": "Tai paslėps jų įrašus ir įrašus, kuriuose jie menėmi, tačiau jie vis tiek galės matyti tavo įrašus ir sekti.", "confirmations.reply.confirm": "Atsakyti", "confirmations.reply.message": "Atsakydamas (-a) dabar perrašysi šiuo metu rašomą žinutę. Ar tikrai nori tęsti?", "confirmations.unfollow.confirm": "Nebesekti", @@ -219,6 +259,8 @@ "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {# days}}", "upload_form.audio_description": "Describe for people with hearing loss", "upload_form.description": "Describe for the visually impaired", + "upload_form.description_missing": "Nėra pridėto aprašymo", + "upload_form.edit": "Redaguoti", "upload_form.video_description": "Describe for people with hearing loss or visual impairment", "upload_modal.edit_media": "Redaguoti mediją", "upload_progress.label": "Uploading…" diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index db4fe4eab..974096d2f 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -12,7 +12,7 @@ "about.powered_by": "由 {mastodon} 提供的去中心化社群媒體", "about.rules": "伺服器規則", "account.account_note_header": "備註", - "account.add_or_remove_from_list": "從列表中新增或移除", + "account.add_or_remove_from_list": "自列表中新增或移除", "account.badges.bot": "機器人", "account.badges.group": "群組", "account.block": "封鎖 @{name}", @@ -26,7 +26,7 @@ "account.domain_blocked": "已封鎖網域", "account.edit_profile": "編輯個人檔案", "account.enable_notifications": "當 @{name} 嘟文時通知我", - "account.endorse": "在個人檔案推薦對方", + "account.endorse": "於個人檔案推薦對方", "account.featured_tags.last_status_at": "上次嘟文於 {date}", "account.featured_tags.last_status_never": "沒有嘟文", "account.featured_tags.title": "{name} 的推薦主題標籤", @@ -65,7 +65,7 @@ "account.unblock": "解除封鎖 @{name}", "account.unblock_domain": "解除封鎖網域 {domain}", "account.unblock_short": "解除封鎖", - "account.unendorse": "取消在個人檔案推薦對方", + "account.unendorse": "取消於個人檔案推薦對方", "account.unfollow": "取消跟隨", "account.unmute": "解除靜音 @{name}", "account.unmute_notifications_short": "取消靜音推播通知", @@ -102,7 +102,7 @@ "bundle_modal_error.message": "載入此元件時發生錯誤。", "bundle_modal_error.retry": "重試", "closed_registrations.other_server_instructions": "因為 Mastodon 是去中心化的,所以您也能於其他伺服器上建立帳號,並仍然與這個伺服器互動。", - "closed_registrations_modal.description": "目前無法在 {domain} 建立新帳號,但也請別忘了,您並不一定需要有 {domain} 伺服器的帳號,也能使用 Mastodon 。", + "closed_registrations_modal.description": "目前無法於 {domain} 建立新帳號,但也請別忘了,您並不一定需要有 {domain} 伺服器的帳號,也能使用 Mastodon 。", "closed_registrations_modal.find_another_server": "尋找另一個伺服器", "closed_registrations_modal.preamble": "Mastodon 是去中心化的,所以無論您於哪個伺服器新增帳號,都可以與此伺服器上的任何人跟隨及互動。您甚至能自行架一個自己的伺服器!", "closed_registrations_modal.title": "註冊 Mastodon", @@ -171,7 +171,7 @@ "confirmations.delete_list.confirm": "刪除", "confirmations.delete_list.message": "您確定要永久刪除此列表嗎?", "confirmations.discard_edit_media.confirm": "捨棄", - "confirmations.discard_edit_media.message": "您在媒體描述或預覽區塊有未儲存的變更。是否要捨棄這些變更?", + "confirmations.discard_edit_media.message": "您於媒體描述或預覽區塊有未儲存的變更。是否要捨棄這些變更?", "confirmations.domain_block.confirm": "封鎖整個網域", "confirmations.domain_block.message": "您真的非常確定要封鎖整個 {domain} 網域嗎?大部分情況下,封鎖或靜音少數特定的帳號就能滿足需求了。您將不能在任何公開的時間軸及通知中看到來自此網域的內容。您來自該網域的跟隨者也將被移除。", "confirmations.edit.confirm": "編輯", @@ -205,7 +205,7 @@ "dismissable_banner.explore_statuses": "這些於此伺服器以及去中心化網路中其他伺服器發出的嘟文正在被此伺服器上的人們熱烈討論著。越多不同人轉嘟及最愛排名更高。", "dismissable_banner.explore_tags": "這些主題標籤正在被此伺服器以及去中心化網路上的人們熱烈討論著。越多不同人所嘟出的主題標籤排名更高。", "dismissable_banner.public_timeline": "這些是來自 {domain} 使用者們跟隨中帳號所發表之最新公開嘟文。", - "embed.instructions": "要在您的網站嵌入此嘟文,請複製以下程式碼。", + "embed.instructions": "若您欲於您的網站嵌入此嘟文,請複製以下程式碼。", "embed.preview": "它將顯示成這樣:", "emoji_button.activity": "活動", "emoji_button.clear": "清除", @@ -218,7 +218,7 @@ "emoji_button.objects": "物件", "emoji_button.people": "人物", "emoji_button.recent": "最常使用", - "emoji_button.search": "搜尋…", + "emoji_button.search": "搜尋...", "emoji_button.search_results": "搜尋結果", "emoji_button.symbols": "符號", "emoji_button.travel": "旅遊與地點", @@ -259,7 +259,7 @@ "filter_modal.added.context_mismatch_title": "不符合情境!", "filter_modal.added.expired_explanation": "此過濾器類別已失效,您需要更新過期日期以套用。", "filter_modal.added.expired_title": "過期的過濾器!", - "filter_modal.added.review_and_configure": "要檢視和進一步設定此過濾器類別,請至 {settings_link}。", + "filter_modal.added.review_and_configure": "要檢視與進一步設定此過濾器類別,請至 {settings_link}。", "filter_modal.added.review_and_configure_title": "過濾器設定", "filter_modal.added.settings_link": "設定頁面", "filter_modal.added.short_explanation": "此嘟文已被新增至以下過濾器類別:{title}。", @@ -362,7 +362,7 @@ "keyboard_shortcuts.search": "將游標移至搜尋框", "keyboard_shortcuts.spoilers": "顯示或隱藏內容警告之嘟文", "keyboard_shortcuts.start": "開啟「開始使用」欄位", - "keyboard_shortcuts.toggle_hidden": "顯示或隱藏在內容警告之後的嘟文", + "keyboard_shortcuts.toggle_hidden": "顯示或隱藏於內容警告之後的嘟文", "keyboard_shortcuts.toggle_sensitivity": "顯示或隱藏媒體", "keyboard_shortcuts.toot": "發個新嘟文", "keyboard_shortcuts.unfocus": "跳離文字撰寫區塊或搜尋框", @@ -376,7 +376,7 @@ "limited_account_hint.title": "此個人檔案已被 {domain} 的管理員隱藏。", "link_preview.author": "由 {name} 提供", "lists.account.add": "新增至列表", - "lists.account.remove": "從列表中移除", + "lists.account.remove": "自列表中移除", "lists.delete": "刪除列表", "lists.edit": "編輯列表", "lists.edit.submit": "變更標題", @@ -469,7 +469,7 @@ "notifications.permission_denied_alert": "由於之前瀏覽器權限被拒絕,無法啟用桌面通知", "notifications.permission_required": "由於尚未授予所需的權限,因此無法使用桌面通知。", "notifications_permission_banner.enable": "啟用桌面通知", - "notifications_permission_banner.how_to_control": "啟用桌面通知以在 Mastodon 沒有開啟的時候接收通知。在已經啟用桌面通知的時候,您可以透過上面的 {icon} 按鈕準確的控制哪些類型的互動會產生桌面通知。", + "notifications_permission_banner.how_to_control": "啟用桌面通知以於 Mastodon 沒有開啟的時候接收通知。啟用桌面通知後,您可以透過上面的 {icon} 按鈕準確的控制哪些類型的互動會產生桌面通知。", "notifications_permission_banner.title": "不要錯過任何東西!", "onboarding.action.back": "返回", "onboarding.actions.back": "返回", @@ -490,7 +490,7 @@ "onboarding.steps.follow_people.title": "客製化您的首頁時間軸", "onboarding.steps.publish_status.body": "向新世界打聲招呼吧。", "onboarding.steps.publish_status.title": "撰寫您第一則嘟文", - "onboarding.steps.setup_profile.body": "若您完整填寫個人檔案,其他人比較願意和您互動。", + "onboarding.steps.setup_profile.body": "若您完整填寫個人檔案,其他人比較願意與您互動。", "onboarding.steps.setup_profile.title": "客製化您的個人檔案", "onboarding.steps.share_profile.body": "讓您的朋友們知道如何於 Mastodon 找到您!", "onboarding.steps.share_profile.title": "分享您的 Mastodon 個人檔案", @@ -614,10 +614,10 @@ "sign_in_banner.create_account": "新增帳號", "sign_in_banner.sign_in": "登入", "sign_in_banner.sso_redirect": "登入或註冊", - "sign_in_banner.text": "登入以跟隨個人檔案和主題標籤,或收藏、分享和回覆嘟文。您也可以使用您的帳號在其他伺服器上進行互動。", + "sign_in_banner.text": "登入以跟隨個人檔案與主題標籤,或收藏、分享及回覆嘟文。您也可以使用您的帳號於其他伺服器進行互動。", "status.admin_account": "開啟 @{name} 的管理介面", "status.admin_domain": "開啟 {domain} 的管理介面", - "status.admin_status": "在管理介面開啟此嘟文", + "status.admin_status": "於管理介面開啟此嘟文", "status.block": "封鎖 @{name}", "status.bookmark": "書籤", "status.cancel_reblog_private": "取消轉嘟", @@ -672,8 +672,8 @@ "status.translated_from_with": "透過 {provider} 翻譯 {lang}", "status.uncached_media_warning": "無法預覽", "status.unmute_conversation": "解除此對話的靜音", - "status.unpin": "從個人檔案頁面取消釘選", - "subscribed_languages.lead": "僅選定語言的嘟文才會出現在您的首頁上,並在變更後列出時間軸。選取「無」以接收所有語言的嘟文。", + "status.unpin": "自個人檔案頁面取消釘選", + "subscribed_languages.lead": "僅選定語言的嘟文才會出現於您的首頁上,並於變更後列出時間軸。選取「無」以接收所有語言的嘟文。", "subscribed_languages.save": "儲存變更", "subscribed_languages.target": "變更 {target} 的訂閱語言", "tabs_bar.home": "首頁", @@ -696,7 +696,7 @@ "upload_area.title": "拖放來上傳", "upload_button.label": "上傳圖片、影片、或者音樂檔案", "upload_error.limit": "已達到檔案上傳限制。", - "upload_error.poll": "不允許在投票中上傳檔案。", + "upload_error.poll": "不允許於投票時上傳檔案。", "upload_form.audio_description": "為聽障人士增加文字說明", "upload_form.description": "為視障人士增加文字說明", "upload_form.description_missing": "沒有任何描述", @@ -706,7 +706,7 @@ "upload_form.video_description": "為聽障或視障人士增加文字說明", "upload_modal.analyzing_picture": "正在分析圖片…", "upload_modal.apply": "套用", - "upload_modal.applying": "正在套用⋯⋯", + "upload_modal.applying": "正在套用...", "upload_modal.choose_image": "選擇圖片", "upload_modal.description_placeholder": "我能吞下玻璃而不傷身體", "upload_modal.detect_text": "從圖片中偵測文字", diff --git a/config/locales/activerecord.lt.yml b/config/locales/activerecord.lt.yml index f54d00471..cb6e21d8e 100644 --- a/config/locales/activerecord.lt.yml +++ b/config/locales/activerecord.lt.yml @@ -1,6 +1,19 @@ --- lt: activerecord: + attributes: + poll: + expires_at: Galutinė data + options: Pasirinkimai + user: + agreement: Paslaugos sutartis + email: El. laiško adresas + locale: Lokali + password: Slaptažodis + user/account: + username: Naudotojo vardas + user/invite_request: + text: Priežastis errors: models: account: @@ -12,3 +25,35 @@ lt: attributes: url: invalid: nėra tinkamas URL adresas. + doorkeeper/application: + attributes: + website: + invalid: nėra tinkamas URL adresas. + import: + attributes: + data: + malformed: yra netaisyklinga. + status: + attributes: + reblog: + taken: įrašas jau egzistuoja. + user: + attributes: + email: + blocked: naudoja neleidžiamą el. laiško paslaugų teikėją. + unreachable: neatrodo, kad egzistuoja. + role_id: + elevated: negali būti didesnis nei tavo dabartinis vaidmuo. + user_role: + attributes: + permissions_as_keys: + dangerous: apima leidimus, kurie nėra saugūs pagrindiniam vaidmeniui. + elevated: negali apimti leidimų, kurių neturi tavo dabartinis vaidmuo. + own_role: negali būti pakeistas tavo dabartinis vaidmuo. + position: + elevated: negali būti didesnis nei tavo dabartinis vaidmuo. + own_role: negali būti pakeistas tavo dabartinis vaidmuo. + webhook: + attributes: + events: + invalid_permissions: negali įtraukti įvykių, į kuriuos neturi teisių. diff --git a/config/locales/be.yml b/config/locales/be.yml index 96a272012..223b4d1df 100644 --- a/config/locales/be.yml +++ b/config/locales/be.yml @@ -1052,7 +1052,7 @@ be: localization: body: Mastodon перакладаецца добраахвотнікамі. guide_link: https://be.crowdin.com/project/mastodon/be - guide_link_text: Кожны можа зрабіць унёсак. + guide_link_text: Кожны і кожная можа зрабіць унёсак. sensitive_content: Далікатны змест application_mailer: notification_preferences: Змяніць налады эл. пошты @@ -1575,7 +1575,7 @@ be: duration_too_short: гэта занадта хутка expired: Апытанне ўжо скончана invalid_choice: Абраны варыянт апытання не існуе - over_character_limit: не можа быць даўжэй за %{max} сімвалаў кожны + over_character_limit: кожны не можа быць даўжэй за %{max} сімвалаў self_vote: Вы не можаце галасаваць ва ўласных апытаннях too_few_options: павінна быць болей за адзін варыянт too_many_options: колькасць варыянтаў не можа перавышаць %{max} @@ -1764,7 +1764,7 @@ be: public: Публічны public_long: Усе могуць бачыць unlisted: Не ў спісе - unlisted_long: Кожны можа ўбачыць гэты допіс, але ён не паказваецца ў публічных стужках + unlisted_long: Усе могуць пабачыць гэты допіс, але ён не паказваецца ў публічных стужках statuses_cleanup: enabled: Аўтаматычна выдаляць старыя допісы enabled_hint: Аўтаматычна выдаляць вашыя допісы, калі яны дасягаюць вызначанага тэрміну, акрамя наступных выпадкаў diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 15ca09470..a04682a44 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -1074,6 +1074,10 @@ cs: hint_html: Ještě jedna věc! Musíme potvrdit, že jste člověk (to proto, abychom drželi stranou spam!). Vyřešte CAPTCHA níže a klikněte na "Pokračovat". title: Bezpečnostní kontrola confirmations: + awaiting_review_title: Vaše registrace se ověřuje + clicking_this_link: kliknutím na tento odkaz + registration_complete: Vaše registrace na %{domain} je hotová! + welcome_title: Vítám uživatele %{name}! wrong_email_hint: Pokud není e-mail správný, můžete si ho změnit v nastavení účtu. delete_account: Odstranit účet delete_account_html: Chcete-li odstranit svůj účet, <a href="%{path}">pokračujte zde</a>. Budete požádáni o potvrzení. @@ -1770,6 +1774,9 @@ cs: default: "%d. %b %Y, %H:%M" month: "%b %Y" time: "%H:%M" + translation: + errors: + too_many_requests: Na překladatelskou službu bylo zasláno v poslední době příliš mnoho požadavků. two_factor_authentication: add: Přidat disable: Vypnout 2FA diff --git a/config/locales/devise.zh-TW.yml b/config/locales/devise.zh-TW.yml index c01beb796..de977426f 100644 --- a/config/locales/devise.zh-TW.yml +++ b/config/locales/devise.zh-TW.yml @@ -9,7 +9,7 @@ zh-TW: already_authenticated: 您已登入。 inactive: 您的帳號尚未啟用。 invalid: 無效的 %{authentication_keys} 或密碼。 - last_attempt: 在帳號鎖定前,您還有最後一次嘗試機會。 + last_attempt: 帳號鎖定前,您還有最後一次嘗試機會。 locked: 已鎖定您的帳號。 not_found_in_database: 無效的 %{authentication_keys} 或密碼。 pending: 您的帳號仍在審核中。 @@ -20,8 +20,8 @@ zh-TW: confirmation_instructions: action: 驗證電子郵件地址 action_with_app: 確認並返回 %{app} - explanation: 您已經在 %{host} 上以此電子郵件地址建立了一支帳號。您距離啟用它只剩一點之遙了。若這不是您,請忽略此信件。 - explanation_when_pending: 您使用此電子郵件地址申請了 %{host} 的邀請。當您確認電子郵件地址後我們將審核您的申請。您可以在登入後變更詳細資訊或刪除您的帳號,但直到您的帳號被核准之前,您無法操作大部分的功能。若您的申請遭拒絕,您的資料將被移除而不必做後續動作。如果這不是您本人,請忽略此郵件。 + explanation: 您已於 %{host} 上以此電子郵件地址建立了一支帳號。您距離啟用它只剩一點之遙了。若這不是您,請忽略此信件。 + explanation_when_pending: 您使用此電子郵件地址申請了 %{host} 的邀請。當您確認電子郵件地址後我們將審核您的申請。您能於登入後變更詳細資訊或刪除您的帳號,但直到您的帳號被核准之前,您無法操作大部分的功能。若您的申請遭拒絕,您的資料將被移除而不必做後續動作。如果這不是您本人,請忽略此郵件。 extra_html: 同時也請看看<a href="%{terms_path}">伺服器規則</a>與<a href="%{policy_path}">服務條款</a>。 subject: Mastodon:%{instance} 確認說明 title: 驗證電子郵件地址 @@ -37,7 +37,7 @@ zh-TW: title: 密碼已變更 reconfirmation_instructions: explanation: 請確認新的電子郵件地址以變更。 - extra: 若此次變更不是由您起始的,請忽略此信件。Mastodon 帳號的電子郵件地址在您存取上面的連結前不會變更。 + extra: 若此次變更不是由您起始的,請忽略此信件。Mastodon 帳號的電子郵件地址於您存取上面的連結前不會變更。 subject: Mastodon:確認 %{instance} 的電子郵件地址 title: 驗證電子郵件地址 reset_password_instructions: @@ -106,7 +106,7 @@ zh-TW: errors: messages: already_confirmed: 已經確認,請嘗試登入 - confirmation_period_expired: 需要在 %{period} 內完成驗證。請重新申請 + confirmation_period_expired: 您需要於 %{period} 內完成驗證。請重新申請 expired: 已經過期,請重新請求 not_found: 找不到 not_locked: 並未鎖定 diff --git a/config/locales/doorkeeper.zh-TW.yml b/config/locales/doorkeeper.zh-TW.yml index 6073096c3..c0d42ec7b 100644 --- a/config/locales/doorkeeper.zh-TW.yml +++ b/config/locales/doorkeeper.zh-TW.yml @@ -72,7 +72,7 @@ zh-TW: revoke: 您確定嗎? index: authorized_at: 於 %{date} 授權 - description_html: 這些應用程式能透過 API 存取您的帳號。若有您不認得之應用程式,或應用程式行為異常,您可以於此註銷其存取權限。 + description_html: 這些應用程式能透過 API 存取您的帳號。若有您不認得之應用程式,或應用程式行為異常,您能於此註銷其存取權限。 last_used_at: 上次使用時間 %{date} never_used: 從未使用 scopes: 權限 diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 608599724..69b1aa0a9 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -419,8 +419,8 @@ pl: hint: Blokada domen nie zabroni tworzenia wpisów kont w bazie danych, ale pozwoli na automatyczną moderację kont do nich należących. severity: desc_html: "<strong>Wyciszenie</strong> uczyni wpisy użytkowników z tej domeny widoczne tylko dla osób, które go obserwują. <strong>Zawieszenie</strong> spowoduje usunięcie całej zawartości dodanej przez użytkownika. Użyj <strong>Żadne</strong>, jeżeli chcesz jedynie odrzucać zawartość multimedialną." - noop: Nic nie rób - silence: Limit + noop: Żadne + silence: Wycisz suspend: Zawieś title: Nowa blokada domen no_domain_block_selected: Nie zmieniono żadnych bloków domen, gdyż żadna nie została wybrana diff --git a/config/locales/simple_form.zh-TW.yml b/config/locales/simple_form.zh-TW.yml index be21f862f..13b2ad30a 100644 --- a/config/locales/simple_form.zh-TW.yml +++ b/config/locales/simple_form.zh-TW.yml @@ -15,25 +15,25 @@ zh-TW: account_migration: acct: 指定要移動至的帳號的「使用者名稱@網域名稱」 account_warning_preset: - text: 您可使用嘟文語法,例如網址、「#」標籤和提及功能 - title: 可選。不會向收件者顯示 + text: 您可使用嘟文語法,例如網址、「#」標籤與提及功能 + title: 可選的。不會向收件者顯示 admin_account_action: include_statuses: 使用者可看到導致檢舉或警告的嘟文 send_email_notification: 使用者將收到帳號發生之事情的解釋 - text_html: 選用。您能使用嘟文語法。您可 <a href="%{path}">新增警告預設</a> 來節省時間 + text_html: 可選的。您能使用嘟文語法。您可 <a href="%{path}">新增警告預設</a> 來節省時間 type_html: 設定要使用 <strong>%{acct}</strong> 做的事 types: disable: 禁止該使用者使用他們的帳號,但是不刪除或隱藏他們的內容。 none: 使用這個寄送警告給該使用者,而不進行其他動作。 sensitive: 強制標記此使用者所有多媒體附加檔案為敏感內容。 - silence: 禁止該使用者發公開嘟文,從無跟隨他們的帳號中隱藏嘟文和通知。關閉所有對此帳號之檢舉報告。 + silence: 禁止該使用者發公開嘟文,從無跟隨他們的帳號中隱藏嘟文與通知。關閉所有對此帳號之檢舉報告。 suspend: 禁止所有對該帳號任何互動,並且刪除其內容。三十天內可以撤銷此動作。關閉所有對此帳號之檢舉報告。 - warning_preset_id: 選用。您仍可在預設的結尾新增自訂文字 + warning_preset_id: 可選的。您仍可於預設的結尾新增自訂文字 announcement: all_day: 當選取時,僅顯示出時間範圍中的日期部分 - ends_at: 可選的,公告會於該時間點自動取消發布 + ends_at: 可選的。公告會於該時間點自動取消發布 scheduled_at: 空白則立即發布公告 - starts_at: 可選的,讓公告在特定時間範圍內顯示 + starts_at: 可選的。讓公告於特定時間範圍內顯示 text: 您可以使用嘟文語法,但請小心別讓公告太鴨霸而佔據使用者的整個版面。 appeal: text: 您只能對警示提出一次申訴 @@ -44,12 +44,12 @@ zh-TW: context: 此過濾器應套用於以下一項或多項情境 current_password: 因安全因素,請輸入目前帳號的密碼 current_username: 請輸入目前帳號的使用者名稱以確認 - digest: 僅在您長時間未登入且在未登入期間收到私訊時傳送 + digest: 僅於您長時間未登入且於未登入期間收到私訊時傳送 email: 您將收到一封確認電子郵件 header: 支援 PNG、GIF 或 JPG 圖片格式,檔案最大為 %{size},會等比例縮減至 %{dimensions} 像素 inbox_url: 從您想要使用的中繼首頁複製網址 irreversible: 已過濾的嘟文將會不可逆地消失,即便之後移除過濾器也一樣 - locale: 使用者介面、電子郵件和推播通知的語言 + locale: 使用者介面、電子郵件與推播通知的語言 password: 使用至少 8 個字元 phrase: 無論是嘟文的本文或是內容警告都會被過濾 scopes: 允許讓應用程式存取的 API。 若您選擇最高階範圍,則無須選擇個別項目。 @@ -62,12 +62,12 @@ zh-TW: setting_use_blurhash: 彩色漸層圖樣是基於隱藏媒體內容顏色產生,所有細節將變得模糊 setting_use_pending_items: 關閉自動捲動更新,時間軸僅於點擊後更新 username: 您可以使用字幕、數字與底線 - whole_word: 如果關鍵字或詞組僅有字母與數字,則其將只在符合整個單字的時候才會套用 + whole_word: 如果關鍵字或詞組僅有字母與數字,則其將只於符合整個單字時才會套用 domain_allow: - domain: 此網域將能夠攫取本站資料,而自該網域發出的資料也會於本站處理和留存。 + domain: 此網域將能夠攫取本站資料,而自該網域發出的資料也會於本站處理及留存。 email_domain_block: - domain: 這可以是顯示在電子郵件中的網域名稱,或是其使用的 MX 紀錄。其將於註冊時檢查。 - with_dns_records: Mastodon 會嘗試解析所給網域的 DNS 記錄,解析結果一致者將一併封鎖 + domain: 這可以是顯示於電子郵件中的網域名稱,或是其使用的 MX 紀錄。其將於註冊時檢查。 + with_dns_records: Mastodon 會嘗試解析所提供之網域的 DNS 記錄,解析結果一致者將一併封鎖 featured_tag: name: 這些是您最近使用的一些主題標籤: filters: @@ -97,7 +97,7 @@ zh-TW: theme: 未登入之訪客或新使用者所見之佈景主題。 thumbnail: 大約 2:1 圖片會顯示於您伺服器資訊之旁。 timeline_preview: 未登入之訪客能夠瀏覽此伺服器上最新的公開嘟文。 - trendable_by_default: 跳過手動審核熱門內容。仍能在登上熱門趨勢後移除個別內容。 + trendable_by_default: 跳過手動審核熱門內容。仍能於登上熱門趨勢後移除個別內容。 trends: 熱門趨勢將顯示於您伺服器上正在吸引大量注意力的嘟文、主題標籤、或者新聞。 trends_as_landing_page: 顯示熱門趨勢內容給未登入使用者及訪客而不是關於此伺服器之描述。需要啟用熱門趨勢。 form_challenge: @@ -107,9 +107,9 @@ zh-TW: invite_request: text: 這會協助我們審核您的申請 ip_block: - comment: 可選的,但請記得您為何添加這項規則。 + comment: 可選的。但請記得您為何添加這項規則。 expires_in: IP 位址是經常共用或轉手的有限資源,不建議無限期地封鎖特定 IP 位址。 - ip: 請輸入 IPv4 或 IPv6 位址,亦可以用 CIDR 語法以封鎖整個 IP 區段。小心不要將自己給一併封鎖掉囉! + ip: 請輸入 IPv4 或 IPv6 位址,亦可以用 CIDR 語法以封鎖整個 IP 區段。小心不要將自己一併封鎖掉囉! severities: no_access: 封鎖對所有資源存取 sign_up_block: 無法註冊新帳號 @@ -129,11 +129,11 @@ zh-TW: chosen_languages: 當選取時,只有選取語言之嘟文會於公開時間軸中顯示 role: 角色控制使用者有哪些權限 user_role: - color: 在整個使用者介面中用於角色的顏色,十六進位格式的 RGB + color: 於整個使用者介面中用於角色的顏色,十六進位格式的 RGB highlighted: 這會讓角色公開可見 name: 角色的公開名稱,如果角色設定為顯示為徽章 - permissions_as_keys: 有此角色的使用者將有權存取…… - position: 在某些情況下,衝突的解決方式由更高階的角色決定。某些動作只能由優先程度較低的角色執行 + permissions_as_keys: 有此角色的使用者將有權存取... + position: 某些情況下,衝突的解決方式由更高階的角色決定。某些動作只能由優先程度較低的角色執行 webhook: events: 請選擇要傳送的事件 template: 使用變數代換組合您自己的 JSON payload。留白以使用預設 JSON 。 @@ -155,7 +155,7 @@ zh-TW: text: 預設文字 title: 標題 admin_account_action: - include_statuses: 在電子郵件中加入檢舉的嘟文 + include_statuses: 於電子郵件中加入檢舉之嘟文內容 send_email_notification: 透過電子郵件通知使用者 text: 自訂警告 type: 動作 @@ -230,7 +230,7 @@ zh-TW: username_or_email: 使用者名稱或電子郵件地址 whole_word: 整個詞彙 email_domain_block: - with_dns_records: 包括網域的 MX 記錄和 IP 位址 + with_dns_records: 包括網域的 MX 記錄與 IP 位址 featured_tag: name: "「#」主題標籤" filters: @@ -287,7 +287,7 @@ zh-TW: favourite: 當有使用者將您的嘟文加入最愛時,傳送電子郵件通知 follow: 當有使用者跟隨您時,傳送電子郵件通知 follow_request: 當有使用者請求跟隨您時,傳送電子郵件通知 - mention: 當有使用者在嘟文提及您時,傳送電子郵件通知 + mention: 當有使用者於嘟文提及您時,傳送電子郵件通知 pending_account: 有新的帳號需要審核 reblog: 當有使用者轉嘟您的嘟文時,傳送電子郵件通知 report: 新回報已遞交 @@ -304,16 +304,16 @@ zh-TW: indexable: 於搜尋引擎中包含個人檔案頁面 show_application: 顯示您發嘟文之應用程式 tag: - listable: 允許此主題標籤在搜尋及個人檔案目錄中顯示 + listable: 允許此主題標籤於搜尋及個人檔案目錄中顯示 name: 主題標籤 - trendable: 允許此主題標籤在熱門趨勢下顯示 + trendable: 允許此主題標籤於熱門趨勢下顯示 usable: 允許嘟文使用此主題標籤 user: role: 角色 time_zone: 時區 user_role: color: 識別顏色 - highlighted: 在使用者個人檔案上將角色顯示為徽章 + highlighted: 於使用者個人檔案中顯示角色徽章 name: 名稱 permissions_as_keys: 權限 position: 優先權 diff --git a/config/locales/vi.yml b/config/locales/vi.yml index ec8f6c139..9d90d1d51 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -1343,6 +1343,7 @@ vi: '86400': 1 ngày expires_in_prompt: Không giới hạn generate: Tạo lời mời + invalid: Lời mời không hợp lệ invited_by: 'Bạn đã được mời bởi:' max_uses: other: "%{count} lần dùng" diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 3063b7afd..7259afdbe 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -1,10 +1,10 @@ --- zh-TW: about: - about_mastodon_html: Mastodon (長毛象)是一個<em>自由、開放原始碼</em>的社群網站。它是一個分散式的服務,避免您的通訊被單一商業機構壟斷操控。請您選擇一家您信任的 Mastodon 站點,在上面建立帳號,然後您就可以和任一 Mastodon 站點上的使用者互通,享受無縫的<em>社群網路</em>交流。 + about_mastodon_html: Mastodon (長毛象)是一個<em>自由、開放原始碼</em>的社群網站。它是一個分散式的服務,避免您的通訊被單一商業機構壟斷操控。請您選擇一家您信任的 Mastodon 站點,於其建立帳號,您就能與任一 Mastodon 站點上的使用者互通,享受無縫的<em>社群網路</em>交流。 contact_missing: 未設定 contact_unavailable: 未公開 - hosted_on: 在 %{domain} 運作的 Mastodon 站點 + hosted_on: 於 %{domain} 託管之 Mastodon 站點 title: 關於本站 accounts: follow: 跟隨 @@ -13,7 +13,7 @@ zh-TW: following: 正在跟隨 instance_actor_flash: 此帳號是用來代表此伺服器的虛擬執行者,而非個別使用者。它的用途為維繫聯邦宇宙,且不應被停權。 last_active: 上次活躍時間 - link_verified_on: 此連結的所有權已在 %{date} 檢查過 + link_verified_on: 此連結之所有權已於 %{date} 檢查過 nothing_here: 暫時沒有內容可供顯示! pin_errors: following: 您只能推薦您正在跟隨的使用者。 @@ -115,7 +115,7 @@ zh-TW: reject: 拒絕 rejected_msg: 已成功婉拒 %{username} 的新帳號申請 remote_suspension_irreversible: 此帳號之資料已被不可逆地刪除。 - remote_suspension_reversible_hint_html: 這個帳號已於此伺服器被停權,所有資料將會於 %{date} 被刪除。在此之前,遠端伺服器可以完全回復此的帳號。如果您想即時刪除這個帳號的資料,您可以在下面進行操作。 + remote_suspension_reversible_hint_html: 這個帳號已於此伺服器被停權,所有資料將會於 %{date} 被刪除。於此之前,遠端伺服器可以完全回復此的帳號。如果您想即時刪除這個帳號的資料,您能於下面進行操作。 remove_avatar: 取消大頭貼 remove_header: 移除開頭 removed_avatar_msg: 已成功刪除 %{username} 的大頭貼 @@ -149,7 +149,7 @@ zh-TW: suspend: 停權 suspended: 已停權 suspension_irreversible: 已永久刪除此帳號的資料。您可以取消這個帳號的停權狀態,但無法還原已刪除的資料。 - suspension_reversible_hint_html: 這個帳號已被暫停,所有數據將於 %{date} 被刪除。在此之前,您可以完全回復您的帳號。如果您想即時刪除這個帳號的數據,您可以在下面進行操作。 + suspension_reversible_hint_html: 這個帳號已被暫停,所有數據將於 %{date} 被刪除。於此之前,您可以完全回復您的帳號。如果您想即時刪除這個帳號的數據,您能於下面進行操作。 title: 帳號 unblock_email: 解除封鎖電子郵件地址 unblocked_email_msg: 成功解除封鎖 %{username} 的電子郵件地址 @@ -333,7 +333,7 @@ zh-TW: not_permitted: 您無權執行此操作 overwrite: 覆蓋 shortcode: 短代碼 - shortcode_hint: 至少 2 個字元,只能使用字母、數字和下劃線 + shortcode_hint: 至少 2 個字元,只能使用字母、數字與下劃線 title: 自訂表情符號 uncategorized: 未分類 unlist: 不公開 @@ -370,10 +370,10 @@ zh-TW: domain_allows: add_new: 將網域加入聯邦宇宙白名單 created_msg: 網域已成功加入聯邦宇宙白名單 - destroyed_msg: 網域已成功從聯邦宇宙白名單移除 + destroyed_msg: 網域已成功自聯邦宇宙白名單移除 export: 匯出 import: 匯入 - undo: 從聯邦宇宙白名單移除 + undo: 自聯邦宇宙白名單移除 domain_blocks: add_new: 新增網域黑名單 confirm_suspension: @@ -397,7 +397,7 @@ zh-TW: create: 新增封鎖 hint: 站點封鎖動作並不會阻止帳號紀錄被新增至資料庫,但會自動回溯性地對那些帳號套用特定管理設定。 severity: - desc_html: "「<strong>靜音</strong>」令該站點下使用者的嘟文,設定為只對跟隨者顯示,沒有跟隨的人會看不到。「<strong>停權</strong>」會刪除將該站點下使用者的嘟文、媒體檔案和個人檔案。「<strong>無</strong>」則會拒絕接收來自該站點的媒體檔案。" + desc_html: "「<strong>靜音</strong>」令該站點下使用者的嘟文,設定為只對跟隨者顯示,沒有跟隨的人會看不到。「<strong>停權</strong>」會刪除將該站點下使用者的嘟文、媒體檔案與個人檔案。「<strong>無</strong>」則會拒絕接收來自該站點的媒體檔案。" noop: 無 silence: 靜音 suspend: 停權 @@ -451,7 +451,7 @@ zh-TW: title: 匯入網域黑名單 no_file: 尚未選擇檔案 follow_recommendations: - description_html: "<strong>跟隨建議幫助新使用者們快速找到有趣的內容</strong>。當使用者沒有與其他帳號有足夠多的互動以建立個人化跟隨建議時,這些帳號將會被推薦。這些帳號將基於某選定語言之高互動和高本地跟隨者數量帳號而每日重新更新。" + description_html: "<strong>跟隨建議幫助新使用者們快速找到有趣的內容</strong>。當使用者沒有與其他帳號有足夠多的互動以建立個人化跟隨建議時,這些帳號將會被推薦。這些帳號將基於某選定語言之高互動與高本地跟隨者數量帳號而每日重新更新。" language: 對於語言 status: 狀態 suppress: 取消跟隨建議 @@ -461,7 +461,7 @@ zh-TW: instances: availability: description_html: - other: 若在<strong>%{count}天</strong>向某個網域遞送失敗,除非收到某個網域的遞送<em>表單</em>,否則不會繼續嘗試遞送。 + other: 若於 <strong>%{count} 天</strong>向某個網域遞送失敗,除非收到某個網域的遞送<em>表單</em>,否則不會繼續嘗試遞送。 failure_threshold_reached: 錯誤門檻於 %{date}。 failures_recorded: other: 錯誤嘗試於 %{count} 天。 @@ -590,7 +590,7 @@ zh-TW: by_target_domain: 檢舉帳號之網域 cancel: 取消 category: 分類 - category_description_html: 此帳號及/或被檢舉內容之原因會被引用在檢舉帳號通知中 + category_description_html: 此帳號及/或被檢舉內容之原因將被引用於檢舉帳號通知中 comment: none: 無 comment_description_html: 提供更多資訊,%{name} 寫道: @@ -611,7 +611,7 @@ zh-TW: delete: 刪除 placeholder: 記錄已執行的動作,或其他相關的更新... title: 備註 - notes_description_html: 檢視及留下些給其他管理員和未來的自己的備註 + notes_description_html: 檢視及留下些給其他管理員與未來的自己的備註 processed_msg: '檢舉報告 #%{id} 已被成功處理' quick_actions_description_html: 採取一個快速行動,或者下捲以檢視檢舉內容: remote_user_placeholder: 來自 %{instance} 之遠端使用者 @@ -624,7 +624,7 @@ zh-TW: skip_to_actions: 跳過行動 status: 嘟文 statuses: 被檢舉的內容 - statuses_description_html: 侵犯性違規內容會被引用在檢舉帳號通知中 + statuses_description_html: 侵犯性違規內容將被引用於檢舉帳號通知中 summary: action_preambles: delete_html: 您將要 <strong>移除</strong> 某些 <strong>@%{acct}</strong> 之嘟文。此將會: @@ -677,7 +677,7 @@ zh-TW: manage_announcements: 管理公告 manage_announcements_description: 允許使用者管理伺服器上的公告 manage_appeals: 管理解封申訴系統 - manage_appeals_description: 允許使用者審閱針對站務動作的申訴 + manage_appeals_description: 允許使用者審閱針對站務動作之申訴 manage_blocks: 管理封鎖 manage_blocks_description: 允許使用者封鎖電子郵件提供者與 IP 位置 manage_custom_emojis: 管理自訂表情符號 @@ -741,7 +741,7 @@ zh-TW: title: 預設將使用者排除於搜尋引擎索引 discovery: follow_recommendations: 跟隨建議 - preamble: 呈現有趣的內容有助於 Mastodon 上一人不識的新手上路。控制各種不同的分類在您伺服器上如何被探索到。 + preamble: 呈現有趣的內容有助於 Mastodon 上一人不識的新手上路。控制各種不同的分類於您伺服器上如何被探索到。 profile_directory: 個人檔案目錄 public_timelines: 公開時間軸 publish_discovered_servers: 公開已知伺服器列表 @@ -989,11 +989,11 @@ zh-TW: created_msg: 成功建立別名。您可以自舊帳號開始轉移。 deleted_msg: 成功移除別名。您將無法再由舊帳號轉移至目前的帳號。 empty: 您目前沒有任何別名。 - hint_html: 如果想由其他帳號轉移至此帳號,您可以於此處新增別名,稍後系統將容許您將跟隨者由舊帳號轉移至此。此項作業是<strong>無害且可復原的</strong>。 <strong>帳號的遷移程序需要在舊帳號啟動</strong>。 + hint_html: 如果想由其他帳號轉移至此帳號,您能於此處新增別名,稍後系統將容許您將跟隨者由舊帳號轉移至此。此項作業是<strong>無害且可復原的</strong>。 <strong>帳號的遷移程序需要於舊帳號啟動</strong>。 remove: 取消連結別名 appearance: advanced_web_interface: 進階網頁介面 - advanced_web_interface_hint: 進階網頁介面可讓您設定許多不同的欄位來善用螢幕空間,依需要同時查看許多不同的資訊如:首頁、通知、聯邦宇宙時間軸、任意數量的列表和主題標籤。 + advanced_web_interface_hint: 進階網頁介面可讓您設定許多不同的欄位來善用螢幕空間,依需要同時查看許多不同的資訊如:首頁、通知、聯邦宇宙時間軸、任意數量的列表與主題標籤。 animations_and_accessibility: 動畫與無障礙設定 confirmation_dialogs: 確認對話框 discovery: 探索 @@ -1033,13 +1033,13 @@ zh-TW: redirect_to_app_html: 您應被重新導向至 <strong>%{app_name}</strong> 應用程式。如尚未重新導向,請嘗試 %{clicking_this_link} 或手動回到應用程式。 registration_complete: 您於 %{domain} 之註冊申請已完成! welcome_title: 歡迎,%{name}! - wrong_email_hint: 若電子郵件地址不正確,您可以於帳號設定中更改。 + wrong_email_hint: 若電子郵件地址不正確,您能於帳號設定中更改。 delete_account: 刪除帳號 delete_account_html: 如果您欲刪除您的帳號,請<a href="%{path}">點擊這裡繼續</a>。您需要再三確認您的操作。 description: prefix_invited_by_user: "@%{name} 邀請您加入這個 Mastodon 伺服器!" prefix_sign_up: 馬上註冊 Mastodon 帳號吧! - suffix: 有了帳號,就可以從任何 Mastodon 伺服器跟隨任何人、發發廢嘟,並且與任何 Mastodon 伺服器的使用者交流,以及更多! + suffix: 有了帳號,就可以自任何 Mastodon 伺服器跟隨任何人、發發廢嘟,並且與任何 Mastodon 伺服器的使用者交流,以及更多! didnt_get_confirmation: 沒有收到確認連結嗎? dont_have_your_security_key: 找不到您的安全金鑰? forgot_password: 忘記密碼? @@ -1085,7 +1085,7 @@ zh-TW: preamble_html: 請使用您於 <strong>%{domain}</strong> 的帳號密碼登入。若您的帳號託管於其他伺服器,您將無法於此登入。 title: 登入 %{domain} sign_up: - manual_review: "%{domain} 上的註冊由我們的管理員進行人工審核。為協助我們處理您的註冊,請寫一些關於您自己的資訊以及您想要在 %{domain} 上註冊帳號的原因。" + manual_review: "%{domain} 上的註冊由我們的管理員進行人工審核。為協助我們處理您的註冊,請寫一些關於您自己的資訊以及您欲於 %{domain} 上註冊帳號之原因。" preamble: 於此 Mastodon 伺服器擁有帳號的話,您將能跟隨聯邦宇宙網路中任何一份子,無論他們的帳號託管於何處。 title: 讓我們一起設定 %{domain} 吧! status: @@ -1100,7 +1100,7 @@ zh-TW: use_security_key: 使用安全金鑰 challenge: confirm: 繼續 - hint_html: "<strong>温馨小提醒:</strong> 我們在接下來一小時內不會再要求您輸入密碼。" + hint_html: "<strong>温馨小提醒:</strong> 我們於接下來一小時內不會再要求您輸入密碼。" invalid_password: 密碼錯誤 prompt: 輸入密碼以繼續 crypto: @@ -1134,8 +1134,8 @@ zh-TW: warning: before: 在進行下一步驟之前,請詳細閱讀以下説明: caches: 已被其他節點快取的內容可能會殘留其中 - data_removal: 您的嘟文和其他資料將會被永久刪除 - email_change_html: 您可以在不刪除帳號的情況下<a href="%{path}">變更您的電子郵件地址</a> + data_removal: 您的嘟文與其他資料將被永久刪除 + email_change_html: 您能於不刪除帳號的情況下<a href="%{path}">變更您的電子郵件地址</a> email_contact_html: 如果您仍然沒有收到郵件,請寄信至 <a href="mailto:%{email}">%{email}</a> 以獲得協助 email_reconfirmation_html: 如果您沒有收到確認郵件,可以<a href="%{path}">請求再次發送</a> irreversible: 您將無法復原或重新啟用您的帳號 @@ -1176,7 +1176,7 @@ zh-TW: invalid_domain: 並非一個有效網域 edit_profile: basic_information: 基本資訊 - hint_html: "<strong>自訂人們可以於您個人檔案及嘟文內容。</strong>當您完成填寫個人檔案以及設定大頭貼後,其他人們比較願意跟隨您並與您互動。" + hint_html: "<strong>自訂人們能於您個人檔案及嘟文旁所見之內容。</strong>當您完成填寫個人檔案以及設定大頭貼後,其他人們比較願意跟隨您並與您互動。" other: 其他 errors: '400': 您所送出的請求無效或格式不正確。 @@ -1194,13 +1194,13 @@ zh-TW: '503': 此頁面因伺服器暫時發生錯誤而無法提供。 noscript_html: 使用 Mastodon 網頁版應用需要啟用 JavaScript。您也可以選擇適用於您的平台的 <a href="%{apps_path}">Mastodon 應用</a>。 existing_username_validator: - not_found: 無法在本站找到這個名稱的使用者 + not_found: 無法於本伺服器找到此使用者帳號 not_found_multiple: 揣嘸 %{usernames} exports: archive_takeout: date: 日期 download: 下載檔案 - hint_html: 您可以下載包含您的<strong>文章和媒體</strong>的檔案。資料以 ActivityPub 格式儲存,可用於相容的軟體。每次允許存檔的間隔至少 7 天。 + hint_html: 您可以下載包含您的<strong>嘟文與媒體</strong>的檔案。資料以 ActivityPub 格式儲存,可用於相容之軟體。每次允許存檔的間隔至少 7 天。 in_progress: 正在準備您的存檔... request: 下載存檔 size: 大小 @@ -1304,7 +1304,7 @@ zh-TW: following_html: 您將要 <strong>跟隨</strong> 自 <strong>%{filename}</strong> 中之 <strong>%{total_items} 個帳號</strong>。 lists_html: 您將自 <strong>%{filename}</strong> 新增 <strong>%{total_items} 個帳號</strong>至您的<strong>列表</strong>。若不存在列表用以新增帳號,則會建立新列表。 muting_html: 您將要 <strong>靜音</strong> 自 <strong>%{filename}</strong> 中之 <strong>%{total_items} 個帳號</strong>。 - preface: 您能於此匯入您在其他伺服器所匯出的資料檔,包括跟隨中的使用者、封鎖的使用者名單等。 + preface: 您能於此匯入您於其他伺服器所匯出的資料檔,包括跟隨中的使用者、封鎖的使用者名單等。 recent_imports: 最近匯入的 states: finished: 已完成 @@ -1414,12 +1414,12 @@ zh-TW: warning: backreference_required: 新的帳號必須先設定為反向參照到目前帳號 before: 在進行下一步驟之前,請詳細閱讀以下説明: - cooldown: 在轉移帳號後會有一段等待時間,在等待時間內您將無法再次轉移 + cooldown: 轉移帳號後會有一段等待時間,等待時間內您將無法再次轉移 disabled_account: 之後您的目前帳號將完全無法使用。但您可以存取資料匯出與重新啟用。 followers: 此動作將會將目前帳號的所有跟隨者轉移至新帳號 - only_redirect_html: 或者,您也可以<a href="%{path}">僅在您的個人檔案中設定重新導向</a>。 + only_redirect_html: 或者,您也可以<a href="%{path}">僅於您的個人檔案中設定重新導向</a>。 other_data: 其他資料並不會自動轉移 - redirect: 您目前的帳號將於個人檔案頁面新增重新導向公告,並會被排除在搜尋結果之外 + redirect: 您目前的帳號將於個人檔案頁面新增重新導向公告,並會被排除於搜尋結果之外 moderation: title: 站務 move_handler: @@ -1449,8 +1449,8 @@ zh-TW: title: 新的跟隨請求 mention: action: 回覆 - body: "%{name} 在嘟文中提及您:" - subject: "%{name} 在嘟文中提及您" + body: "%{name} 於嘟文中提及您:" + subject: "%{name} 於嘟文中提及您" title: 新的提及 poll: subject: 由 %{name} 發起的投票已結束 @@ -1510,7 +1510,7 @@ zh-TW: privacy: hint_html: "<strong>自訂您希望如何讓您的個人檔案及嘟文被發現。</strong>藉由啟用一系列 Mastodon 功能以幫助您觸及更廣的受眾。煩請花些時間確認您是否欲啟用這些設定。" privacy: 隱私權 - privacy_hint_html: 控制您希望向其他人揭露之內容。人們透過瀏覽其他人的跟隨者與其發嘟之應用程式發現有趣的個人檔案和酷炫的 Mastodon 應用程式,但您能選擇將其隱藏。 + privacy_hint_html: 控制您希望向其他人揭露之內容。人們透過瀏覽其他人的跟隨者與其發嘟之應用程式發現有趣的個人檔案與酷炫的 Mastodon 應用程式,但您能選擇將其隱藏。 reach: 觸及 reach_hint_html: 控制您希望被新使用者探索或跟隨之方式。想讓您的嘟文出現於探索頁面嗎?想讓其他人透過他們的跟隨建議找到您嗎?想自動接受所有新跟隨者嗎?或是想逐一控制跟隨請求嗎? search: 搜尋 @@ -1669,7 +1669,7 @@ zh-TW: private_long: 只有跟隨您的人能看到 public: 公開 public_long: 所有人都能看到 - unlisted: 不在公開時間軸顯示 + unlisted: 不於公開時間軸顯示 unlisted_long: 所有人都能看到,但不會出現在公開時間軸上 statuses_cleanup: enabled: 自動刪除舊嘟文 @@ -1679,7 +1679,7 @@ zh-TW: ignore_favs: 忽略最愛數 ignore_reblogs: 忽略轉嘟數 interaction_exceptions: 基於互動的例外規則 - interaction_exceptions_explanation: 請注意嘟文是無法保證被刪除的,如果在一次處理過後嘟文低於最愛或轉嘟的門檻。 + interaction_exceptions_explanation: 請注意嘟文是無法保證被刪除的,如果於一次處理過後嘟文低於最愛或轉嘟的門檻。 keep_direct: 保留私訊 keep_direct_hint: 不會刪除任何您的私訊 keep_media: 保留包含多媒體附加檔案之嘟文 @@ -1735,7 +1735,7 @@ zh-TW: enabled: 兩階段認證已啟用 enabled_success: 已成功啟用兩階段認證 generate_recovery_codes: 產生備用驗證碼 - lost_recovery_codes: 讓您可以在遺失手機時,使用備用驗證碼登入。若您已遺失備用驗證碼,可於此產生一批新的,舊有的備用驗證碼將會失效。 + lost_recovery_codes: 讓您能於遺失手機時,使用備用驗證碼登入。若您已遺失備用驗證碼,可於此產生一批新的,舊有的備用驗證碼將會失效。 methods: 兩步驟方式 otp: 驗證應用程式 recovery_codes: 備份備用驗證碼 @@ -1745,12 +1745,12 @@ zh-TW: user_mailer: appeal_approved: action: 前往您的帳號 - explanation: 您在 %{appeal_date} 遞交的針對您帳號的 %{strike_date} 警示的申訴已獲批准。您的帳號再次享有良好的信譽。 - subject: 您在 %{date} 提出的申訴已獲批准 + explanation: 您於 %{appeal_date} 遞交的針對您帳號的 %{strike_date} 警示之申訴已獲批准。您的帳號再次享有良好的信譽。 + subject: 您於 %{date} 提出之申訴已獲批准 title: 申訴已批准 appeal_rejected: - explanation: 您在 %{appeal_date} 遞交的針對您帳號的 %{strike_date} 警示的申訴已被駁回。 - subject: 您在 %{date} 提出的申訴已被駁回 + explanation: 您於 %{appeal_date} 遞交的針對您帳號的 %{strike_date} 警示之申訴已被駁回。 + subject: 您於 %{date} 提出之申訴已被駁回 title: 申訴被駁回 backup_ready: explanation: 您要求的 Mastodon 帳號完整備份檔案現已就緒,可供下載! @@ -1772,7 +1772,7 @@ zh-TW: explanation: delete_statuses: 您的某些嘟文被發現已違反一項或多項社群準則,隨後已被 %{instance} 的管理員刪除。 disable: 您無法繼續使用您的帳號,但您的個人頁面及其他資料內容保持不變。您可以要求一份您的資料備份,帳號異動設定,或是刪除帳號。 - mark_statuses_as_sensitive: 您的部份嘟文已被 %{instance} 的管理員標記為敏感內容。這代表了人們必須在顯示預覽前點擊嘟文中的媒體。您可以在將來嘟文時自己將媒體標記為敏感內容。 + mark_statuses_as_sensitive: 您的部份嘟文已被 %{instance} 的管理員標記為敏感內容。這代表了人們必須於顯示預覽前點擊嘟文中的媒體。您能於將來嘟文時自己將媒體標記為敏感內容。 sensitive: 由此刻起,您所有上傳的媒體檔案將被標記為敏感內容,並且隱藏於點擊警告之後。 silence: 您仍然能使用您的帳號,但僅有已跟隨您的人才能見到您於此伺服器之嘟文,您也可能會從各式探索功能中被排除。但其他人仍可手動跟隨您。 suspend: 您將不能使用您的帳號,您的個人檔案頁面及其他資料將不再能被存取。您仍可於約 30 日內資料被完全刪除前要求下載您的資料,但我們仍會保留一部份基本資料,以防止有人規避停權處罰。 @@ -1781,9 +1781,9 @@ zh-TW: subject: delete_statuses: 您於 %{acct} 之嘟文已被移除 disable: 您的帳號 %{acct} 已被凍結 - mark_statuses_as_sensitive: 您在 %{acct} 上的嘟文已被標記為敏感內容 + mark_statuses_as_sensitive: 您於 %{acct} 上的嘟文已被標記為敏感內容 none: 對 %{acct} 的警告 - sensitive: 從現在開始,您在 %{acct} 上的嘟文將會被標記為敏感內容 + sensitive: 從現在開始,您於 %{acct} 上之嘟文將會被標記為敏感內容 silence: 您的帳號 %{acct} 已被限制 suspend: 您的帳號 %{acct} 已被停權 title: @@ -1796,10 +1796,10 @@ zh-TW: suspend: 帳號己被停權 welcome: edit_profile_action: 設定個人檔案 - edit_profile_step: 您可以設定您的個人檔案,包括上傳大頭貼、變更顯示名稱等等。您也可以選擇在新的跟隨者跟隨前,先對他們進行審核。 + edit_profile_step: 您可以設定您的個人檔案,包括上傳大頭貼、變更顯示名稱等等。您也可以選擇於新的跟隨者跟隨前,先對他們進行審核。 explanation: 下面是幾個小幫助,希望它們能幫到您 final_action: 開始嘟嘟 - final_step: '開始嘟嘟吧!即使您現在沒有跟隨者,其他人仍然能在本站時間軸、主題標籤等地方,看到您的公開嘟文。試著用 #introductions 這個主題標籤介紹一下自己吧。' + final_step: '開始嘟嘟吧!即使您現在沒有跟隨者,其他人仍然能於本站時間軸、主題標籤等地方,看到您的公開嘟文。試著用 #introductions 這個主題標籤介紹一下自己吧。' full_handle: 您的完整帳號名稱 full_handle_hint: 您需要將這告訴您的朋友們,這樣他們就能從另一個伺服器向您發送訊息或跟隨您。 subject: 歡迎來到 Mastodon From d67bd44ca1542d665354e733b632c841b6b7d29b Mon Sep 17 00:00:00 2001 From: Eugen Rochko <eugen@zeonfederated.com> Date: Wed, 15 Nov 2023 12:13:53 +0100 Subject: [PATCH 37/63] Add profile setup to onboarding in web UI (#27829) --- .../api/v1/accounts/credentials_controller.rb | 2 + app/javascript/mastodon/actions/accounts.js | 15 ++ app/javascript/mastodon/api_types/accounts.ts | 1 + .../mastodon/components/admin/Retention.jsx | 2 +- .../mastodon/components/loading_indicator.tsx | 26 ++- .../components/progress_indicator.jsx | 29 --- .../features/onboarding/components/step.jsx | 15 +- .../mastodon/features/onboarding/follows.jsx | 105 ++++------ .../mastodon/features/onboarding/index.jsx | 190 ++++++------------ .../mastodon/features/onboarding/profile.jsx | 162 +++++++++++++++ .../mastodon/features/onboarding/share.jsx | 100 ++++----- app/javascript/mastodon/features/ui/index.jsx | 2 +- app/javascript/mastodon/locales/en.json | 13 +- app/javascript/mastodon/models/account.ts | 1 + .../styles/mastodon/components.scss | 164 +++++---------- app/javascript/styles/mastodon/forms.scss | 105 ++++++++-- app/serializers/rest/account_serializer.rb | 6 +- config/routes.rb | 2 +- 18 files changed, 524 insertions(+), 416 deletions(-) delete mode 100644 app/javascript/mastodon/features/onboarding/components/progress_indicator.jsx create mode 100644 app/javascript/mastodon/features/onboarding/profile.jsx diff --git a/app/controllers/api/v1/accounts/credentials_controller.rb b/app/controllers/api/v1/accounts/credentials_controller.rb index 76ba75824..8f31336b9 100644 --- a/app/controllers/api/v1/accounts/credentials_controller.rb +++ b/app/controllers/api/v1/accounts/credentials_controller.rb @@ -16,6 +16,8 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController current_user.update(user_params) if user_params ActivityPub::UpdateDistributionWorker.perform_async(@account.id) render json: @account, serializer: REST::CredentialAccountSerializer + rescue ActiveRecord::RecordInvalid => e + render json: ValidationErrorFormatter.new(e).as_json, status: 422 end private diff --git a/app/javascript/mastodon/actions/accounts.js b/app/javascript/mastodon/actions/accounts.js index e0448f004..9f3bbba03 100644 --- a/app/javascript/mastodon/actions/accounts.js +++ b/app/javascript/mastodon/actions/accounts.js @@ -661,3 +661,18 @@ export function unpinAccountFail(error) { error, }; } + +export const updateAccount = ({ displayName, note, avatar, header, discoverable, indexable }) => (dispatch, getState) => { + const data = new FormData(); + + data.append('display_name', displayName); + data.append('note', note); + if (avatar) data.append('avatar', avatar); + if (header) data.append('header', header); + data.append('discoverable', discoverable); + data.append('indexable', indexable); + + return api(getState).patch('/api/v1/accounts/update_credentials', data).then(response => { + dispatch(importFetchedAccount(response.data)); + }); +}; diff --git a/app/javascript/mastodon/api_types/accounts.ts b/app/javascript/mastodon/api_types/accounts.ts index 985abf946..5bf3e6428 100644 --- a/app/javascript/mastodon/api_types/accounts.ts +++ b/app/javascript/mastodon/api_types/accounts.ts @@ -20,6 +20,7 @@ export interface ApiAccountJSON { bot: boolean; created_at: string; discoverable: boolean; + indexable: boolean; display_name: string; emojis: ApiCustomEmojiJSON[]; fields: ApiAccountFieldJSON[]; diff --git a/app/javascript/mastodon/components/admin/Retention.jsx b/app/javascript/mastodon/components/admin/Retention.jsx index 2f5671068..1e8ef48b7 100644 --- a/app/javascript/mastodon/components/admin/Retention.jsx +++ b/app/javascript/mastodon/components/admin/Retention.jsx @@ -51,7 +51,7 @@ export default class Retention extends PureComponent { let content; if (loading) { - content = <FormattedMessage id='loading_indicator.label' defaultMessage='Loading...' />; + content = <FormattedMessage id='loading_indicator.label' defaultMessage='Loading…' />; } else { content = ( <table className='retention__table'> diff --git a/app/javascript/mastodon/components/loading_indicator.tsx b/app/javascript/mastodon/components/loading_indicator.tsx index 6bc24a0d6..fcdbe80d8 100644 --- a/app/javascript/mastodon/components/loading_indicator.tsx +++ b/app/javascript/mastodon/components/loading_indicator.tsx @@ -1,7 +1,23 @@ +import { useIntl, defineMessages } from 'react-intl'; + import { CircularProgress } from './circular_progress'; -export const LoadingIndicator: React.FC = () => ( - <div className='loading-indicator'> - <CircularProgress size={50} strokeWidth={6} /> - </div> -); +const messages = defineMessages({ + loading: { id: 'loading_indicator.label', defaultMessage: 'Loading…' }, +}); + +export const LoadingIndicator: React.FC = () => { + const intl = useIntl(); + + return ( + <div + className='loading-indicator' + role='progressbar' + aria-busy + aria-live='polite' + aria-label={intl.formatMessage(messages.loading)} + > + <CircularProgress size={50} strokeWidth={6} /> + </div> + ); +}; diff --git a/app/javascript/mastodon/features/onboarding/components/progress_indicator.jsx b/app/javascript/mastodon/features/onboarding/components/progress_indicator.jsx deleted file mode 100644 index 37288a286..000000000 --- a/app/javascript/mastodon/features/onboarding/components/progress_indicator.jsx +++ /dev/null @@ -1,29 +0,0 @@ -import PropTypes from 'prop-types'; -import { Fragment } from 'react'; - -import classNames from 'classnames'; - -import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/done.svg'; - -import { Icon } from 'mastodon/components/icon'; - -const ProgressIndicator = ({ steps, completed }) => ( - <div className='onboarding__progress-indicator'> - {(new Array(steps)).fill().map((_, i) => ( - <Fragment key={i}> - {i > 0 && <div className={classNames('onboarding__progress-indicator__line', { active: completed > i })} />} - - <div className={classNames('onboarding__progress-indicator__step', { active: completed > i })}> - {completed > i && <Icon icon={CheckIcon} />} - </div> - </Fragment> - ))} - </div> -); - -ProgressIndicator.propTypes = { - steps: PropTypes.number.isRequired, - completed: PropTypes.number, -}; - -export default ProgressIndicator; diff --git a/app/javascript/mastodon/features/onboarding/components/step.jsx b/app/javascript/mastodon/features/onboarding/components/step.jsx index 1f42d9d49..1f83f2080 100644 --- a/app/javascript/mastodon/features/onboarding/components/step.jsx +++ b/app/javascript/mastodon/features/onboarding/components/step.jsx @@ -1,11 +1,13 @@ import PropTypes from 'prop-types'; +import { Link } from 'react-router-dom'; + import { ReactComponent as ArrowRightAltIcon } from '@material-symbols/svg-600/outlined/arrow_right_alt.svg'; import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/done.svg'; -import { Icon } from 'mastodon/components/icon'; +import { Icon } from 'mastodon/components/icon'; -const Step = ({ label, description, icon, iconComponent, completed, onClick, href }) => { +export const Step = ({ label, description, icon, iconComponent, completed, onClick, href, to }) => { const content = ( <> <div className='onboarding__steps__item__icon'> @@ -29,6 +31,12 @@ const Step = ({ label, description, icon, iconComponent, completed, onClick, hre {content} </a> ); + } else if (to) { + return ( + <Link to={to} className='onboarding__steps__item'> + {content} + </Link> + ); } return ( @@ -45,7 +53,6 @@ Step.propTypes = { iconComponent: PropTypes.func, completed: PropTypes.bool, href: PropTypes.string, + to: PropTypes.string, onClick: PropTypes.func, }; - -export default Step; diff --git a/app/javascript/mastodon/features/onboarding/follows.jsx b/app/javascript/mastodon/features/onboarding/follows.jsx index e21c7c75b..e23a335c0 100644 --- a/app/javascript/mastodon/features/onboarding/follows.jsx +++ b/app/javascript/mastodon/features/onboarding/follows.jsx @@ -1,79 +1,62 @@ -import PropTypes from 'prop-types'; -import { PureComponent } from 'react'; +import { useEffect } from 'react'; import { FormattedMessage } from 'react-intl'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import { connect } from 'react-redux'; +import { Link } from 'react-router-dom'; + +import { useDispatch } from 'react-redux'; + import { fetchSuggestions } from 'mastodon/actions/suggestions'; import { markAsPartial } from 'mastodon/actions/timelines'; -import Column from 'mastodon/components/column'; import { ColumnBackButton } from 'mastodon/components/column_back_button'; import { EmptyAccount } from 'mastodon/components/empty_account'; import Account from 'mastodon/containers/account_container'; +import { useAppSelector } from 'mastodon/store'; -const mapStateToProps = state => ({ - suggestions: state.getIn(['suggestions', 'items']), - isLoading: state.getIn(['suggestions', 'isLoading']), -}); +export const Follows = () => { + const dispatch = useDispatch(); + const isLoading = useAppSelector(state => state.getIn(['suggestions', 'isLoading'])); + const suggestions = useAppSelector(state => state.getIn(['suggestions', 'items'])); -class Follows extends PureComponent { - - static propTypes = { - onBack: PropTypes.func, - dispatch: PropTypes.func.isRequired, - suggestions: ImmutablePropTypes.list, - isLoading: PropTypes.bool, - }; - - componentDidMount () { - const { dispatch } = this.props; + useEffect(() => { dispatch(fetchSuggestions(true)); + + return () => { + dispatch(markAsPartial('home')); + }; + }, [dispatch]); + + let loadedContent; + + if (isLoading) { + loadedContent = (new Array(8)).fill().map((_, i) => <EmptyAccount key={i} />); + } else if (suggestions.isEmpty()) { + loadedContent = <div className='follow-recommendations__empty'><FormattedMessage id='onboarding.follows.empty' defaultMessage='Unfortunately, no results can be shown right now. You can try using search or browsing the explore page to find people to follow, or try again later.' /></div>; + } else { + loadedContent = suggestions.map(suggestion => <Account id={suggestion.get('account')} key={suggestion.get('account')} withBio />); } - componentWillUnmount () { - const { dispatch } = this.props; - dispatch(markAsPartial('home')); - } + return ( + <> + <ColumnBackButton /> - render () { - const { onBack, isLoading, suggestions } = this.props; - - let loadedContent; - - if (isLoading) { - loadedContent = (new Array(8)).fill().map((_, i) => <EmptyAccount key={i} />); - } else if (suggestions.isEmpty()) { - loadedContent = <div className='follow-recommendations__empty'><FormattedMessage id='onboarding.follows.empty' defaultMessage='Unfortunately, no results can be shown right now. You can try using search or browsing the explore page to find people to follow, or try again later.' /></div>; - } else { - loadedContent = suggestions.map(suggestion => <Account id={suggestion.get('account')} key={suggestion.get('account')} withBio />); - } - - return ( - <Column> - <ColumnBackButton onClick={onBack} /> - - <div className='scrollable privacy-policy'> - <div className='column-title'> - <h3><FormattedMessage id='onboarding.follows.title' defaultMessage='Popular on Mastodon' /></h3> - <p><FormattedMessage id='onboarding.follows.lead' defaultMessage='You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!' /></p> - </div> - - <div className='follow-recommendations'> - {loadedContent} - </div> - - <p className='onboarding__lead'><FormattedMessage id='onboarding.tips.accounts_from_other_servers' defaultMessage='<strong>Did you know?</strong> Since Mastodon is decentralized, some profiles you come across will be hosted on servers other than yours. And yet you can interact with them seamlessly! Their server is in the second half of their username!' values={{ strong: chunks => <strong>{chunks}</strong> }} /></p> - - <div className='onboarding__footer'> - <button className='link-button' onClick={onBack}><FormattedMessage id='onboarding.actions.back' defaultMessage='Take me back' /></button> - </div> + <div className='scrollable privacy-policy'> + <div className='column-title'> + <h3><FormattedMessage id='onboarding.follows.title' defaultMessage='Popular on Mastodon' /></h3> + <p><FormattedMessage id='onboarding.follows.lead' defaultMessage='You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!' /></p> </div> - </Column> - ); - } -} + <div className='follow-recommendations'> + {loadedContent} + </div> -export default connect(mapStateToProps)(Follows); + <p className='onboarding__lead'><FormattedMessage id='onboarding.tips.accounts_from_other_servers' defaultMessage='<strong>Did you know?</strong> Since Mastodon is decentralized, some profiles you come across will be hosted on servers other than yours. And yet you can interact with them seamlessly! Their server is in the second half of their username!' values={{ strong: chunks => <strong>{chunks}</strong> }} /></p> + + <div className='onboarding__footer'> + <Link className='link-button' to='/start'><FormattedMessage id='onboarding.actions.back' defaultMessage='Take me back' /></Link> + </div> + </div> + </> + ); +}; diff --git a/app/javascript/mastodon/features/onboarding/index.jsx b/app/javascript/mastodon/features/onboarding/index.jsx index 51d4b71f2..51677fbc7 100644 --- a/app/javascript/mastodon/features/onboarding/index.jsx +++ b/app/javascript/mastodon/features/onboarding/index.jsx @@ -1,152 +1,90 @@ -import PropTypes from 'prop-types'; +import { useCallback } from 'react'; -import { FormattedMessage, injectIntl, defineMessages } from 'react-intl'; +import { FormattedMessage, useIntl, defineMessages } from 'react-intl'; import { Helmet } from 'react-helmet'; -import { Link, withRouter } from 'react-router-dom'; +import { Link, Switch, Route, useHistory } from 'react-router-dom'; + +import { useDispatch } from 'react-redux'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import { connect } from 'react-redux'; import { ReactComponent as AccountCircleIcon } from '@material-symbols/svg-600/outlined/account_circle.svg'; import { ReactComponent as ArrowRightAltIcon } from '@material-symbols/svg-600/outlined/arrow_right_alt.svg'; import { ReactComponent as ContentCopyIcon } from '@material-symbols/svg-600/outlined/content_copy.svg'; import { ReactComponent as EditNoteIcon } from '@material-symbols/svg-600/outlined/edit_note.svg'; import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outlined/person_add.svg'; -import { debounce } from 'lodash'; import illustration from 'mastodon/../images/elephant_ui_conversation.svg'; -import { fetchAccount } from 'mastodon/actions/accounts'; import { focusCompose } from 'mastodon/actions/compose'; -import { closeOnboarding } from 'mastodon/actions/onboarding'; import { Icon } from 'mastodon/components/icon'; import Column from 'mastodon/features/ui/components/column'; import { me } from 'mastodon/initial_state'; -import { makeGetAccount } from 'mastodon/selectors'; +import { useAppSelector } from 'mastodon/store'; import { assetHost } from 'mastodon/utils/config'; -import { WithRouterPropTypes } from 'mastodon/utils/react_router'; -import Step from './components/step'; -import Follows from './follows'; -import Share from './share'; +import { Step } from './components/step'; +import { Follows } from './follows'; +import { Profile } from './profile'; +import { Share } from './share'; const messages = defineMessages({ template: { id: 'onboarding.compose.template', defaultMessage: 'Hello #Mastodon!' }, }); -const mapStateToProps = () => { - const getAccount = makeGetAccount(); +const Onboarding = () => { + const account = useAppSelector(state => state.getIn(['accounts', me])); + const dispatch = useDispatch(); + const intl = useIntl(); + const history = useHistory(); - return state => ({ - account: getAccount(state, me), - }); + const handleComposeClick = useCallback(() => { + dispatch(focusCompose(history, intl.formatMessage(messages.template))); + }, [dispatch, intl, history]); + + return ( + <Column> + <Switch> + <Route path='/start' exact> + <div className='scrollable privacy-policy'> + <div className='column-title'> + <img src={illustration} alt='' className='onboarding__illustration' /> + <h3><FormattedMessage id='onboarding.start.title' defaultMessage="You've made it!" /></h3> + <p><FormattedMessage id='onboarding.start.lead' defaultMessage="Your new Mastodon account is ready to go. Here's how you can make the most of it:" /></p> + </div> + + <div className='onboarding__steps'> + <Step to='/start/profile' completed={(!account.get('avatar').endsWith('missing.png')) || (account.get('display_name').length > 0 && account.get('note').length > 0)} icon='address-book-o' iconComponent={AccountCircleIcon} label={<FormattedMessage id='onboarding.steps.setup_profile.title' defaultMessage='Customize your profile' />} description={<FormattedMessage id='onboarding.steps.setup_profile.body' defaultMessage='Others are more likely to interact with you with a filled out profile.' />} /> + <Step to='/start/follows' completed={(account.get('following_count') * 1) >= 1} icon='user-plus' iconComponent={PersonAddIcon} label={<FormattedMessage id='onboarding.steps.follow_people.title' defaultMessage='Find at least {count, plural, one {one person} other {# people}} to follow' values={{ count: 7 }} />} description={<FormattedMessage id='onboarding.steps.follow_people.body' defaultMessage="You curate your own home feed. Let's fill it with interesting people." />} /> + <Step onClick={handleComposeClick} completed={(account.get('statuses_count') * 1) >= 1} icon='pencil-square-o' iconComponent={EditNoteIcon} label={<FormattedMessage id='onboarding.steps.publish_status.title' defaultMessage='Make your first post' />} description={<FormattedMessage id='onboarding.steps.publish_status.body' defaultMessage='Say hello to the world.' values={{ emoji: <img className='emojione' alt='🐘' src={`${assetHost}/emoji/1f418.svg`} /> }} />} /> + <Step to='/start/share' icon='copy' iconComponent={ContentCopyIcon} label={<FormattedMessage id='onboarding.steps.share_profile.title' defaultMessage='Share your profile' />} description={<FormattedMessage id='onboarding.steps.share_profile.body' defaultMessage='Let your friends know how to find you on Mastodon!' />} /> + </div> + + <p className='onboarding__lead'><FormattedMessage id='onboarding.start.skip' defaultMessage="Don't need help getting started?" /></p> + + <div className='onboarding__links'> + <Link to='/explore' className='onboarding__link'> + <FormattedMessage id='onboarding.actions.go_to_explore' defaultMessage='Take me to trending' /> + <Icon icon={ArrowRightAltIcon} /> + </Link> + + <Link to='/home' className='onboarding__link'> + <FormattedMessage id='onboarding.actions.go_to_home' defaultMessage='Take me to my home feed' /> + <Icon icon={ArrowRightAltIcon} /> + </Link> + </div> + </div> + </Route> + + <Route path='/start/profile' component={Profile} /> + <Route path='/start/follows' component={Follows} /> + <Route path='/start/share' component={Share} /> + </Switch> + + <Helmet> + <meta name='robots' content='noindex' /> + </Helmet> + </Column> + ); }; -class Onboarding extends ImmutablePureComponent { - static propTypes = { - dispatch: PropTypes.func.isRequired, - account: ImmutablePropTypes.record, - ...WithRouterPropTypes, - }; - - state = { - step: null, - profileClicked: false, - shareClicked: false, - }; - - handleClose = () => { - const { dispatch, history } = this.props; - - dispatch(closeOnboarding()); - history.push('/home'); - }; - - handleProfileClick = () => { - this.setState({ profileClicked: true }); - }; - - handleFollowClick = () => { - this.setState({ step: 'follows' }); - }; - - handleComposeClick = () => { - const { dispatch, intl, history } = this.props; - - dispatch(focusCompose(history, intl.formatMessage(messages.template))); - }; - - handleShareClick = () => { - this.setState({ step: 'share', shareClicked: true }); - }; - - handleBackClick = () => { - this.setState({ step: null }); - }; - - handleWindowFocus = debounce(() => { - const { dispatch, account } = this.props; - dispatch(fetchAccount(account.get('id'))); - }, 1000, { trailing: true }); - - componentDidMount () { - window.addEventListener('focus', this.handleWindowFocus, false); - } - - componentWillUnmount () { - window.removeEventListener('focus', this.handleWindowFocus); - } - - render () { - const { account } = this.props; - const { step, shareClicked } = this.state; - - switch(step) { - case 'follows': - return <Follows onBack={this.handleBackClick} />; - case 'share': - return <Share onBack={this.handleBackClick} />; - } - - return ( - <Column> - <div className='scrollable privacy-policy'> - <div className='column-title'> - <img src={illustration} alt='' className='onboarding__illustration' /> - <h3><FormattedMessage id='onboarding.start.title' defaultMessage="You've made it!" /></h3> - <p><FormattedMessage id='onboarding.start.lead' defaultMessage="Your new Mastodon account is ready to go. Here's how you can make the most of it:" /></p> - </div> - - <div className='onboarding__steps'> - <Step onClick={this.handleProfileClick} href='/settings/profile' completed={(!account.get('avatar').endsWith('missing.png')) || (account.get('display_name').length > 0 && account.get('note').length > 0)} icon='address-book-o' iconComponent={AccountCircleIcon} label={<FormattedMessage id='onboarding.steps.setup_profile.title' defaultMessage='Customize your profile' />} description={<FormattedMessage id='onboarding.steps.setup_profile.body' defaultMessage='Others are more likely to interact with you with a filled out profile.' />} /> - <Step onClick={this.handleFollowClick} completed={(account.get('following_count') * 1) >= 7} icon='user-plus' iconComponent={PersonAddIcon} label={<FormattedMessage id='onboarding.steps.follow_people.title' defaultMessage='Find at least {count, plural, one {one person} other {# people}} to follow' values={{ count: 7 }} />} description={<FormattedMessage id='onboarding.steps.follow_people.body' defaultMessage="You curate your own home feed. Let's fill it with interesting people." />} /> - <Step onClick={this.handleComposeClick} completed={(account.get('statuses_count') * 1) >= 1} icon='pencil-square-o' iconComponent={EditNoteIcon} label={<FormattedMessage id='onboarding.steps.publish_status.title' defaultMessage='Make your first post' />} description={<FormattedMessage id='onboarding.steps.publish_status.body' defaultMessage='Say hello to the world.' values={{ emoji: <img className='emojione' alt='🐘' src={`${assetHost}/emoji/1f418.svg`} /> }} />} /> - <Step onClick={this.handleShareClick} completed={shareClicked} icon='copy' iconComponent={ContentCopyIcon} label={<FormattedMessage id='onboarding.steps.share_profile.title' defaultMessage='Share your profile' />} description={<FormattedMessage id='onboarding.steps.share_profile.body' defaultMessage='Let your friends know how to find you on Mastodon!' />} /> - </div> - - <p className='onboarding__lead'><FormattedMessage id='onboarding.start.skip' defaultMessage="Don't need help getting started?" /></p> - - <div className='onboarding__links'> - <Link to='/explore' className='onboarding__link'> - <FormattedMessage id='onboarding.actions.go_to_explore' defaultMessage='Take me to trending' /> - <Icon icon={ArrowRightAltIcon} /> - </Link> - - <Link to='/home' className='onboarding__link'> - <FormattedMessage id='onboarding.actions.go_to_home' defaultMessage='Take me to my home feed' /> - <Icon icon={ArrowRightAltIcon} /> - </Link> - </div> - </div> - - <Helmet> - <meta name='robots' content='noindex' /> - </Helmet> - </Column> - ); - } - -} - -export default withRouter(connect(mapStateToProps)(injectIntl(Onboarding))); +export default Onboarding; diff --git a/app/javascript/mastodon/features/onboarding/profile.jsx b/app/javascript/mastodon/features/onboarding/profile.jsx new file mode 100644 index 000000000..19ba0bcb9 --- /dev/null +++ b/app/javascript/mastodon/features/onboarding/profile.jsx @@ -0,0 +1,162 @@ +import { useState, useMemo, useCallback, createRef } from 'react'; + +import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; + +import classNames from 'classnames'; +import { useHistory } from 'react-router-dom'; + +import { useDispatch } from 'react-redux'; + + +import { ReactComponent as AddPhotoAlternateIcon } from '@material-symbols/svg-600/outlined/add_photo_alternate.svg'; +import { ReactComponent as EditIcon } from '@material-symbols/svg-600/outlined/edit.svg'; +import Toggle from 'react-toggle'; + +import { updateAccount } from 'mastodon/actions/accounts'; +import { Button } from 'mastodon/components/button'; +import { ColumnBackButton } from 'mastodon/components/column_back_button'; +import { Icon } from 'mastodon/components/icon'; +import { LoadingIndicator } from 'mastodon/components/loading_indicator'; +import { me } from 'mastodon/initial_state'; +import { useAppSelector } from 'mastodon/store'; +import { unescapeHTML } from 'mastodon/utils/html'; + +const messages = defineMessages({ + uploadHeader: { id: 'onboarding.profile.upload_header', defaultMessage: 'Upload profile header' }, + uploadAvatar: { id: 'onboarding.profile.upload_avatar', defaultMessage: 'Upload profile picture' }, +}); + +export const Profile = () => { + const account = useAppSelector(state => state.getIn(['accounts', me])); + const [displayName, setDisplayName] = useState(account.get('display_name')); + const [note, setNote] = useState(unescapeHTML(account.get('note'))); + const [avatar, setAvatar] = useState(null); + const [header, setHeader] = useState(null); + const [discoverable, setDiscoverable] = useState(account.get('discoverable')); + const [indexable, setIndexable] = useState(account.get('indexable')); + const [isSaving, setIsSaving] = useState(false); + const [errors, setErrors] = useState(); + const avatarFileRef = createRef(); + const headerFileRef = createRef(); + const dispatch = useDispatch(); + const intl = useIntl(); + const history = useHistory(); + + const handleDisplayNameChange = useCallback(e => { + setDisplayName(e.target.value); + }, [setDisplayName]); + + const handleNoteChange = useCallback(e => { + setNote(e.target.value); + }, [setNote]); + + const handleDiscoverableChange = useCallback(e => { + setDiscoverable(e.target.checked); + }, [setDiscoverable]); + + const handleIndexableChange = useCallback(e => { + setIndexable(e.target.checked); + }, [setIndexable]); + + const handleAvatarChange = useCallback(e => { + setAvatar(e.target?.files?.[0]); + }, [setAvatar]); + + const handleHeaderChange = useCallback(e => { + setHeader(e.target?.files?.[0]); + }, [setHeader]); + + const avatarPreview = useMemo(() => avatar ? URL.createObjectURL(avatar) : account.get('avatar'), [avatar, account]); + const headerPreview = useMemo(() => header ? URL.createObjectURL(header) : account.get('header'), [header, account]); + + const handleSubmit = useCallback(() => { + setIsSaving(true); + + dispatch(updateAccount({ + displayName, + note, + avatar, + header, + discoverable, + indexable, + })).then(() => history.push('/start/follows')).catch(err => { + setIsSaving(false); + setErrors(err.response.data.details); + }); + }, [dispatch, displayName, note, avatar, header, discoverable, indexable, history]); + + return ( + <> + <ColumnBackButton /> + + <div className='scrollable privacy-policy'> + <div className='column-title'> + <h3><FormattedMessage id='onboarding.profile.title' defaultMessage='Profile setup' /></h3> + <p><FormattedMessage id='onboarding.profile.lead' defaultMessage='You can always complete this later in the settings, where even more customization options are available.' /></p> + </div> + + <div className='simple_form'> + <div className='onboarding__profile'> + <label className={classNames('app-form__header-input', { selected: !!headerPreview, invalid: !!errors?.header })} title={intl.formatMessage(messages.uploadHeader)}> + <input + type='file' + hidden + ref={headerFileRef} + accept='image/*' + onChange={handleHeaderChange} + /> + + {headerPreview && <img src={headerPreview} alt='' />} + + <Icon icon={headerPreview ? EditIcon : AddPhotoAlternateIcon} /> + </label> + + <label className={classNames('app-form__avatar-input', { selected: !!avatarPreview, invalid: !!errors?.avatar })} title={intl.formatMessage(messages.uploadAvatar)}> + <input + type='file' + hidden + ref={avatarFileRef} + accept='image/*' + onChange={handleAvatarChange} + /> + + {avatarPreview && <img src={avatarPreview} alt='' />} + + <Icon icon={avatarPreview ? EditIcon : AddPhotoAlternateIcon} /> + </label> + </div> + + <div className={classNames('input with_block_label', { field_with_errors: !!errors?.display_name })}> + <label htmlFor='display_name'><FormattedMessage id='onboarding.profile.display_name' defaultMessage='Display name' /></label> + <span className='hint'><FormattedMessage id='onboarding.profile.display_name_hint' defaultMessage='Your full name or your fun name…' /></span> + <div className='label_input'> + <input id='display_name' type='text' value={displayName} onChange={handleDisplayNameChange} maxLength={30} /> + </div> + </div> + + <div className={classNames('input with_block_label', { field_with_errors: !!errors?.note })}> + <label htmlFor='note'><FormattedMessage id='onboarding.profile.note' defaultMessage='Bio' /></label> + <span className='hint'><FormattedMessage id='onboarding.profile.note_hint' defaultMessage='You can @mention other people or #hashtags…' /></span> + <div className='label_input'> + <textarea id='note' value={note} onChange={handleNoteChange} maxLength={500} /> + </div> + </div> + </div> + + <label className='report-dialog-modal__toggle'> + <Toggle checked={discoverable} onChange={handleDiscoverableChange} /> + <FormattedMessage id='onboarding.profile.discoverable' defaultMessage='Feature profile and posts in discovery algorithms' /> + </label> + + <label className='report-dialog-modal__toggle'> + <Toggle checked={indexable} onChange={handleIndexableChange} /> + <FormattedMessage id='onboarding.profile.indexable' defaultMessage='Include public posts in search results' /> + </label> + + <div className='onboarding__footer'> + <Button block onClick={handleSubmit} disabled={isSaving}>{isSaving ? <LoadingIndicator /> : <FormattedMessage id='onboarding.profile.save_and_continue' defaultMessage='Save and continue' />}</Button> + </div> + </div> + </> + ); +}; diff --git a/app/javascript/mastodon/features/onboarding/share.jsx b/app/javascript/mastodon/features/onboarding/share.jsx index 334924422..adc0f9cba 100644 --- a/app/javascript/mastodon/features/onboarding/share.jsx +++ b/app/javascript/mastodon/features/onboarding/share.jsx @@ -1,31 +1,25 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; -import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import classNames from 'classnames'; import { Link } from 'react-router-dom'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import { connect } from 'react-redux'; import { ReactComponent as ArrowRightAltIcon } from '@material-symbols/svg-600/outlined/arrow_right_alt.svg'; import { ReactComponent as ContentCopyIcon } from '@material-symbols/svg-600/outlined/content_copy.svg'; import SwipeableViews from 'react-swipeable-views'; -import Column from 'mastodon/components/column'; import { ColumnBackButton } from 'mastodon/components/column_back_button'; import { Icon } from 'mastodon/components/icon'; import { me, domain } from 'mastodon/initial_state'; +import { useAppSelector } from 'mastodon/store'; const messages = defineMessages({ shareableMessage: { id: 'onboarding.share.message', defaultMessage: 'I\'m {username} on #Mastodon! Come follow me at {url}' }, }); -const mapStateToProps = state => ({ - account: state.getIn(['accounts', me]), -}); - class CopyPasteText extends PureComponent { static propTypes = { @@ -141,59 +135,47 @@ class TipCarousel extends PureComponent { } -class Share extends PureComponent { +export const Share = () => { + const account = useAppSelector(state => state.getIn(['accounts', me])); + const intl = useIntl(); + const url = (new URL(`/@${account.get('username')}`, document.baseURI)).href; - static propTypes = { - onBack: PropTypes.func, - account: ImmutablePropTypes.record, - intl: PropTypes.object, - }; + return ( + <> + <ColumnBackButton /> - render () { - const { onBack, account, intl } = this.props; - - const url = (new URL(`/@${account.get('username')}`, document.baseURI)).href; - - return ( - <Column> - <ColumnBackButton onClick={onBack} /> - - <div className='scrollable privacy-policy'> - <div className='column-title'> - <h3><FormattedMessage id='onboarding.share.title' defaultMessage='Share your profile' /></h3> - <p><FormattedMessage id='onboarding.share.lead' defaultMessage='Let people know how they can find you on Mastodon!' /></p> - </div> - - <CopyPasteText value={intl.formatMessage(messages.shareableMessage, { username: `@${account.get('username')}@${domain}`, url })} /> - - <TipCarousel> - <div><p className='onboarding__lead'><FormattedMessage id='onboarding.tips.verification' defaultMessage='<strong>Did you know?</strong> You can verify your account by putting a link to your Mastodon profile on your own website and adding the website to your profile. No fees or documents necessary!' values={{ strong: chunks => <strong>{chunks}</strong> }} /></p></div> - <div><p className='onboarding__lead'><FormattedMessage id='onboarding.tips.migration' defaultMessage='<strong>Did you know?</strong> If you feel like {domain} is not a great server choice for you in the future, you can move to another Mastodon server without losing your followers. You can even host your own server!' values={{ domain, strong: chunks => <strong>{chunks}</strong> }} /></p></div> - <div><p className='onboarding__lead'><FormattedMessage id='onboarding.tips.2fa' defaultMessage='<strong>Did you know?</strong> You can secure your account by setting up two-factor authentication in your account settings. It works with any TOTP app of your choice, no phone number necessary!' values={{ strong: chunks => <strong>{chunks}</strong> }} /></p></div> - </TipCarousel> - - <p className='onboarding__lead'><FormattedMessage id='onboarding.share.next_steps' defaultMessage='Possible next steps:' /></p> - - <div className='onboarding__links'> - <Link to='/home' className='onboarding__link'> - <FormattedMessage id='onboarding.actions.go_to_home' defaultMessage='Take me to my home feed' /> - <Icon icon={ArrowRightAltIcon} /> - </Link> - - <Link to='/explore' className='onboarding__link'> - <FormattedMessage id='onboarding.actions.go_to_explore' defaultMessage='Take me to trending' /> - <Icon icon={ArrowRightAltIcon} /> - </Link> - </div> - - <div className='onboarding__footer'> - <button className='link-button' onClick={onBack}><FormattedMessage id='onboarding.action.back' defaultMessage='Take me back' /></button> - </div> + <div className='scrollable privacy-policy'> + <div className='column-title'> + <h3><FormattedMessage id='onboarding.share.title' defaultMessage='Share your profile' /></h3> + <p><FormattedMessage id='onboarding.share.lead' defaultMessage='Let people know how they can find you on Mastodon!' /></p> </div> - </Column> - ); - } -} + <CopyPasteText value={intl.formatMessage(messages.shareableMessage, { username: `@${account.get('username')}@${domain}`, url })} /> -export default connect(mapStateToProps)(injectIntl(Share)); + <TipCarousel> + <div><p className='onboarding__lead'><FormattedMessage id='onboarding.tips.verification' defaultMessage='<strong>Did you know?</strong> You can verify your account by putting a link to your Mastodon profile on your own website and adding the website to your profile. No fees or documents necessary!' values={{ strong: chunks => <strong>{chunks}</strong> }} /></p></div> + <div><p className='onboarding__lead'><FormattedMessage id='onboarding.tips.migration' defaultMessage='<strong>Did you know?</strong> If you feel like {domain} is not a great server choice for you in the future, you can move to another Mastodon server without losing your followers. You can even host your own server!' values={{ domain, strong: chunks => <strong>{chunks}</strong> }} /></p></div> + <div><p className='onboarding__lead'><FormattedMessage id='onboarding.tips.2fa' defaultMessage='<strong>Did you know?</strong> You can secure your account by setting up two-factor authentication in your account settings. It works with any TOTP app of your choice, no phone number necessary!' values={{ strong: chunks => <strong>{chunks}</strong> }} /></p></div> + </TipCarousel> + + <p className='onboarding__lead'><FormattedMessage id='onboarding.share.next_steps' defaultMessage='Possible next steps:' /></p> + + <div className='onboarding__links'> + <Link to='/home' className='onboarding__link'> + <FormattedMessage id='onboarding.actions.go_to_home' defaultMessage='Take me to my home feed' /> + <Icon icon={ArrowRightAltIcon} /> + </Link> + + <Link to='/explore' className='onboarding__link'> + <FormattedMessage id='onboarding.actions.go_to_explore' defaultMessage='Take me to trending' /> + <Icon icon={ArrowRightAltIcon} /> + </Link> + </div> + + <div className='onboarding__footer'> + <Link className='link-button' to='/start'><FormattedMessage id='onboarding.action.back' defaultMessage='Take me back' /></Link> + </div> + </div> + </> + ); +}; diff --git a/app/javascript/mastodon/features/ui/index.jsx b/app/javascript/mastodon/features/ui/index.jsx index 02c69cbba..d3fee272f 100644 --- a/app/javascript/mastodon/features/ui/index.jsx +++ b/app/javascript/mastodon/features/ui/index.jsx @@ -210,7 +210,7 @@ class SwitchingColumnsArea extends PureComponent { <WrappedRoute path='/bookmarks' component={BookmarkedStatuses} content={children} /> <WrappedRoute path='/pinned' component={PinnedStatuses} content={children} /> - <WrappedRoute path='/start' exact component={Onboarding} content={children} /> + <WrappedRoute path='/start' component={Onboarding} content={children} /> <WrappedRoute path='/directory' component={Directory} content={children} /> <WrappedRoute path={['/explore', '/search']} component={Explore} content={children} /> <WrappedRoute path={['/publish', '/statuses/new']} component={Compose} content={children} /> diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 9cbaf9305..041446037 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -390,7 +390,7 @@ "lists.search": "Search among people you follow", "lists.subheading": "Your lists", "load_pending": "{count, plural, one {# new item} other {# new items}}", - "loading_indicator.label": "Loading...", + "loading_indicator.label": "Loading…", "media_gallery.toggle_visible": "{number, plural, one {Hide image} other {Hide images}}", "moved_to_account_banner.text": "Your account {disabledAccount} is currently disabled because you moved to {movedToAccount}.", "mute_modal.duration": "Duration", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "Unfortunately, no results can be shown right now. You can try using search or browsing the explore page to find people to follow, or try again later.", "onboarding.follows.lead": "Your home feed is the primary way to experience Mastodon. The more people you follow, the more active and interesting it will be. To get you started, here are some suggestions:", "onboarding.follows.title": "Personalize your home feed", + "onboarding.profile.discoverable": "Feature profile and posts in discovery algorithms", + "onboarding.profile.display_name": "Display name", + "onboarding.profile.display_name_hint": "Your full name or your fun name…", + "onboarding.profile.indexable": "Include public posts in search results", + "onboarding.profile.lead": "You can always complete this later in the settings, where even more customization options are available.", + "onboarding.profile.note": "Bio", + "onboarding.profile.note_hint": "You can @mention other people or #hashtags…", + "onboarding.profile.save_and_continue": "Save and continue", + "onboarding.profile.title": "Profile setup", + "onboarding.profile.upload_avatar": "Upload profile picture", + "onboarding.profile.upload_header": "Upload profile header", "onboarding.share.lead": "Let people know how they can find you on Mastodon!", "onboarding.share.message": "I'm {username} on #Mastodon! Come follow me at {url}", "onboarding.share.next_steps": "Possible next steps:", diff --git a/app/javascript/mastodon/models/account.ts b/app/javascript/mastodon/models/account.ts index 00066e284..a04ebe629 100644 --- a/app/javascript/mastodon/models/account.ts +++ b/app/javascript/mastodon/models/account.ts @@ -67,6 +67,7 @@ export const accountDefaultValues: AccountShape = { bot: false, created_at: '', discoverable: false, + indexable: false, display_name: '', display_name_html: '', emojis: List<CustomEmoji>(), diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index c8cfe46a8..9f87352f5 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2552,7 +2552,7 @@ $ui-header-height: 55px; .column-title { text-align: center; - padding-bottom: 40px; + padding-bottom: 32px; h3 { font-size: 24px; @@ -2743,58 +2743,6 @@ $ui-header-height: 55px; } } -.onboarding__progress-indicator { - display: flex; - align-items: center; - margin-bottom: 30px; - position: sticky; - background: $ui-base-color; - - @media screen and (width >= 600) { - padding: 0 40px; - } - - &__line { - height: 4px; - flex: 1 1 auto; - background: lighten($ui-base-color, 4%); - } - - &__step { - flex: 0 0 auto; - width: 30px; - height: 30px; - background: lighten($ui-base-color, 4%); - border-radius: 50%; - color: $primary-text-color; - display: flex; - align-items: center; - justify-content: center; - - svg { - width: 15px; - height: auto; - } - - &.active { - background: $valid-value-color; - } - } - - &__step.active, - &__line.active { - background: $valid-value-color; - background-image: linear-gradient( - 90deg, - $valid-value-color, - lighten($valid-value-color, 8%), - $valid-value-color - ); - background-size: 200px 100%; - animation: skeleton 1.2s ease-in-out infinite; - } -} - .follow-recommendations { background: darken($ui-base-color, 4%); border-radius: 8px; @@ -2871,6 +2819,28 @@ $ui-header-height: 55px; } } +.onboarding__profile { + position: relative; + margin-bottom: 40px + 20px; + + .app-form__avatar-input { + border: 2px solid $ui-base-color; + position: absolute; + inset-inline-start: -2px; + bottom: -40px; + z-index: 2; + } + + .app-form__header-input { + margin: 0 -20px; + border-radius: 0; + + img { + border-radius: 0; + } + } +} + .compose-form__highlightable { display: flex; flex-direction: column; @@ -3145,6 +3115,7 @@ $ui-header-height: 55px; cursor: pointer; background-color: transparent; border: 0; + border-radius: 10px; padding: 0; user-select: none; -webkit-tap-highlight-color: rgba($base-overlay-background, 0); @@ -3169,81 +3140,41 @@ $ui-header-height: 55px; } .react-toggle-track { - width: 50px; - height: 24px; + width: 32px; + height: 20px; padding: 0; - border-radius: 30px; - background-color: $ui-base-color; - transition: background-color 0.2s ease; + border-radius: 10px; + background-color: #626982; } -.react-toggle:is(:hover, :focus-within):not(.react-toggle--disabled) - .react-toggle-track { - background-color: darken($ui-base-color, 10%); +.react-toggle--focus { + outline: $ui-button-focus-outline; } .react-toggle--checked .react-toggle-track { - background-color: darken($ui-highlight-color, 2%); -} - -.react-toggle--checked:is(:hover, :focus-within):not(.react-toggle--disabled) - .react-toggle-track { background-color: $ui-highlight-color; } -.react-toggle-track-check { - position: absolute; - width: 14px; - height: 10px; - top: 0; - bottom: 0; - margin-top: auto; - margin-bottom: auto; - line-height: 0; - inset-inline-start: 8px; - opacity: 0; - transition: opacity 0.25s ease; -} - -.react-toggle--checked .react-toggle-track-check { - opacity: 1; - transition: opacity 0.25s ease; -} - +.react-toggle-track-check, .react-toggle-track-x { - position: absolute; - width: 10px; - height: 10px; - top: 0; - bottom: 0; - margin-top: auto; - margin-bottom: auto; - line-height: 0; - inset-inline-end: 10px; - opacity: 1; - transition: opacity 0.25s ease; -} - -.react-toggle--checked .react-toggle-track-x { - opacity: 0; + display: none; } .react-toggle-thumb { position: absolute; - top: 1px; - inset-inline-start: 1px; - width: 22px; - height: 22px; - border: 1px solid $ui-base-color; + top: 2px; + inset-inline-start: 2px; + width: 16px; + height: 16px; border-radius: 50%; - background-color: darken($simple-background-color, 2%); + background-color: $primary-text-color; box-sizing: border-box; transition: all 0.25s ease; transition-property: border-color, left; } .react-toggle--checked .react-toggle-thumb { - inset-inline-start: 27px; + inset-inline-start: 32px - 16px - 2px; border-color: $ui-highlight-color; } @@ -4066,6 +3997,17 @@ a.status-card { justify-content: center; } +.button .loading-indicator { + position: static; + transform: none; + + .circular-progress { + color: $primary-text-color; + width: 22px; + height: 22px; + } +} + .circular-progress { color: lighten($ui-base-color, 26%); animation: 1.4s linear 0s infinite normal none running simple-rotate; @@ -5799,12 +5741,14 @@ a.status-card { &__toggle { display: flex; align-items: center; - margin-bottom: 10px; + margin-bottom: 16px; + gap: 8px; & > span { - font-size: 17px; + display: block; + font-size: 14px; font-weight: 500; - margin-inline-start: 10px; + line-height: 20px; } } diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index 0f8eecee0..e72a01936 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -36,7 +36,7 @@ code { } .input { - margin-bottom: 15px; + margin-bottom: 16px; overflow: hidden; &.hidden { @@ -266,12 +266,13 @@ code { font-size: 14px; color: $primary-text-color; display: block; - font-weight: 500; - padding-top: 5px; + font-weight: 600; + line-height: 20px; } .hint { - margin-bottom: 15px; + line-height: 16px; + margin-bottom: 12px; } ul { @@ -427,7 +428,8 @@ code { input[type='datetime-local'], textarea { box-sizing: border-box; - font-size: 16px; + font-size: 14px; + line-height: 20px; color: $primary-text-color; display: block; width: 100%; @@ -435,9 +437,9 @@ code { font-family: inherit; resize: vertical; background: darken($ui-base-color, 10%); - border: 1px solid darken($ui-base-color, 14%); - border-radius: 4px; - padding: 10px; + border: 1px solid darken($ui-base-color, 10%); + border-radius: 8px; + padding: 10px 16px; &::placeholder { color: lighten($darker-text-color, 4%); @@ -451,14 +453,13 @@ code { border-color: $valid-value-color; } - &:hover { - border-color: darken($ui-base-color, 20%); - } - &:active, &:focus { border-color: $highlight-text-color; - background: darken($ui-base-color, 8%); + } + + @media screen and (width <= 600px) { + font-size: 16px; } } @@ -524,12 +525,11 @@ code { border-radius: 4px; background: $ui-button-background-color; color: $ui-button-color; - font-size: 18px; - line-height: inherit; + font-size: 15px; + line-height: 22px; height: auto; - padding: 10px; + padding: 7px 18px; text-decoration: none; - text-transform: uppercase; text-align: center; box-sizing: border-box; cursor: pointer; @@ -1220,3 +1220,74 @@ code { background: $highlight-text-color; } } + +.app-form { + & > * { + margin-bottom: 16px; + } + + &__avatar-input, + &__header-input { + display: block; + border-radius: 8px; + background: var(--dropdown-background-color); + position: relative; + cursor: pointer; + + img { + position: absolute; + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 8px; + z-index: 0; + } + + .icon { + position: absolute; + inset-inline-start: 50%; + top: 50%; + transform: translate(-50%, -50%); + color: $darker-text-color; + z-index: 3; + } + + &.selected .icon { + color: $primary-text-color; + transform: none; + inset-inline-start: auto; + inset-inline-end: 8px; + top: auto; + bottom: 8px; + } + + &.invalid img { + outline: 1px solid $error-value-color; + outline-offset: -1px; + } + + &.invalid::before { + display: block; + content: ''; + width: 100%; + height: 100%; + position: absolute; + background: rgba($error-value-color, 0.25); + z-index: 2; + border-radius: 8px; + } + + &:hover { + background-color: var(--dropdown-border-color); + } + } + + &__avatar-input { + width: 80px; + height: 80px; + } + + &__header-input { + aspect-ratio: 580/193; + } +} diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb index 8c6520b30..5d1292a6b 100644 --- a/app/serializers/rest/account_serializer.rb +++ b/app/serializers/rest/account_serializer.rb @@ -6,7 +6,7 @@ class REST::AccountSerializer < ActiveModel::Serializer # Please update `app/javascript/mastodon/api_types/accounts.ts` when making changes to the attributes - attributes :id, :username, :acct, :display_name, :locked, :bot, :discoverable, :group, :created_at, + attributes :id, :username, :acct, :display_name, :locked, :bot, :discoverable, :indexable, :group, :created_at, :note, :url, :uri, :avatar, :avatar_static, :header, :header_static, :followers_count, :following_count, :statuses_count, :last_status_at, :hide_collections @@ -112,6 +112,10 @@ class REST::AccountSerializer < ActiveModel::Serializer object.suspended? ? false : object.discoverable end + def indexable + object.suspended? ? false : object.indexable + end + def moved_to_account object.suspended? ? nil : AccountDecorator.new(object.moved_to_account) end diff --git a/config/routes.rb b/config/routes.rb index 82431f6ec..150b26cf1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -31,7 +31,7 @@ Rails.application.routes.draw do /favourites /bookmarks /pinned - /start + /start/(*any) /directory /explore/(*any) /search From fe58ac8d9f1b0c4347fde451f1caedac2ac605bc Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Wed, 15 Nov 2023 08:14:51 -0500 Subject: [PATCH 38/63] Improve spec coverage for `api/web/push_subscriptions` controller (#27858) Co-authored-by: Claire <claire.github-309c@sitedethib.com> --- .../api/web/push_subscriptions_controller.rb | 78 ++++++++++++------- .../web/push_subscriptions_controller_spec.rb | 60 ++++++++------ 2 files changed, 86 insertions(+), 52 deletions(-) diff --git a/app/controllers/api/web/push_subscriptions_controller.rb b/app/controllers/api/web/push_subscriptions_controller.rb index 5167928e9..167d16fc4 100644 --- a/app/controllers/api/web/push_subscriptions_controller.rb +++ b/app/controllers/api/web/push_subscriptions_controller.rb @@ -3,37 +3,13 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController before_action :require_user! before_action :set_push_subscription, only: :update + before_action :destroy_previous_subscriptions, only: :create, if: :prior_subscriptions? + after_action :update_session_with_subscription, only: :create def create - active_session = current_session + @push_subscription = ::Web::PushSubscription.create!(web_push_subscription_params) - unless active_session.web_push_subscription.nil? - active_session.web_push_subscription.destroy! - active_session.update!(web_push_subscription: nil) - end - - # Mobile devices do not support regular notifications, so we enable push notifications by default - alerts_enabled = active_session.detection.device.mobile? || active_session.detection.device.tablet? - - data = { - policy: 'all', - alerts: Notification::TYPES.index_with { alerts_enabled }, - } - - data.deep_merge!(data_params) if params[:data] - - push_subscription = ::Web::PushSubscription.create!( - endpoint: subscription_params[:endpoint], - key_p256dh: subscription_params[:keys][:p256dh], - key_auth: subscription_params[:keys][:auth], - data: data, - user_id: active_session.user_id, - access_token_id: active_session.access_token_id - ) - - active_session.update!(web_push_subscription: push_subscription) - - render json: push_subscription, serializer: REST::WebPushSubscriptionSerializer + render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer end def update @@ -43,6 +19,41 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController private + def active_session + @active_session ||= current_session + end + + def destroy_previous_subscriptions + active_session.web_push_subscription.destroy! + active_session.update!(web_push_subscription: nil) + end + + def prior_subscriptions? + active_session.web_push_subscription.present? + end + + def subscription_data + default_subscription_data.tap do |data| + data.deep_merge!(data_params) if params[:data] + end + end + + def default_subscription_data + { + policy: 'all', + alerts: Notification::TYPES.index_with { alerts_enabled }, + } + end + + def alerts_enabled + # Mobile devices do not support regular notifications, so we enable push notifications by default + active_session.detection.device.mobile? || active_session.detection.device.tablet? + end + + def update_session_with_subscription + active_session.update!(web_push_subscription: @push_subscription) + end + def set_push_subscription @push_subscription = ::Web::PushSubscription.find(params[:id]) end @@ -51,6 +62,17 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController @subscription_params ||= params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh]) end + def web_push_subscription_params + { + access_token_id: active_session.access_token_id, + data: subscription_data, + endpoint: subscription_params[:endpoint], + key_auth: subscription_params[:keys][:auth], + key_p256dh: subscription_params[:keys][:p256dh], + user_id: active_session.user_id, + } + end + def data_params @data_params ||= params.require(:data).permit(:policy, alerts: Notification::TYPES) end diff --git a/spec/controllers/api/web/push_subscriptions_controller_spec.rb b/spec/controllers/api/web/push_subscriptions_controller_spec.rb index 9f027ede9..58677841c 100644 --- a/spec/controllers/api/web/push_subscriptions_controller_spec.rb +++ b/spec/controllers/api/web/push_subscriptions_controller_spec.rb @@ -37,37 +37,49 @@ describe Api::Web::PushSubscriptionsController do } end + before do + sign_in(user) + + stub_request(:post, create_payload[:subscription][:endpoint]).to_return(status: 200) + end + describe 'POST #create' do it 'saves push subscriptions' do - sign_in(user) - - stub_request(:post, create_payload[:subscription][:endpoint]).to_return(status: 200) - post :create, format: :json, params: create_payload + expect(response).to have_http_status(200) + user.reload - push_subscription = Web::PushSubscription.find_by(endpoint: create_payload[:subscription][:endpoint]) + expect(created_push_subscription).to have_attributes( + endpoint: eq(create_payload[:subscription][:endpoint]), + key_p256dh: eq(create_payload[:subscription][:keys][:p256dh]), + key_auth: eq(create_payload[:subscription][:keys][:auth]) + ) + expect(user.session_activations.first.web_push_subscription).to eq(created_push_subscription) + end - expect(push_subscription['endpoint']).to eq(create_payload[:subscription][:endpoint]) - expect(push_subscription['key_p256dh']).to eq(create_payload[:subscription][:keys][:p256dh]) - expect(push_subscription['key_auth']).to eq(create_payload[:subscription][:keys][:auth]) + context 'with a user who has a session with a prior subscription' do + let!(:prior_subscription) { Fabricate(:web_push_subscription, session_activation: user.session_activations.last) } + + it 'destroys prior subscription when creating new one' do + post :create, format: :json, params: create_payload + + expect(response).to have_http_status(200) + expect { prior_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound) + end end context 'with initial data' do it 'saves alert settings' do - sign_in(user) - - stub_request(:post, create_payload[:subscription][:endpoint]).to_return(status: 200) - post :create, format: :json, params: create_payload.merge(alerts_payload) - push_subscription = Web::PushSubscription.find_by(endpoint: create_payload[:subscription][:endpoint]) + expect(response).to have_http_status(200) - expect(push_subscription.data['policy']).to eq 'all' + expect(created_push_subscription.data['policy']).to eq 'all' %w(follow follow_request favourite reblog mention poll status).each do |type| - expect(push_subscription.data['alerts'][type]).to eq(alerts_payload[:data][:alerts][type.to_sym].to_s) + expect(created_push_subscription.data['alerts'][type]).to eq(alerts_payload[:data][:alerts][type.to_sym].to_s) end end end @@ -75,23 +87,23 @@ describe Api::Web::PushSubscriptionsController do describe 'PUT #update' do it 'changes alert settings' do - sign_in(user) - - stub_request(:post, create_payload[:subscription][:endpoint]).to_return(status: 200) - post :create, format: :json, params: create_payload - alerts_payload[:id] = Web::PushSubscription.find_by(endpoint: create_payload[:subscription][:endpoint]).id + expect(response).to have_http_status(200) + + alerts_payload[:id] = created_push_subscription.id put :update, format: :json, params: alerts_payload - push_subscription = Web::PushSubscription.find_by(endpoint: create_payload[:subscription][:endpoint]) - - expect(push_subscription.data['policy']).to eq 'all' + expect(created_push_subscription.data['policy']).to eq 'all' %w(follow follow_request favourite reblog mention poll status).each do |type| - expect(push_subscription.data['alerts'][type]).to eq(alerts_payload[:data][:alerts][type.to_sym].to_s) + expect(created_push_subscription.data['alerts'][type]).to eq(alerts_payload[:data][:alerts][type.to_sym].to_s) end end end + + def created_push_subscription + Web::PushSubscription.find_by(endpoint: create_payload[:subscription][:endpoint]) + end end From 12a5b7391d0298a99b94386538fe4ca625cea7e9 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Wed, 15 Nov 2023 08:21:18 -0500 Subject: [PATCH 39/63] Add spec for well known change password endpoint (#27856) --- spec/requests/well_known/change_password_spec.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 spec/requests/well_known/change_password_spec.rb diff --git a/spec/requests/well_known/change_password_spec.rb b/spec/requests/well_known/change_password_spec.rb new file mode 100644 index 000000000..04134b71f --- /dev/null +++ b/spec/requests/well_known/change_password_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'The /.well-known/change-password request' do + it 'redirects to the change password page' do + get '/.well-known/change-password' + + expect(response).to redirect_to '/auth/edit' + end +end From bd575a1dd69d87ca0f69873f7badf28d38e8b9ed Mon Sep 17 00:00:00 2001 From: Claire <claire.github-309c@sitedethib.com> Date: Wed, 15 Nov 2023 15:18:43 +0100 Subject: [PATCH 40/63] Add banner for forwarded reports made by remote users about remote content (#27549) --- app/views/admin/reports/_header_details.html.haml | 2 +- app/views/admin/reports/show.html.haml | 3 +++ config/locales/en.yml | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/views/admin/reports/_header_details.html.haml b/app/views/admin/reports/_header_details.html.haml index 5878cd2ff..45790b9cd 100644 --- a/app/views/admin/reports/_header_details.html.haml +++ b/app/views/admin/reports/_header_details.html.haml @@ -22,7 +22,7 @@ = t('admin.reports.resolved') - else = t('admin.reports.unresolved') - - unless report.target_account.local? + - if report.account.local? && !report.target_account.local? .report-header__details__item .report-header__details__item__header %strong= t('admin.reports.forwarded') diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml index 13a4d4834..4376e5af4 100644 --- a/app/views/admin/reports/show.html.haml +++ b/app/views/admin/reports/show.html.haml @@ -7,6 +7,9 @@ - else = link_to t('admin.reports.mark_as_unresolved'), reopen_admin_report_path(@report), method: :post, class: 'button' +- unless @report.account.local? || @report.target_account.local? + .flash-message= t('admin.reports.forwarded_replies_explanation') + .report-header = render 'admin/reports/header_card', report: @report = render 'admin/reports/header_details', report: @report diff --git a/config/locales/en.yml b/config/locales/en.yml index 7319de53d..057f7a584 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -611,6 +611,7 @@ en: created_at: Reported delete_and_resolve: Delete posts forwarded: Forwarded + forwarded_replies_explanation: This report is from a remote user and about remote content. It has been forwarded to you because the reported content is in reply to one of your users. forwarded_to: Forwarded to %{domain} mark_as_resolved: Mark as resolved mark_as_sensitive: Mark as sensitive From 3de91456132ffadf5b98848409fa2a0377a3bef6 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Wed, 15 Nov 2023 14:12:02 -0500 Subject: [PATCH 41/63] Move controller specs for `well-known` endpoints to request specs (#27855) --- .../well_known/host_meta_controller_spec.rb | 22 -- .../well_known/node_info_controller_spec.rb | 41 --- .../well_known/webfinger_controller_spec.rb | 235 ---------------- spec/requests/host_meta_request_spec.rb | 14 - spec/requests/webfinger_request_spec.rb | 33 --- spec/requests/well_known/host_meta_spec.rb | 27 ++ spec/requests/well_known/node_info_spec.rb | 58 ++++ spec/requests/well_known/webfinger_spec.rb | 255 ++++++++++++++++++ 8 files changed, 340 insertions(+), 345 deletions(-) delete mode 100644 spec/controllers/well_known/host_meta_controller_spec.rb delete mode 100644 spec/controllers/well_known/node_info_controller_spec.rb delete mode 100644 spec/controllers/well_known/webfinger_controller_spec.rb delete mode 100644 spec/requests/host_meta_request_spec.rb delete mode 100644 spec/requests/webfinger_request_spec.rb create mode 100644 spec/requests/well_known/host_meta_spec.rb create mode 100644 spec/requests/well_known/node_info_spec.rb create mode 100644 spec/requests/well_known/webfinger_spec.rb diff --git a/spec/controllers/well_known/host_meta_controller_spec.rb b/spec/controllers/well_known/host_meta_controller_spec.rb deleted file mode 100644 index 4bd161cd9..000000000 --- a/spec/controllers/well_known/host_meta_controller_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe WellKnown::HostMetaController do - render_views - - describe 'GET #show' do - it 'returns http success' do - get :show, format: :xml - - expect(response).to have_http_status(200) - expect(response.media_type).to eq 'application/xrd+xml' - expect(response.body).to eq <<~XML - <?xml version="1.0" encoding="UTF-8"?> - <XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"> - <Link rel="lrdd" template="https://cb6e6126.ngrok.io/.well-known/webfinger?resource={uri}"/> - </XRD> - XML - end - end -end diff --git a/spec/controllers/well_known/node_info_controller_spec.rb b/spec/controllers/well_known/node_info_controller_spec.rb deleted file mode 100644 index 6ec34afd0..000000000 --- a/spec/controllers/well_known/node_info_controller_spec.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe WellKnown::NodeInfoController do - render_views - - describe 'GET #index' do - it 'returns json document pointing to node info' do - get :index - - expect(response).to have_http_status(200) - expect(response.media_type).to eq 'application/json' - - json = body_as_json - - expect(json[:links]).to be_an Array - expect(json[:links][0][:rel]).to eq 'http://nodeinfo.diaspora.software/ns/schema/2.0' - expect(json[:links][0][:href]).to include 'nodeinfo/2.0' - end - end - - describe 'GET #show' do - it 'returns json document with node info properties' do - get :show - - expect(response).to have_http_status(200) - expect(response.media_type).to eq 'application/json' - - json = body_as_json - foo = { 'foo' => 0 } - - expect(foo).to_not match_json_schema('nodeinfo_2.0') - expect(json).to match_json_schema('nodeinfo_2.0') - expect(json[:version]).to eq '2.0' - expect(json[:usage]).to be_a Hash - expect(json[:software]).to be_a Hash - expect(json[:protocols]).to be_an Array - end - end -end diff --git a/spec/controllers/well_known/webfinger_controller_spec.rb b/spec/controllers/well_known/webfinger_controller_spec.rb deleted file mode 100644 index 6610f4d13..000000000 --- a/spec/controllers/well_known/webfinger_controller_spec.rb +++ /dev/null @@ -1,235 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe WellKnown::WebfingerController do - include RoutingHelper - - render_views - - describe 'GET #show' do - subject(:perform_show!) do - get :show, params: { resource: resource }, format: :json - end - - let(:alternate_domains) { [] } - let(:alice) { Fabricate(:account, username: 'alice') } - let(:resource) { nil } - - around do |example| - tmp = Rails.configuration.x.alternate_domains - Rails.configuration.x.alternate_domains = alternate_domains - example.run - Rails.configuration.x.alternate_domains = tmp - end - - shared_examples 'a successful response' do - it 'returns http success' do - expect(response).to have_http_status(200) - end - - it 'does not set a Vary header' do - expect(response.headers['Vary']).to be_nil - end - - it 'returns application/jrd+json' do - expect(response.media_type).to eq 'application/jrd+json' - end - - it 'returns links for the account' do - json = body_as_json - expect(json[:subject]).to eq 'acct:alice@cb6e6126.ngrok.io' - expect(json[:aliases]).to include('https://cb6e6126.ngrok.io/@alice', 'https://cb6e6126.ngrok.io/users/alice') - end - end - - context 'when an account exists' do - let(:resource) { alice.to_webfinger_s } - - before do - perform_show! - end - - it_behaves_like 'a successful response' - end - - context 'when an account is temporarily suspended' do - let(:resource) { alice.to_webfinger_s } - - before do - alice.suspend! - perform_show! - end - - it_behaves_like 'a successful response' - end - - context 'when an account is permanently suspended or deleted' do - let(:resource) { alice.to_webfinger_s } - - before do - alice.suspend! - alice.deletion_request.destroy - perform_show! - end - - it 'returns http gone' do - expect(response).to have_http_status(410) - end - end - - context 'when an account is not found' do - let(:resource) { 'acct:not@existing.com' } - - before do - perform_show! - end - - it 'returns http not found' do - expect(response).to have_http_status(404) - end - end - - context 'with an alternate domain' do - let(:alternate_domains) { ['foo.org'] } - - before do - perform_show! - end - - context 'when an account exists' do - let(:resource) do - username, = alice.to_webfinger_s.split('@') - "#{username}@foo.org" - end - - it_behaves_like 'a successful response' - end - - context 'when the domain is wrong' do - let(:resource) do - username, = alice.to_webfinger_s.split('@') - "#{username}@bar.org" - end - - it 'returns http not found' do - expect(response).to have_http_status(404) - end - end - end - - context 'when the old name scheme is used to query the instance actor' do - let(:resource) do - "#{Rails.configuration.x.local_domain}@#{Rails.configuration.x.local_domain}" - end - - before do - perform_show! - end - - it 'returns http success' do - expect(response).to have_http_status(200) - end - - it 'does not set a Vary header' do - expect(response.headers['Vary']).to be_nil - end - - it 'returns application/jrd+json' do - expect(response.media_type).to eq 'application/jrd+json' - end - - it 'returns links for the internal account' do - json = body_as_json - expect(json[:subject]).to eq 'acct:mastodon.internal@cb6e6126.ngrok.io' - expect(json[:aliases]).to eq ['https://cb6e6126.ngrok.io/actor'] - end - end - - context 'with no resource parameter' do - let(:resource) { nil } - - before do - perform_show! - end - - it 'returns http bad request' do - expect(response).to have_http_status(400) - end - end - - context 'with a nonsense parameter' do - let(:resource) { 'df/:dfkj' } - - before do - perform_show! - end - - it 'returns http bad request' do - expect(response).to have_http_status(400) - end - end - - context 'when an account has an avatar' do - let(:alice) { Fabricate(:account, username: 'alice', avatar: attachment_fixture('attachment.jpg')) } - let(:resource) { alice.to_webfinger_s } - - it 'returns avatar in response' do - perform_show! - - avatar_link = get_avatar_link(body_as_json) - expect(avatar_link).to_not be_nil - expect(avatar_link[:type]).to eq alice.avatar.content_type - expect(avatar_link[:href]).to eq full_asset_url(alice.avatar) - end - - context 'with limited federation mode' do - before do - allow(Rails.configuration.x).to receive(:limited_federation_mode).and_return(true) - end - - it 'does not return avatar in response' do - perform_show! - - avatar_link = get_avatar_link(body_as_json) - expect(avatar_link).to be_nil - end - end - - context 'when enabling DISALLOW_UNAUTHENTICATED_API_ACCESS' do - around do |example| - ClimateControl.modify DISALLOW_UNAUTHENTICATED_API_ACCESS: 'true' do - example.run - end - end - - it 'does not return avatar in response' do - perform_show! - - avatar_link = get_avatar_link(body_as_json) - expect(avatar_link).to be_nil - end - end - end - - context 'when an account does not have an avatar' do - let(:alice) { Fabricate(:account, username: 'alice', avatar: nil) } - let(:resource) { alice.to_webfinger_s } - - before do - perform_show! - end - - it 'does not return avatar in response' do - avatar_link = get_avatar_link(body_as_json) - expect(avatar_link).to be_nil - end - end - end - - private - - def get_avatar_link(json) - json[:links].find { |link| link[:rel] == 'http://webfinger.net/rel/avatar' } - end -end diff --git a/spec/requests/host_meta_request_spec.rb b/spec/requests/host_meta_request_spec.rb deleted file mode 100644 index ec26ecba7..000000000 --- a/spec/requests/host_meta_request_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe 'The host_meta route' do - describe 'requested without accepts headers' do - it 'returns an xml response' do - get host_meta_url - - expect(response).to have_http_status(200) - expect(response.media_type).to eq 'application/xrd+xml' - end - end -end diff --git a/spec/requests/webfinger_request_spec.rb b/spec/requests/webfinger_request_spec.rb deleted file mode 100644 index 68a1478be..000000000 --- a/spec/requests/webfinger_request_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe 'The webfinger route' do - let(:alice) { Fabricate(:account, username: 'alice') } - - describe 'requested with standard accepts headers' do - it 'returns a json response' do - get webfinger_url(resource: alice.to_webfinger_s) - - expect(response).to have_http_status(200) - expect(response.media_type).to eq 'application/jrd+json' - end - end - - describe 'asking for json format' do - it 'returns a json response for json format' do - get webfinger_url(resource: alice.to_webfinger_s, format: :json) - - expect(response).to have_http_status(200) - expect(response.media_type).to eq 'application/jrd+json' - end - - it 'returns a json response for json accept header' do - headers = { 'HTTP_ACCEPT' => 'application/jrd+json' } - get webfinger_url(resource: alice.to_webfinger_s), headers: headers - - expect(response).to have_http_status(200) - expect(response.media_type).to eq 'application/jrd+json' - end - end -end diff --git a/spec/requests/well_known/host_meta_spec.rb b/spec/requests/well_known/host_meta_spec.rb new file mode 100644 index 000000000..ca10a51a0 --- /dev/null +++ b/spec/requests/well_known/host_meta_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'The /.well-known/host-meta request' do + it 'returns http success with valid XML response' do + get '/.well-known/host-meta' + + expect(response) + .to have_http_status(200) + .and have_attributes( + media_type: 'application/xrd+xml', + body: host_meta_xml_template + ) + end + + private + + def host_meta_xml_template + <<~XML + <?xml version="1.0" encoding="UTF-8"?> + <XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"> + <Link rel="lrdd" template="https://cb6e6126.ngrok.io/.well-known/webfinger?resource={uri}"/> + </XRD> + XML + end +end diff --git a/spec/requests/well_known/node_info_spec.rb b/spec/requests/well_known/node_info_spec.rb new file mode 100644 index 000000000..0934b0fde --- /dev/null +++ b/spec/requests/well_known/node_info_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'The well-known node-info endpoints' do + describe 'The /.well-known/node-info endpoint' do + it 'returns JSON document pointing to node info' do + get '/.well-known/nodeinfo' + + expect(response) + .to have_http_status(200) + .and have_attributes( + media_type: 'application/json' + ) + + expect(body_as_json).to include( + links: be_an(Array).and( + contain_exactly( + include( + rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0', + href: include('nodeinfo/2.0') + ) + ) + ) + ) + end + end + + describe 'The /nodeinfo/2.0 endpoint' do + it 'returns JSON document with node info properties' do + get '/nodeinfo/2.0' + + expect(response) + .to have_http_status(200) + .and have_attributes( + media_type: 'application/json' + ) + + expect(non_matching_hash) + .to_not match_json_schema('nodeinfo_2.0') + + expect(body_as_json) + .to match_json_schema('nodeinfo_2.0') + .and include( + version: '2.0', + usage: be_a(Hash), + software: be_a(Hash), + protocols: be_a(Array) + ) + end + + private + + def non_matching_hash + { 'foo' => 0 } + end + end +end diff --git a/spec/requests/well_known/webfinger_spec.rb b/spec/requests/well_known/webfinger_spec.rb new file mode 100644 index 000000000..779f1bba5 --- /dev/null +++ b/spec/requests/well_known/webfinger_spec.rb @@ -0,0 +1,255 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'The /.well-known/webfinger endpoint' do + subject(:perform_request!) { get webfinger_url(resource: resource) } + + let(:alternate_domains) { [] } + let(:alice) { Fabricate(:account, username: 'alice') } + let(:resource) { nil } + + around do |example| + tmp = Rails.configuration.x.alternate_domains + Rails.configuration.x.alternate_domains = alternate_domains + example.run + Rails.configuration.x.alternate_domains = tmp + end + + shared_examples 'a successful response' do + it 'returns http success' do + expect(response).to have_http_status(200) + end + + it 'sets only a Vary Origin header' do + expect(response.headers['Vary']).to eq('Origin') + end + + it 'returns application/jrd+json' do + expect(response.media_type).to eq 'application/jrd+json' + end + + it 'returns links for the account' do + json = body_as_json + expect(json[:subject]).to eq 'acct:alice@cb6e6126.ngrok.io' + expect(json[:aliases]).to include('https://cb6e6126.ngrok.io/@alice', 'https://cb6e6126.ngrok.io/users/alice') + end + end + + context 'when an account exists' do + let(:resource) { alice.to_webfinger_s } + + before do + perform_request! + end + + it_behaves_like 'a successful response' + end + + context 'when an account is temporarily suspended' do + let(:resource) { alice.to_webfinger_s } + + before do + alice.suspend! + perform_request! + end + + it_behaves_like 'a successful response' + end + + context 'when an account is permanently suspended or deleted' do + let(:resource) { alice.to_webfinger_s } + + before do + alice.suspend! + alice.deletion_request.destroy + perform_request! + end + + it 'returns http gone' do + expect(response).to have_http_status(410) + end + end + + context 'when an account is not found' do + let(:resource) { 'acct:not@existing.com' } + + before do + perform_request! + end + + it 'returns http not found' do + expect(response).to have_http_status(404) + end + end + + context 'with an alternate domain' do + let(:alternate_domains) { ['foo.org'] } + + before do + perform_request! + end + + context 'when an account exists' do + let(:resource) do + username, = alice.to_webfinger_s.split('@') + "#{username}@foo.org" + end + + it_behaves_like 'a successful response' + end + + context 'when the domain is wrong' do + let(:resource) do + username, = alice.to_webfinger_s.split('@') + "#{username}@bar.org" + end + + it 'returns http not found' do + expect(response).to have_http_status(404) + end + end + end + + context 'when the old name scheme is used to query the instance actor' do + let(:resource) do + "#{Rails.configuration.x.local_domain}@#{Rails.configuration.x.local_domain}" + end + + before do + perform_request! + end + + it 'returns http success' do + expect(response).to have_http_status(200) + end + + it 'sets only a Vary Origin header' do + expect(response.headers['Vary']).to eq('Origin') + end + + it 'returns application/jrd+json' do + expect(response.media_type).to eq 'application/jrd+json' + end + + it 'returns links for the internal account' do + json = body_as_json + expect(json[:subject]).to eq 'acct:mastodon.internal@cb6e6126.ngrok.io' + expect(json[:aliases]).to eq ['https://cb6e6126.ngrok.io/actor'] + end + end + + context 'with no resource parameter' do + let(:resource) { nil } + + before do + perform_request! + end + + it 'returns http bad request' do + expect(response).to have_http_status(400) + end + end + + context 'with a nonsense parameter' do + let(:resource) { 'df/:dfkj' } + + before do + perform_request! + end + + it 'returns http bad request' do + expect(response).to have_http_status(400) + end + end + + context 'when an account has an avatar' do + let(:alice) { Fabricate(:account, username: 'alice', avatar: attachment_fixture('attachment.jpg')) } + let(:resource) { alice.to_webfinger_s } + + it 'returns avatar in response' do + perform_request! + + avatar_link = get_avatar_link(body_as_json) + expect(avatar_link).to_not be_nil + expect(avatar_link[:type]).to eq alice.avatar.content_type + expect(avatar_link[:href]).to eq Addressable::URI.new(host: Rails.configuration.x.local_domain, path: alice.avatar.to_s, scheme: 'https').to_s + end + + context 'with limited federation mode' do + before do + allow(Rails.configuration.x).to receive(:limited_federation_mode).and_return(true) + end + + it 'does not return avatar in response' do + perform_request! + + avatar_link = get_avatar_link(body_as_json) + expect(avatar_link).to be_nil + end + end + + context 'when enabling DISALLOW_UNAUTHENTICATED_API_ACCESS' do + around do |example| + ClimateControl.modify DISALLOW_UNAUTHENTICATED_API_ACCESS: 'true' do + example.run + end + end + + it 'does not return avatar in response' do + perform_request! + + avatar_link = get_avatar_link(body_as_json) + expect(avatar_link).to be_nil + end + end + end + + context 'when an account does not have an avatar' do + let(:alice) { Fabricate(:account, username: 'alice', avatar: nil) } + let(:resource) { alice.to_webfinger_s } + + before do + perform_request! + end + + it 'does not return avatar in response' do + avatar_link = get_avatar_link(body_as_json) + expect(avatar_link).to be_nil + end + end + + context 'with different headers' do + describe 'requested with standard accepts headers' do + it 'returns a json response' do + get webfinger_url(resource: alice.to_webfinger_s) + + expect(response).to have_http_status(200) + expect(response.media_type).to eq 'application/jrd+json' + end + end + + describe 'asking for json format' do + it 'returns a json response for json format' do + get webfinger_url(resource: alice.to_webfinger_s, format: :json) + + expect(response).to have_http_status(200) + expect(response.media_type).to eq 'application/jrd+json' + end + + it 'returns a json response for json accept header' do + headers = { 'HTTP_ACCEPT' => 'application/jrd+json' } + get webfinger_url(resource: alice.to_webfinger_s), headers: headers + + expect(response).to have_http_status(200) + expect(response.media_type).to eq 'application/jrd+json' + end + end + end + + private + + def get_avatar_link(json) + json[:links].find { |link| link[:rel] == 'http://webfinger.net/rel/avatar' } + end +end From 04121bd02059b8246233d0421349005d75a6b1a1 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Wed, 15 Nov 2023 18:09:31 -0500 Subject: [PATCH 42/63] Disable simplecov `enable_coverage_for_eval` option, move to standalone file (#27869) --- .simplecov | 22 ++++++++++++++++++++++ spec/spec_helper.rb | 25 ++----------------------- 2 files changed, 24 insertions(+), 23 deletions(-) create mode 100644 .simplecov diff --git a/.simplecov b/.simplecov new file mode 100644 index 000000000..fbd0207be --- /dev/null +++ b/.simplecov @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +if ENV['CI'] + require 'simplecov-lcov' + SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true + SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter +else + SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter +end + +SimpleCov.start 'rails' do + enable_coverage :branch + + add_filter 'lib/linter' + + add_group 'Libraries', 'lib' + add_group 'Policies', 'app/policies' + add_group 'Presenters', 'app/presenters' + add_group 'Serializers', 'app/serializers' + add_group 'Services', 'app/services' + add_group 'Validators', 'app/validators' +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0bb4f88cf..dc60976d0 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,28 +1,7 @@ # frozen_string_literal: true -if ENV['DISABLE_SIMPLECOV'] != 'true' - require 'simplecov' - - if ENV['CI'] - require 'simplecov-lcov' - SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true - SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter - else - SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter - end - SimpleCov.start 'rails' do - enable_coverage :branch - enable_coverage_for_eval - - add_filter 'lib/linter' - add_group 'Policies', 'app/policies' - add_group 'Presenters', 'app/presenters' - add_group 'Serializers', 'app/serializers' - add_group 'Services', 'app/services' - add_group 'Validators', 'app/validators' - - add_group 'Libraries', 'lib' - end +unless ENV['DISABLE_SIMPLECOV'] == 'true' + require 'simplecov' # Configuration details loaded from .simplecov end RSpec.configure do |config| From 91a05f3cad55fa3932e8364db1a87becd2d6f14c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 10:33:54 +0100 Subject: [PATCH 43/63] Update libretranslate/libretranslate Docker tag to v1.5.2 (#27716) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .devcontainer/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 137bebc59..40dc72c12 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -70,7 +70,7 @@ services: hard: -1 libretranslate: - image: libretranslate/libretranslate:v1.4.1 + image: libretranslate/libretranslate:v1.5.2 restart: unless-stopped volumes: - lt-data:/home/libretranslate/.local From 669a7157cbc2d950988a80244a1f7cdec5aa59f5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 10:52:23 +0100 Subject: [PATCH 44/63] Update dependency webpack-bundle-analyzer to v4.10.1 (#27885) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index e66f58f8b..32c2546e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17014,8 +17014,8 @@ __metadata: linkType: hard "webpack-bundle-analyzer@npm:^4.8.0": - version: 4.10.0 - resolution: "webpack-bundle-analyzer@npm:4.10.0" + version: 4.10.1 + resolution: "webpack-bundle-analyzer@npm:4.10.1" dependencies: "@discoveryjs/json-ext": "npm:0.5.7" acorn: "npm:^8.0.4" @@ -17032,7 +17032,7 @@ __metadata: ws: "npm:^7.3.1" bin: webpack-bundle-analyzer: lib/bin/analyzer.js - checksum: f812a8d3c0198ce518baf742bff656526f3eae69fb7a64c7f0c9cff202f6fb3380cabf3baaae965b8d6ffbbb6fb802eacb373fca03a596a38b01b84cfb2e8329 + checksum: 6a94c8f6aa03296fb2eb00d6ad3b27bd5c551590fd253772bc61debf3177414d42701014079d4f85c74ba1ca685ae9f0cb4063812b58c21f294d108e9908e5cd languageName: node linkType: hard From c1f93def403e2bdcf3338118a17802a038090a35 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Thu, 16 Nov 2023 04:54:51 -0500 Subject: [PATCH 45/63] Reduce expectations for `RSpec/MultipleExpectations` cop in `controllers/statuses` spec (#27875) --- spec/controllers/statuses_controller_spec.rb | 221 +++++++++++-------- 1 file changed, 134 insertions(+), 87 deletions(-) diff --git a/spec/controllers/statuses_controller_spec.rb b/spec/controllers/statuses_controller_spec.rb index 8b715824b..fe40ee6de 100644 --- a/spec/controllers/statuses_controller_spec.rb +++ b/spec/controllers/statuses_controller_spec.rb @@ -57,11 +57,14 @@ describe StatusesController do let(:format) { 'html' } it 'renders status successfully', :aggregate_failures do - expect(response).to have_http_status(200) - expect(response.headers['Link'].to_s).to include 'activity+json' - expect(response.headers['Vary']).to eq 'Accept, Accept-Language, Cookie' - expect(response.headers['Cache-Control']).to include 'public' - expect(response).to render_template(:show) + expect(response) + .to have_http_status(200) + .and render_template(:show) + expect(response.headers).to include( + 'Vary' => 'Accept, Accept-Language, Cookie', + 'Cache-Control' => include('public'), + 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + ) expect(response.body).to include status.text end end @@ -72,12 +75,15 @@ describe StatusesController do it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie' it 'renders ActivityPub Note object successfully', :aggregate_failures do - expect(response).to have_http_status(200) - expect(response.headers['Link'].to_s).to include 'activity+json' - expect(response.headers['Vary']).to eq 'Accept, Accept-Language, Cookie' - expect(response.headers['Content-Type']).to include 'application/activity+json' - json = body_as_json - expect(json[:content]).to include status.text + expect(response) + .to have_http_status(200) + expect(response.headers).to include( + 'Vary' => 'Accept, Accept-Language, Cookie', + 'Content-Type' => include('application/activity+json'), + 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + ) + expect(body_as_json) + .to include(content: include(status.text)) end end end @@ -157,11 +163,14 @@ describe StatusesController do let(:format) { 'html' } it 'renders status successfully', :aggregate_failures do - expect(response).to have_http_status(200) - expect(response.headers['Link'].to_s).to include 'activity+json' - expect(response.headers['Vary']).to eq 'Accept, Accept-Language, Cookie' - expect(response.headers['Cache-Control']).to include 'private' - expect(response).to render_template(:show) + expect(response) + .to have_http_status(200) + .and render_template(:show) + expect(response.headers).to include( + 'Vary' => 'Accept, Accept-Language, Cookie', + 'Cache-Control' => include('private'), + 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + ) expect(response.body).to include status.text end end @@ -170,13 +179,16 @@ describe StatusesController do let(:format) { 'json' } it 'renders ActivityPub Note object successfully', :aggregate_failures do - expect(response).to have_http_status(200) - expect(response.headers['Link'].to_s).to include 'activity+json' - expect(response.headers['Vary']).to eq 'Accept, Accept-Language, Cookie' - expect(response.headers['Cache-Control']).to include 'private' - expect(response.headers['Content-Type']).to include 'application/activity+json' - json = body_as_json - expect(json[:content]).to include status.text + expect(response) + .to have_http_status(200) + expect(response.headers).to include( + 'Vary' => 'Accept, Accept-Language, Cookie', + 'Cache-Control' => include('private'), + 'Content-Type' => include('application/activity+json'), + 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + ) + expect(body_as_json) + .to include(content: include(status.text)) end end end @@ -194,11 +206,15 @@ describe StatusesController do let(:format) { 'html' } it 'renders status successfully', :aggregate_failures do - expect(response).to have_http_status(200) - expect(response.headers['Link'].to_s).to include 'activity+json' - expect(response.headers['Vary']).to eq 'Accept, Accept-Language, Cookie' - expect(response.headers['Cache-Control']).to include 'private' - expect(response).to render_template(:show) + expect(response) + .to have_http_status(200) + .and render_template(:show) + + expect(response.headers).to include( + 'Vary' => 'Accept, Accept-Language, Cookie', + 'Cache-Control' => include('private'), + 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + ) expect(response.body).to include status.text end end @@ -207,13 +223,16 @@ describe StatusesController do let(:format) { 'json' } it 'renders ActivityPub Note object successfully', :aggregate_failures do - expect(response).to have_http_status(200) - expect(response.headers['Link'].to_s).to include 'activity+json' - expect(response.headers['Vary']).to eq 'Accept, Accept-Language, Cookie' - expect(response.headers['Cache-Control']).to include 'private' - expect(response.headers['Content-Type']).to include 'application/activity+json' - json = body_as_json - expect(json[:content]).to include status.text + expect(response) + .to have_http_status(200) + expect(response.headers).to include( + 'Vary' => 'Accept, Accept-Language, Cookie', + 'Cache-Control' => include('private'), + 'Content-Type' => include('application/activity+json'), + 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + ) + expect(body_as_json) + .to include(content: include(status.text)) end end end @@ -254,11 +273,14 @@ describe StatusesController do let(:format) { 'html' } it 'renders status successfully', :aggregate_failures do - expect(response).to have_http_status(200) - expect(response.headers['Link'].to_s).to include 'activity+json' - expect(response.headers['Vary']).to eq 'Accept, Accept-Language, Cookie' - expect(response.headers['Cache-Control']).to include 'private' - expect(response).to render_template(:show) + expect(response) + .to have_http_status(200) + .and render_template(:show) + expect(response.headers).to include( + 'Vary' => 'Accept, Accept-Language, Cookie', + 'Cache-Control' => include('private'), + 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + ) expect(response.body).to include status.text end end @@ -267,13 +289,16 @@ describe StatusesController do let(:format) { 'json' } it 'renders ActivityPub Note object successfully' do - expect(response).to have_http_status(200) - expect(response.headers['Link'].to_s).to include 'activity+json' - expect(response.headers['Vary']).to eq 'Accept, Accept-Language, Cookie' - expect(response.headers['Cache-Control']).to include 'private' - expect(response.headers['Content-Type']).to include 'application/activity+json' - json = body_as_json - expect(json[:content]).to include status.text + expect(response) + .to have_http_status(200) + expect(response.headers).to include( + 'Vary' => 'Accept, Accept-Language, Cookie', + 'Cache-Control' => include('private'), + 'Content-Type' => include('application/activity+json'), + 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + ) + expect(body_as_json) + .to include(content: include(status.text)) end end end @@ -340,11 +365,14 @@ describe StatusesController do let(:format) { 'html' } it 'renders status successfully', :aggregate_failures do - expect(response).to have_http_status(200) - expect(response.headers['Link'].to_s).to include 'activity+json' - expect(response.headers['Vary']).to eq 'Accept, Accept-Language, Cookie' - expect(response.headers['Cache-Control']).to include 'private' - expect(response).to render_template(:show) + expect(response) + .to have_http_status(200) + .and render_template(:show) + expect(response.headers).to include( + 'Vary' => 'Accept, Accept-Language, Cookie', + 'Cache-Control' => include('private'), + 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + ) expect(response.body).to include status.text end end @@ -355,12 +383,15 @@ describe StatusesController do it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie' it 'renders ActivityPub Note object successfully', :aggregate_failures do - expect(response).to have_http_status(200) - expect(response.headers['Link'].to_s).to include 'activity+json' - expect(response.headers['Vary']).to eq 'Accept, Accept-Language, Cookie' - expect(response.headers['Content-Type']).to include 'application/activity+json' - json = body_as_json - expect(json[:content]).to include status.text + expect(response) + .to have_http_status(200) + expect(response.headers).to include( + 'Vary' => 'Accept, Accept-Language, Cookie', + 'Content-Type' => include('application/activity+json'), + 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + ) + expect(body_as_json) + .to include(content: include(status.text)) end end end @@ -378,11 +409,14 @@ describe StatusesController do let(:format) { 'html' } it 'renders status successfully', :aggregate_failures do - expect(response).to have_http_status(200) - expect(response.headers['Link'].to_s).to include 'activity+json' - expect(response.headers['Vary']).to eq 'Accept, Accept-Language, Cookie' - expect(response.headers['Cache-Control']).to include 'private' - expect(response).to render_template(:show) + expect(response) + .to have_http_status(200) + .and render_template(:show) + expect(response.headers).to include( + 'Vary' => 'Accept, Accept-Language, Cookie', + 'Cache-Control' => include('private'), + 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + ) expect(response.body).to include status.text end end @@ -391,13 +425,17 @@ describe StatusesController do let(:format) { 'json' } it 'renders ActivityPub Note object successfully' do - expect(response).to have_http_status(200) - expect(response.headers['Link'].to_s).to include 'activity+json' - expect(response.headers['Vary']).to eq 'Accept, Accept-Language, Cookie' - expect(response.headers['Cache-Control']).to include 'private' - expect(response.headers['Content-Type']).to include 'application/activity+json' - json = body_as_json - expect(json[:content]).to include status.text + expect(response) + .to have_http_status(200) + expect(response.headers).to include( + 'Vary' => 'Accept, Accept-Language, Cookie', + 'Cache-Control' => include('private'), + 'Content-Type' => include('application/activity+json'), + 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + ) + + expect(body_as_json) + .to include(content: include(status.text)) end end end @@ -438,11 +476,14 @@ describe StatusesController do let(:format) { 'html' } it 'renders status successfully', :aggregate_failures do - expect(response).to have_http_status(200) - expect(response.headers['Link'].to_s).to include 'activity+json' - expect(response.headers['Vary']).to eq 'Accept, Accept-Language, Cookie' - expect(response.headers['Cache-Control']).to include 'private' - expect(response).to render_template(:show) + expect(response) + .to have_http_status(200) + .and render_template(:show) + expect(response.headers).to include( + 'Vary' => 'Accept, Accept-Language, Cookie', + 'Cache-Control' => include('private'), + 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + ) expect(response.body).to include status.text end end @@ -451,13 +492,16 @@ describe StatusesController do let(:format) { 'json' } it 'renders ActivityPub Note object', :aggregate_failures do - expect(response).to have_http_status(200) - expect(response.headers['Link'].to_s).to include 'activity+json' - expect(response.headers['Vary']).to eq 'Accept, Accept-Language, Cookie' - expect(response.headers['Cache-Control']).to include 'private' - expect(response.headers['Content-Type']).to include 'application/activity+json' - json = body_as_json - expect(json[:content]).to include status.text + expect(response) + .to have_http_status(200) + expect(response.headers).to include( + 'Vary' => 'Accept, Accept-Language, Cookie', + 'Cache-Control' => include('private'), + 'Content-Type' => include('application/activity+json'), + 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + ) + expect(body_as_json) + .to include(content: include(status.text)) end end end @@ -732,11 +776,14 @@ describe StatusesController do end it 'renders status successfully', :aggregate_failures do - expect(response).to have_http_status(200) - expect(response.headers['Link'].to_s).to include 'activity+json' - expect(response.headers['Vary']).to eq 'Accept, Accept-Language, Cookie' - expect(response.headers['Cache-Control']).to include 'public' - expect(response).to render_template(:embed) + expect(response) + .to have_http_status(200) + .and render_template(:embed) + expect(response.headers).to include( + 'Vary' => 'Accept, Accept-Language, Cookie', + 'Cache-Control' => include('public'), + 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + ) expect(response.body).to include status.text end end From 3f0c1566c34f4114d191e0eb94c0f1477874e091 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Thu, 16 Nov 2023 04:55:50 -0500 Subject: [PATCH 46/63] Reduce expectations for `RSpec/MultipleExpectations` cop in `api/v1/accounts/relationships` spec (#27879) --- .../api/v1/accounts/relationships_spec.rb | 130 ++++++++++-------- 1 file changed, 76 insertions(+), 54 deletions(-) diff --git a/spec/requests/api/v1/accounts/relationships_spec.rb b/spec/requests/api/v1/accounts/relationships_spec.rb index 5011352c6..e06ffdfae 100644 --- a/spec/requests/api/v1/accounts/relationships_spec.rb +++ b/spec/requests/api/v1/accounts/relationships_spec.rb @@ -27,12 +27,16 @@ describe 'GET /api/v1/accounts/relationships' do it 'returns JSON with correct data', :aggregate_failures do subject - json = body_as_json - - expect(response).to have_http_status(200) - expect(json).to be_a Enumerable - expect(json.first[:following]).to be true - expect(json.first[:followed_by]).to be false + expect(response) + .to have_http_status(200) + expect(body_as_json) + .to be_an(Enumerable) + .and have_attributes( + first: include( + following: true, + followed_by: false + ) + ) end end @@ -40,18 +44,19 @@ describe 'GET /api/v1/accounts/relationships' do let(:params) { { id: [simon.id, lewis.id, bob.id] } } context 'when there is returned JSON data' do - let(:json) { body_as_json } - context 'with default parameters' do it 'returns an enumerable json with correct elements, excluding suspended accounts', :aggregate_failures do subject - expect(response).to have_http_status(200) - expect(json).to be_a Enumerable - expect(json.size).to eq 2 - - expect_simon_item_one - expect_lewis_item_two + expect(response) + .to have_http_status(200) + expect(body_as_json) + .to be_an(Enumerable) + .and have_attributes( + size: 2, + first: include(simon_item), + second: include(lewis_item) + ) end end @@ -61,62 +66,75 @@ describe 'GET /api/v1/accounts/relationships' do it 'returns an enumerable json with correct elements, including suspended accounts', :aggregate_failures do subject - expect(response).to have_http_status(200) - expect(json).to be_a Enumerable - expect(json.size).to eq 3 - - expect_simon_item_one - expect_lewis_item_two - expect_bob_item_three + expect(response) + .to have_http_status(200) + expect(body_as_json) + .to be_an(Enumerable) + .and have_attributes( + size: 3, + first: include(simon_item), + second: include(lewis_item), + third: include(bob_item) + ) end end - def expect_simon_item_one - expect(json.first[:id]).to eq simon.id.to_s - expect(json.first[:following]).to be true - expect(json.first[:showing_reblogs]).to be true - expect(json.first[:followed_by]).to be false - expect(json.first[:muting]).to be false - expect(json.first[:requested]).to be false - expect(json.first[:domain_blocking]).to be false + def simon_item + { + id: simon.id.to_s, + following: true, + showing_reblogs: true, + followed_by: false, + muting: false, + requested: false, + domain_blocking: false, + } end - def expect_lewis_item_two - expect(json.second[:id]).to eq lewis.id.to_s - expect(json.second[:following]).to be false - expect(json.second[:showing_reblogs]).to be false - expect(json.second[:followed_by]).to be true - expect(json.second[:muting]).to be false - expect(json.second[:requested]).to be false - expect(json.second[:domain_blocking]).to be false + def lewis_item + { + id: lewis.id.to_s, + following: false, + showing_reblogs: false, + followed_by: true, + muting: false, + requested: false, + domain_blocking: false, + + } end - def expect_bob_item_three - expect(json.third[:id]).to eq bob.id.to_s - expect(json.third[:following]).to be false - expect(json.third[:showing_reblogs]).to be false - expect(json.third[:followed_by]).to be false - expect(json.third[:muting]).to be false - expect(json.third[:requested]).to be false - expect(json.third[:domain_blocking]).to be false + def bob_item + { + id: bob.id.to_s, + following: false, + showing_reblogs: false, + followed_by: false, + muting: false, + requested: false, + domain_blocking: false, + + } end end it 'returns JSON with correct data on previously cached requests' do # Initial request including multiple accounts in params get '/api/v1/accounts/relationships', headers: headers, params: { id: [simon.id, lewis.id] } - expect(body_as_json.size).to eq(2) + expect(body_as_json) + .to have_attributes(size: 2) # Subsequent request with different id, should override cache from first request get '/api/v1/accounts/relationships', headers: headers, params: { id: [simon.id] } - expect(response).to have_http_status(200) + expect(response) + .to have_http_status(200) expect(body_as_json) .to be_an(Enumerable) .and have_attributes( size: 1, - first: hash_including( + first: include( following: true, showing_reblogs: true ) @@ -129,13 +147,17 @@ describe 'GET /api/v1/accounts/relationships' do get '/api/v1/accounts/relationships', headers: headers, params: { id: [simon.id] } - expect(response).to have_http_status(200) + expect(response) + .to have_http_status(200) - json = body_as_json - - expect(json).to be_a Enumerable - expect(json.first[:following]).to be false - expect(json.first[:showing_reblogs]).to be false + expect(body_as_json) + .to be_an(Enumerable) + .and have_attributes( + first: include( + following: false, + showing_reblogs: false + ) + ) end end end From 8a285413f71eac89d87ab64c985cd9f33aabdaaa Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Thu, 16 Nov 2023 05:03:51 -0500 Subject: [PATCH 47/63] Reduce expectations for `RSpec/MultipleExpectations` cop in `MoveWorker` spec (#27880) --- spec/workers/move_worker_spec.rb | 36 +++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/spec/workers/move_worker_spec.rb b/spec/workers/move_worker_spec.rb index efad92c04..774296fda 100644 --- a/spec/workers/move_worker_spec.rb +++ b/spec/workers/move_worker_spec.rb @@ -35,17 +35,16 @@ describe MoveWorker do context 'when user notes are short enough' do it 'copies user note with prelude' do subject.perform(source_account.id, target_account.id) - expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(source_account.acct) - expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(account_note.comment) + expect(relevant_account_note.comment) + .to include(source_account.acct, account_note.comment) end it 'merges user notes when needed' do new_account_note = AccountNote.create!(account: account_note.account, target_account: target_account, comment: 'new note prior to move') subject.perform(source_account.id, target_account.id) - expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(source_account.acct) - expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(account_note.comment) - expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(new_account_note.comment) + expect(relevant_account_note.comment) + .to include(source_account.acct, account_note.comment, new_account_note.comment) end end @@ -54,16 +53,24 @@ describe MoveWorker do it 'copies user note without prelude' do subject.perform(source_account.id, target_account.id) - expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(account_note.comment) + expect(relevant_account_note.comment) + .to include(account_note.comment) end it 'keeps user notes unchanged' do new_account_note = AccountNote.create!(account: account_note.account, target_account: target_account, comment: 'new note prior to move') subject.perform(source_account.id, target_account.id) - expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(new_account_note.comment) + expect(relevant_account_note.comment) + .to include(new_account_note.comment) end end + + private + + def relevant_account_note + AccountNote.find_by(account: account_note.account, target_account: target_account) + end end shared_examples 'block and mute handling' do @@ -71,10 +78,19 @@ describe MoveWorker do subject.perform(source_account.id, target_account.id) expect(block_service).to have_received(:call).with(blocking_account, target_account) - expect(AccountNote.find_by(account: blocking_account, target_account: target_account).comment).to include(source_account.acct) - expect(muting_account.muting?(target_account)).to be true - expect(AccountNote.find_by(account: muting_account, target_account: target_account).comment).to include(source_account.acct) + + expect( + [note_account_comment, mute_account_comment] + ).to all include(source_account.acct) + end + + def note_account_comment + AccountNote.find_by(account: blocking_account, target_account: target_account).comment + end + + def mute_account_comment + AccountNote.find_by(account: muting_account, target_account: target_account).comment end end From 7232d4750dedcde879bde81889fbd028897e15b0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 11:08:22 +0100 Subject: [PATCH 48/63] New Crowdin Translations (automated) (#27884) Co-authored-by: GitHub Actions <noreply@github.com> --- app/javascript/mastodon/locales/an.json | 1 - app/javascript/mastodon/locales/ar.json | 1 - app/javascript/mastodon/locales/ast.json | 1 - app/javascript/mastodon/locales/be.json | 13 ++++++++++++- app/javascript/mastodon/locales/bg.json | 14 +++++++++++++- app/javascript/mastodon/locales/bn.json | 1 - app/javascript/mastodon/locales/br.json | 1 - app/javascript/mastodon/locales/ca.json | 1 - app/javascript/mastodon/locales/ckb.json | 1 - app/javascript/mastodon/locales/co.json | 1 - app/javascript/mastodon/locales/cs.json | 1 - app/javascript/mastodon/locales/cy.json | 1 - app/javascript/mastodon/locales/da.json | 13 ++++++++++++- app/javascript/mastodon/locales/de.json | 13 ++++++++++++- app/javascript/mastodon/locales/el.json | 1 - app/javascript/mastodon/locales/en-GB.json | 13 ++++++++++++- app/javascript/mastodon/locales/eo.json | 1 - app/javascript/mastodon/locales/es-AR.json | 13 ++++++++++++- app/javascript/mastodon/locales/es-MX.json | 11 +++++++++++ app/javascript/mastodon/locales/es.json | 1 + app/javascript/mastodon/locales/et.json | 1 - app/javascript/mastodon/locales/eu.json | 13 ++++++++++++- app/javascript/mastodon/locales/fa.json | 9 ++++++++- app/javascript/mastodon/locales/fi.json | 13 ++++++++++++- app/javascript/mastodon/locales/fo.json | 1 - app/javascript/mastodon/locales/fr-QC.json | 1 - app/javascript/mastodon/locales/fr.json | 1 - app/javascript/mastodon/locales/fy.json | 1 - app/javascript/mastodon/locales/ga.json | 1 - app/javascript/mastodon/locales/gd.json | 1 - app/javascript/mastodon/locales/gl.json | 1 - app/javascript/mastodon/locales/he.json | 15 +++++++++++++-- app/javascript/mastodon/locales/hi.json | 1 - app/javascript/mastodon/locales/hr.json | 1 - app/javascript/mastodon/locales/hu.json | 13 ++++++++++++- app/javascript/mastodon/locales/hy.json | 1 - app/javascript/mastodon/locales/id.json | 1 - app/javascript/mastodon/locales/ig.json | 1 - app/javascript/mastodon/locales/io.json | 1 - app/javascript/mastodon/locales/is.json | 13 ++++++++++++- app/javascript/mastodon/locales/it.json | 13 ++++++++++++- app/javascript/mastodon/locales/ja.json | 2 +- app/javascript/mastodon/locales/ka.json | 1 - app/javascript/mastodon/locales/kab.json | 1 - app/javascript/mastodon/locales/kk.json | 1 - app/javascript/mastodon/locales/ko.json | 11 +++++++++++ app/javascript/mastodon/locales/ku.json | 1 - app/javascript/mastodon/locales/kw.json | 1 - app/javascript/mastodon/locales/lt.json | 12 ++++++++++++ app/javascript/mastodon/locales/lv.json | 1 - app/javascript/mastodon/locales/ml.json | 1 - app/javascript/mastodon/locales/mr.json | 1 - app/javascript/mastodon/locales/ms.json | 1 - app/javascript/mastodon/locales/my.json | 1 - app/javascript/mastodon/locales/nl.json | 11 +++++++++++ app/javascript/mastodon/locales/nn.json | 13 ++++++++++++- app/javascript/mastodon/locales/no.json | 13 ++++++++++++- app/javascript/mastodon/locales/oc.json | 1 - app/javascript/mastodon/locales/pa.json | 1 - app/javascript/mastodon/locales/pl.json | 11 +++++++++++ app/javascript/mastodon/locales/pt-BR.json | 1 - app/javascript/mastodon/locales/pt-PT.json | 13 ++++++++++++- app/javascript/mastodon/locales/ro.json | 1 - app/javascript/mastodon/locales/ru.json | 1 - app/javascript/mastodon/locales/sa.json | 1 - app/javascript/mastodon/locales/sc.json | 1 - app/javascript/mastodon/locales/sco.json | 1 - app/javascript/mastodon/locales/si.json | 1 - app/javascript/mastodon/locales/sk.json | 1 - app/javascript/mastodon/locales/sl.json | 1 - app/javascript/mastodon/locales/sq.json | 11 +++++++++++ app/javascript/mastodon/locales/sr-Latn.json | 1 - app/javascript/mastodon/locales/sr.json | 1 - app/javascript/mastodon/locales/sv.json | 10 +++++++++- app/javascript/mastodon/locales/ta.json | 1 - app/javascript/mastodon/locales/te.json | 1 - app/javascript/mastodon/locales/th.json | 13 ++++++++++++- app/javascript/mastodon/locales/tr.json | 13 ++++++++++++- app/javascript/mastodon/locales/tt.json | 1 - app/javascript/mastodon/locales/uk.json | 13 ++++++++++++- app/javascript/mastodon/locales/uz.json | 1 - app/javascript/mastodon/locales/vi.json | 1 - app/javascript/mastodon/locales/zgh.json | 1 - app/javascript/mastodon/locales/zh-CN.json | 13 ++++++++++++- app/javascript/mastodon/locales/zh-HK.json | 13 ++++++++++++- app/javascript/mastodon/locales/zh-TW.json | 13 ++++++++++++- config/locales/be.yml | 1 + config/locales/bg.yml | 1 + config/locales/da.yml | 1 + config/locales/de.yml | 1 + config/locales/es-AR.yml | 1 + config/locales/es-MX.yml | 1 + config/locales/fi.yml | 2 ++ config/locales/he.yml | 11 ++++++----- config/locales/hu.yml | 1 + config/locales/is.yml | 1 + config/locales/it.yml | 1 + config/locales/ko.yml | 1 + config/locales/lt.yml | 1 + config/locales/nl.yml | 1 + config/locales/nn.yml | 1 + config/locales/no.yml | 1 + config/locales/pl.yml | 1 + config/locales/pt-PT.yml | 1 + config/locales/simple_form.fi.yml | 2 +- config/locales/simple_form.he.yml | 6 +++--- config/locales/simple_form.ko.yml | 4 ++-- config/locales/sq.yml | 12 +++++++----- config/locales/th.yml | 1 + config/locales/uk.yml | 1 + config/locales/zh-HK.yml | 1 + config/locales/zh-TW.yml | 5 +++-- 112 files changed, 383 insertions(+), 98 deletions(-) diff --git a/app/javascript/mastodon/locales/an.json b/app/javascript/mastodon/locales/an.json index 6243f8d4c..a652272fa 100644 --- a/app/javascript/mastodon/locales/an.json +++ b/app/javascript/mastodon/locales/an.json @@ -334,7 +334,6 @@ "lists.search": "Buscar entre la chent a la quala sigues", "lists.subheading": "Las tuyas listas", "load_pending": "{count, plural, one {# nuevo elemento} other {# nuevos elementos}}", - "loading_indicator.label": "Cargando...", "media_gallery.toggle_visible": "{number, plural, one {Amaga la imachen} other {Amaga las imáchens}}", "moved_to_account_banner.text": "La tuya cuenta {disabledAccount} ye actualment deshabilitada perque t'has mudau a {movedToAccount}.", "mute_modal.duration": "Duración", diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index 4a5e04764..979bc9a70 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -389,7 +389,6 @@ "lists.search": "إبحث في قائمة الحسابات التي تُتابِعها", "lists.subheading": "قوائمك", "load_pending": "{count, plural, one {# عنصر جديد} other {# عناصر جديدة}}", - "loading_indicator.label": "جارٍ التحميل…", "media_gallery.toggle_visible": "{number, plural, zero {} one {اخف الصورة} two {اخف الصورتين} few {اخف الصور} many {اخف الصور} other {اخف الصور}}", "moved_to_account_banner.text": "حسابك {disabledAccount} معطل حاليًا لأنك انتقلت إلى {movedToAccount}.", "mute_modal.duration": "المدة", diff --git a/app/javascript/mastodon/locales/ast.json b/app/javascript/mastodon/locales/ast.json index fed324e2d..0d49d9a19 100644 --- a/app/javascript/mastodon/locales/ast.json +++ b/app/javascript/mastodon/locales/ast.json @@ -272,7 +272,6 @@ "lists.search": "Buscar ente los perfiles que sigues", "lists.subheading": "Les tos llistes", "load_pending": "{count, plural, one {# elementu nuevu} other {# elementos nuevos}}", - "loading_indicator.label": "Cargando…", "media_gallery.toggle_visible": "{number, plural, one {Anubrir la imaxe} other {Anubrir les imáxenes}}", "mute_modal.duration": "Duración", "mute_modal.hide_notifications": "¿Quies anubrir los avisos d'esti perfil?", diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json index e8a52ee29..4f5b247b6 100644 --- a/app/javascript/mastodon/locales/be.json +++ b/app/javascript/mastodon/locales/be.json @@ -390,7 +390,7 @@ "lists.search": "Шукайце сярод людзей, на якіх Вы падпісаны", "lists.subheading": "Вашыя спісы", "load_pending": "{count, plural, one {# новы элемент} few {# новыя элементы} many {# новых элементаў} other {# новых элементаў}}", - "loading_indicator.label": "Загрузка...", + "loading_indicator.label": "Загрузка…", "media_gallery.toggle_visible": "{number, plural, one {Схаваць відарыс} other {Схаваць відарысы}}", "moved_to_account_banner.text": "Ваш уліковы запіс {disabledAccount} зараз адключаны таму што вы перанесены на {movedToAccount}.", "mute_modal.duration": "Працягласць", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "На жаль, зараз немагчыма паказаць вынікі. Вы можаце паспрабаваць выкарыстоўваць пошук і праглядзець старонку агляду, каб знайсці людзей, на якіх можна падпісацца, або паўтарыце спробу пазней.", "onboarding.follows.lead": "Вы самі ствараеце свой хатні канал. Чым больш людзей вы падпішаце, тым больш актыўна і цікавей гэта будзе. Гэтыя профілі могуць стаць добрай адпраўной кропкай — вы заўсёды можаце адмяніць падпіску на іх пазней!", "onboarding.follows.title": "Папулярна на Mastodon", + "onboarding.profile.discoverable": "Уключыць профіль і допісы ў алгарытмы рэкамендацый", + "onboarding.profile.display_name": "Бачнае імя", + "onboarding.profile.display_name_hint": "Ваша поўнае імя або ваш псеўданім…", + "onboarding.profile.indexable": "Індэксаваць публічныя допісы ў пошукавых сістэмах", + "onboarding.profile.lead": "Вы заўсёды можаце выканаць гэта пазней у Наладах, дзе даступна яшчэ больш параметраў.", + "onboarding.profile.note": "Біяграфія", + "onboarding.profile.note_hint": "Вы можаце @згадаць іншых людзей або выкарыстоўваць #хэштэгі…", + "onboarding.profile.save_and_continue": "Захаваць і працягнуць", + "onboarding.profile.title": "Налады профілю", + "onboarding.profile.upload_avatar": "Загрузіць фота профілю", + "onboarding.profile.upload_header": "Загрузіць шапку профілю", "onboarding.share.lead": "Дайце людзям ведаць, як яны могуць знайсці вас на Mastodon!", "onboarding.share.message": "Я {username} на #Mastodon! Сачыце за мной на {url}", "onboarding.share.next_steps": "Магчымыя наступныя крокі:", diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index 83bffd946..b30dfecaa 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -222,6 +222,7 @@ "emoji_button.search_results": "Резултати от търсене", "emoji_button.symbols": "Символи", "emoji_button.travel": "Пътуване и места", + "empty_column.account_hides_collections": "Този потребител е избрал да не прави това сведение достъпно", "empty_column.account_suspended": "Спрян акаунт", "empty_column.account_timeline": "Тук няма публикации!", "empty_column.account_unavailable": "Профилът не е наличен", @@ -389,7 +390,7 @@ "lists.search": "Търсене измежду последваните", "lists.subheading": "Вашите списъци", "load_pending": "{count, plural, one {# нов елемент} other {# нови елемента}}", - "loading_indicator.label": "Зареждане...", + "loading_indicator.label": "Зареждане…", "media_gallery.toggle_visible": "Скриване на {number, plural, one {изображение} other {изображения}}", "moved_to_account_banner.text": "Вашият акаунт {disabledAccount} сега е изключен, защото се преместихте в {movedToAccount}.", "mute_modal.duration": "Времетраене", @@ -478,6 +479,17 @@ "onboarding.follows.empty": "За съжаление, в момента не могат да се показват резултати. Може да опитате да употребявате търсене или да прегледате страницата за изследване, за да намерите страница за последване, или да опитате пак по-късно.", "onboarding.follows.lead": "Може да бъдете куратор на началния си инфоканал. Последвайки повече хора, по-деен и по-интересен ще става. Тези профили може да са добра начална точка, от която винаги по-късно да спрете да следвате!", "onboarding.follows.title": "Популярно в Mastodon", + "onboarding.profile.discoverable": "Включване на профила и публикации в алгоритмите за откриване", + "onboarding.profile.display_name": "Името на показ", + "onboarding.profile.display_name_hint": "Вашето пълно име или псевдоним…", + "onboarding.profile.indexable": "Включване на обществени публикации в резултатите от търсене", + "onboarding.profile.lead": "Винаги може да завършите това по-късно в настройките, където дори има повече възможности за настройване.", + "onboarding.profile.note": "Биогр.", + "onboarding.profile.note_hint": "Може да @споменавате други хора или #хаштагове…", + "onboarding.profile.save_and_continue": "Запазване и продължаване", + "onboarding.profile.title": "Настройване на профила", + "onboarding.profile.upload_avatar": "Качване на снимка на профила", + "onboarding.profile.upload_header": "Качване на заглавка на профила", "onboarding.share.lead": "Позволете на хората да знаят, че могат да ви намерят в Mastodon!", "onboarding.share.message": "Аз съм {username} в #Mastodon! Елате да ме последвате при {url}", "onboarding.share.next_steps": "Възможни следващи стъпки:", diff --git a/app/javascript/mastodon/locales/bn.json b/app/javascript/mastodon/locales/bn.json index 85d6f2474..b6e4fbb96 100644 --- a/app/javascript/mastodon/locales/bn.json +++ b/app/javascript/mastodon/locales/bn.json @@ -314,7 +314,6 @@ "lists.search": "যাদের অনুসরণ করেন তাদের ভেতরে খুঁজুন", "lists.subheading": "আপনার তালিকা", "load_pending": "{count, plural, one {# নতুন জিনিস} other {# নতুন জিনিস}}", - "loading_indicator.label": "আসছে...", "media_gallery.toggle_visible": "দৃশ্যতার অবস্থা বদলান", "mute_modal.duration": "সময়কাল", "mute_modal.hide_notifications": "এই ব্যবহারকারীর প্রজ্ঞাপন বন্ধ করবেন ?", diff --git a/app/javascript/mastodon/locales/br.json b/app/javascript/mastodon/locales/br.json index 8449762c2..39cd73241 100644 --- a/app/javascript/mastodon/locales/br.json +++ b/app/javascript/mastodon/locales/br.json @@ -329,7 +329,6 @@ "lists.search": "Klask e-touez tud heuliet ganeoc'h", "lists.subheading": "Ho listennoù", "load_pending": "{count, plural, one {# dra nevez} other {# dra nevez}}", - "loading_indicator.label": "O kargañ...", "media_gallery.toggle_visible": "{number, plural, one {Kuzhat ar skeudenn} other {Kuzhat ar skeudenn}}", "mute_modal.duration": "Padelezh", "mute_modal.hide_notifications": "Kuzhat kemenadennoù eus an implijer-se ?", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 99cae584b..926343f67 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -390,7 +390,6 @@ "lists.search": "Cerca entre les persones que segueixes", "lists.subheading": "Les teves llistes", "load_pending": "{count, plural, one {# element nou} other {# elements nous}}", - "loading_indicator.label": "Es carrega...", "media_gallery.toggle_visible": "{number, plural, one {Amaga la imatge} other {Amaga les imatges}}", "moved_to_account_banner.text": "El teu compte {disabledAccount} està desactivat perquè l'has mogut a {movedToAccount}.", "mute_modal.duration": "Durada", diff --git a/app/javascript/mastodon/locales/ckb.json b/app/javascript/mastodon/locales/ckb.json index 381eaa5b1..7e9641832 100644 --- a/app/javascript/mastodon/locales/ckb.json +++ b/app/javascript/mastodon/locales/ckb.json @@ -342,7 +342,6 @@ "lists.search": "بگەڕێ لەناو ئەو کەسانەی کە شوێنیان کەوتویت", "lists.subheading": "لیستەکانت", "load_pending": "{count, plural, one {# بەڕگەی نوێ} other {# بەڕگەی نوێ}}", - "loading_indicator.label": "بارکردن...", "media_gallery.toggle_visible": "شاردنەوەی {number, plural, one {image} other {images}}", "moved_to_account_banner.text": "ئەکاونتەکەت {disabledAccount} لە ئێستادا لەکارخراوە چونکە تۆ چوویتە {movedToAccount}.", "mute_modal.duration": "ماوە", diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json index 1d878a0cd..d4bb2f82b 100644 --- a/app/javascript/mastodon/locales/co.json +++ b/app/javascript/mastodon/locales/co.json @@ -236,7 +236,6 @@ "lists.search": "Circà indè i vostr'abbunamenti", "lists.subheading": "E vo liste", "load_pending": "{count, plural, one {# entrata nova} other {# entrate nove}}", - "loading_indicator.label": "Caricamentu...", "media_gallery.toggle_visible": "Piattà {number, plural, one {ritrattu} other {ritratti}}", "mute_modal.duration": "Durata", "mute_modal.hide_notifications": "Piattà nutificazione da st'utilizatore?", diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json index aaf50d67c..20ef43733 100644 --- a/app/javascript/mastodon/locales/cs.json +++ b/app/javascript/mastodon/locales/cs.json @@ -386,7 +386,6 @@ "lists.search": "Hledejte mezi lidmi, které sledujete", "lists.subheading": "Vaše seznamy", "load_pending": "{count, plural, one {# nová položka} few {# nové položky} many {# nových položek} other {# nových položek}}", - "loading_indicator.label": "Načítání...", "media_gallery.toggle_visible": "{number, plural, one {Skrýt obrázek} few {Skrýt obrázky} many {Skrýt obrázky} other {Skrýt obrázky}}", "moved_to_account_banner.text": "Váš účet {disabledAccount} je momentálně deaktivován, protože jste se přesunul/a na {movedToAccount}.", "mute_modal.duration": "Trvání", diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json index 183ab6955..dcd5406d0 100644 --- a/app/javascript/mastodon/locales/cy.json +++ b/app/javascript/mastodon/locales/cy.json @@ -390,7 +390,6 @@ "lists.search": "Chwilio ymysg pobl rydych yn eu dilyn", "lists.subheading": "Eich rhestrau", "load_pending": "{count, plural, one {# eitem newydd} other {# eitem newydd}}", - "loading_indicator.label": "Llwytho...", "media_gallery.toggle_visible": "{number, plural, one {Cuddio delwedd} other {Cuddio delwedd}}", "moved_to_account_banner.text": "Ar hyn y bryd, mae eich cyfrif {disabledAccount} wedi ei analluogi am i chi symud i {movedToAccount}.", "mute_modal.duration": "Hyd", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index bb8b72bca..33eda6f43 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -390,7 +390,7 @@ "lists.search": "Søg blandt personer, som følges", "lists.subheading": "Dine lister", "load_pending": "{count, plural, one {# nyt emne} other {# nye emner}}", - "loading_indicator.label": "Indlæser...", + "loading_indicator.label": "Indlæser…", "media_gallery.toggle_visible": "{number, plural, one {Skjul billede} other {Skjul billeder}}", "moved_to_account_banner.text": "Din konto {disabledAccount} er pt. deaktiveret, da du flyttede til {movedToAccount}.", "mute_modal.duration": "Varighed", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "Ingen resultater tilgængelige pt. Prøv at bruge søgning eller gennemse siden for at finde personer at følge, eller forsøg igen senere.", "onboarding.follows.lead": "Man kurerer sin eget hjemme-feed. Jo flere personer man følger, des mere aktiv og interessant vil det være. Disse profiler kan være et godt udgangspunkt – de kan altid fjernes senere!", "onboarding.follows.title": "Populært på Mastodon", + "onboarding.profile.discoverable": "Fremhæv profil og indlæg i detekteringsalgoritmer", + "onboarding.profile.display_name": "Visningsnavn", + "onboarding.profile.display_name_hint": "Fulde navn eller dit sjove navn…", + "onboarding.profile.indexable": "Medtag offentlige indlæg i søgeresultater", + "onboarding.profile.lead": "Dette kan altid færdiggøres senere i indstillingerne, hvor endnu flere tilpasningsmuligheder forefindes.", + "onboarding.profile.note": "Bio", + "onboarding.profile.note_hint": "Man kan @omtale andre personer eller #hashtags…", + "onboarding.profile.save_and_continue": "Gem og fortsæt", + "onboarding.profile.title": "Profilopsætning", + "onboarding.profile.upload_avatar": "Upload profilbillede", + "onboarding.profile.upload_header": "Upload profiloverskrift", "onboarding.share.lead": "Lad folk vide, hvordan de kan finde dig på Mastodon!", "onboarding.share.message": "Jeg er {username} på #Mastodon! Følg mig på {url}", "onboarding.share.next_steps": "Mulige næste trin:", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index e4f7fe6ce..5c1ccf46d 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -390,7 +390,7 @@ "lists.search": "Suche nach Leuten, denen du folgst", "lists.subheading": "Deine Listen", "load_pending": "{count, plural, one {# neuer Beitrag} other {# neue Beiträge}}", - "loading_indicator.label": "Wird geladen …", + "loading_indicator.label": "Wird geladen …", "media_gallery.toggle_visible": "{number, plural, one {Medium ausblenden} other {Medien ausblenden}}", "moved_to_account_banner.text": "Dein Konto {disabledAccount} ist derzeit deaktiviert, weil du zu {movedToAccount} umgezogen bist.", "mute_modal.duration": "Dauer", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "Bedauerlicherweise können aktuell keine Ergebnisse angezeigt werden. Du kannst die Suche verwenden oder den Reiter „Entdecken“ auswählen, um neue Leute zum Folgen zu finden – oder du versuchst es später erneut.", "onboarding.follows.lead": "Deine Startseite ist der primäre Anlaufpunkt, um Mastodon zu erleben. Je mehr Profilen du folgst, umso aktiver und interessanter wird sie. Damit du direkt loslegen kannst, gibt es hier ein paar Vorschläge:", "onboarding.follows.title": "Personalisiere deine Startseite", + "onboarding.profile.discoverable": "Profil und Beiträge in Suchalgorithmen berücksichtigen", + "onboarding.profile.display_name": "Anzeigename", + "onboarding.profile.display_name_hint": "Dein richtiger Name oder dein Fantasiename …", + "onboarding.profile.indexable": "Öffentliche Beiträge in die Suchergebnisse einbeziehen", + "onboarding.profile.lead": "Du kannst das später in den Einstellungen vervollständigen, wo noch mehr Anpassungsmöglichkeiten zur Verfügung stehen.", + "onboarding.profile.note": "Über mich", + "onboarding.profile.note_hint": "Du kannst andere @Profile erwähnen oder #Hashtags verwenden …", + "onboarding.profile.save_and_continue": "Speichern und fortsetzen", + "onboarding.profile.title": "Profil einrichten", + "onboarding.profile.upload_avatar": "Profilbild hochladen", + "onboarding.profile.upload_header": "Titelbild hochladen", "onboarding.share.lead": "Lass die Leute wissen, wie sie dich auf Mastodon finden können!", "onboarding.share.message": "Ich bin {username} auf #Mastodon! Folge mir auf {url}", "onboarding.share.next_steps": "Mögliche nächste Schritte:", diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index d3be386b4..31806bcfb 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -345,7 +345,6 @@ "lists.search": "Αναζήτησε μεταξύ των ανθρώπων που ακουλουθείς", "lists.subheading": "Οι λίστες σου", "load_pending": "{count, plural, one {# νέο στοιχείο} other {# νέα στοιχεία}}", - "loading_indicator.label": "Φορτώνει...", "media_gallery.toggle_visible": "{number, plural, one {Απόκρυψη εικόνας} other {Απόκρυψη εικόνων}}", "moved_to_account_banner.text": "Ο λογαριασμός σου {disabledAccount} είναι προσωρινά απενεργοποιημένος επειδή μεταφέρθηκες στον {movedToAccount}.", "mute_modal.duration": "Διάρκεια", diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json index 20ed8937b..d00782592 100644 --- a/app/javascript/mastodon/locales/en-GB.json +++ b/app/javascript/mastodon/locales/en-GB.json @@ -389,7 +389,7 @@ "lists.search": "Search among people you follow", "lists.subheading": "Your lists", "load_pending": "{count, plural, one {# new item} other {# new items}}", - "loading_indicator.label": "Loading...", + "loading_indicator.label": "Loading…", "media_gallery.toggle_visible": "{number, plural, one {Hide image} other {Hide images}}", "moved_to_account_banner.text": "Your account {disabledAccount} is currently disabled because you moved to {movedToAccount}.", "mute_modal.duration": "Duration", @@ -478,6 +478,17 @@ "onboarding.follows.empty": "Unfortunately, no results can be shown right now. You can try using search or browsing the explore page to find people to follow, or try again later.", "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", "onboarding.follows.title": "Personalize your home feed", + "onboarding.profile.discoverable": "Feature profile and posts in discovery algorithms", + "onboarding.profile.display_name": "Display name", + "onboarding.profile.display_name_hint": "Your full name or your fun name…", + "onboarding.profile.indexable": "Include public posts in search results", + "onboarding.profile.lead": "You can always complete this later in the settings, where even more customisation options are available.", + "onboarding.profile.note": "Bio", + "onboarding.profile.note_hint": "You can @mention other people or #hashtags…", + "onboarding.profile.save_and_continue": "Save and continue", + "onboarding.profile.title": "Profile setup", + "onboarding.profile.upload_avatar": "Upload profile picture", + "onboarding.profile.upload_header": "Upload profile header", "onboarding.share.lead": "Let people know how they can find you on Mastodon!", "onboarding.share.message": "I'm {username} on #Mastodon! Come follow me at {url}", "onboarding.share.next_steps": "Possible next steps:", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 6b4258a6b..5679d5e41 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -365,7 +365,6 @@ "lists.search": "Serĉi inter la homoj, kiujn vi sekvas", "lists.subheading": "Viaj listoj", "load_pending": "{count,plural, one {# nova elemento} other {# novaj elementoj}}", - "loading_indicator.label": "Ŝargado…", "media_gallery.toggle_visible": "{number, plural, one {Kaŝi la bildon} other {Kaŝi la bildojn}}", "moved_to_account_banner.text": "Via konto {disabledAccount} estas malvalidigita ĉar vi movis ĝin al {movedToAccount}.", "mute_modal.duration": "Daŭro", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index 71d6e3119..680336369 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -390,7 +390,7 @@ "lists.search": "Buscar entre la gente que seguís", "lists.subheading": "Tus listas", "load_pending": "{count, plural, one {# elemento nuevo} other {# elementos nuevos}}", - "loading_indicator.label": "Cargando...", + "loading_indicator.label": "Cargando…", "media_gallery.toggle_visible": "Ocultar {number, plural, one {imagen} other {imágenes}}", "moved_to_account_banner.text": "Tu cuenta {disabledAccount} está actualmente deshabilitada porque te mudaste a {movedToAccount}.", "mute_modal.duration": "Duración", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "Desafortunadamente, no se pueden mostrar resultados en este momento. Podés intentar usar la búsqueda o navegar por la página de exploración para encontrar cuentas a las que seguir, o intentarlo de nuevo más tarde.", "onboarding.follows.lead": "Tu línea temporal de inicio es la forma principal de experimentar Mastodon. Cuanta más cuentas sigás, más activa e interesante será. Para empezar, acá tenés algunas sugerencias:", "onboarding.follows.title": "Personalizá tu línea de tiempo principal", + "onboarding.profile.discoverable": "Destacar perfil y mensajes en algoritmos de descubrimiento", + "onboarding.profile.display_name": "Nombre para mostrar", + "onboarding.profile.display_name_hint": "Tu nombre completo o tu pseudónimo…", + "onboarding.profile.indexable": "Incluir mensajes públicos en resultados de búsqueda", + "onboarding.profile.lead": "Siempre podés completar esto más tarde en la configuración, donde hay disponibles más opciones de personalización.", + "onboarding.profile.note": "Biografía", + "onboarding.profile.note_hint": "Podés @mencionar otras cuentas o usar #etiquetas…", + "onboarding.profile.save_and_continue": "Guardar y continuar", + "onboarding.profile.title": "Configuración del perfil", + "onboarding.profile.upload_avatar": "Subir avatar", + "onboarding.profile.upload_header": "Subir cabecera", "onboarding.share.lead": "¡Decile a la gente cómo te pueden encontrar en Mastodon!", "onboarding.share.message": "¡En #Mastodon soy «{username}»! Podés seguirme desde {url}", "onboarding.share.next_steps": "Posibles próximos pasos:", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index aadc901f9..aa8a21edb 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -479,6 +479,17 @@ "onboarding.follows.empty": "Desafortunadamente, no se pueden mostrar resultados en este momento. Puedes intentar usar la búsqueda o navegar por la página de exploración para encontrar gente a la que seguir, o inténtalo de nuevo más tarde.", "onboarding.follows.lead": "Tienes que personalizar tu inicio. Cuantas más personas sigas, más activo e interesante será. Estos perfiles pueden ser un buen punto de partida, ¡pero siempre puedes dejar de seguirlos más adelante!", "onboarding.follows.title": "Popular en Mastodon", + "onboarding.profile.discoverable": "Destacar el perfil y las publicaciones en el algoritmo de descubrimiento", + "onboarding.profile.display_name": "Nombre a mostrar", + "onboarding.profile.display_name_hint": "Tu nombre completo o tu apodo…", + "onboarding.profile.indexable": "Incluir publicaciones públicas en los resultados de búsqueda", + "onboarding.profile.lead": "Siempre puedes completar esto más tarde en los ajustes, donde hay aún más opciones de personalización disponibles.", + "onboarding.profile.note": "Biografía", + "onboarding.profile.note_hint": "Puedes @mencionar a otras personas o #hashtags…", + "onboarding.profile.save_and_continue": "Guardar y continuar", + "onboarding.profile.title": "Configuración del perfil", + "onboarding.profile.upload_avatar": "Subir foto de perfil", + "onboarding.profile.upload_header": "Subir foto de cabecera", "onboarding.share.lead": "¡Dile a la gente cómo te pueden encontrar en Mastodon!", "onboarding.share.message": "¡Soy {username} en #Mastodon! Ven a seguirme en {url}", "onboarding.share.next_steps": "Posibles siguientes pasos:", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index f3735d968..5d1aa004b 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -479,6 +479,7 @@ "onboarding.follows.empty": "Desafortunadamente, no se pueden mostrar resultados en este momento. Puedes intentar usar la búsqueda o navegar por la página de exploración para encontrar personas a las que seguir, o inténtalo de nuevo más tarde.", "onboarding.follows.lead": "Tu línea de inicio es la forma principal de experimentar Mastodon. Cuanta más personas sigas, más activa e interesante será. Para empezar, aquí hay algunas sugerencias:", "onboarding.follows.title": "Personaliza tu línea de inicio", + "onboarding.profile.display_name": "Nombre para mostrar", "onboarding.share.lead": "¡Cuéntale a otras personas cómo te pueden encontrar en Mastodon!", "onboarding.share.message": "¡Soy {username} en #Mastodon! Ven a seguirme en {url}", "onboarding.share.next_steps": "Posibles siguientes pasos:", diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json index c4182a073..3b6d86a9d 100644 --- a/app/javascript/mastodon/locales/et.json +++ b/app/javascript/mastodon/locales/et.json @@ -390,7 +390,6 @@ "lists.search": "Otsi enda jälgitavate inimeste hulgast", "lists.subheading": "Sinu nimekirjad", "load_pending": "{count, plural, one {# uus kirje} other {# uut kirjet}}", - "loading_indicator.label": "Laeb..", "media_gallery.toggle_visible": "{number, plural, one {Varja pilt} other {Varja pildid}}", "moved_to_account_banner.text": "Kontot {disabledAccount} ei ole praegu võimalik kasutada, sest kolisid kontole {movedToAccount}.", "mute_modal.duration": "Kestus", diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json index 419589aba..88f70cffb 100644 --- a/app/javascript/mastodon/locales/eu.json +++ b/app/javascript/mastodon/locales/eu.json @@ -390,7 +390,7 @@ "lists.search": "Bilatu jarraitzen dituzun pertsonen artean", "lists.subheading": "Zure zerrendak", "load_pending": "{count, plural, one {elementu berri #} other {# elementu berri}}", - "loading_indicator.label": "Kargatzen...", + "loading_indicator.label": "Kargatzen…", "media_gallery.toggle_visible": "Txandakatu ikusgaitasuna", "moved_to_account_banner.text": "Zure {disabledAccount} kontua desgaituta dago une honetan, {movedToAccount} kontura aldatu zinelako.", "mute_modal.duration": "Iraupena", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "Zoritxarrez, ezin da emaitzik erakutsi orain. Bilaketa erabil dezakezu edo Arakatu orrian jendea bilatu jarraitzeko, edo saiatu geroago.", "onboarding.follows.lead": "Hasierako orria zuk pertsonalizatzen duzu. Gero eta jende gehiagori jarraitu, orduan eta aktibo eta interesgarriago izango da. Profil hauek egokiak izan daitezke hasteko, beti ere, geroago jarraitzeari utz diezazkiekezu!", "onboarding.follows.title": "Mastodonen pil-pilean", + "onboarding.profile.discoverable": "Ezagutarazi profila eta bidalketak bilaketa algoritmoetan", + "onboarding.profile.display_name": "Bistaratzeko izena", + "onboarding.profile.display_name_hint": "Zure izena edo ezizena…", + "onboarding.profile.indexable": "Gehitu argitalpen publikoak bilaketa-emaitzetan", + "onboarding.profile.lead": "Geroagoago bete daiteke konfigurazioan, non pertsonalizatzeko aukera gehiago dauden.", + "onboarding.profile.note": "Biografia", + "onboarding.profile.note_hint": "Beste pertsona batzuk @aipa ditzakezu edo #traolak erabili…", + "onboarding.profile.save_and_continue": "Gorde eta jarraitu", + "onboarding.profile.title": "Profilaren konfigurazioa", + "onboarding.profile.upload_avatar": "Igo profilaren irudia", + "onboarding.profile.upload_header": "Igo profilaren goiburua", "onboarding.share.lead": "Esan jendeari nola aurki zaitzaketen Mastodonen!", "onboarding.share.message": "{username} naiz #Mastodon-en! Jarrai nazazu hemen: {url}", "onboarding.share.next_steps": "Hurrengo urrats posibleak:", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 246f21899..97dae30d4 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -390,7 +390,7 @@ "lists.search": "جستوجو بین کسانی که پیگرفتهاید", "lists.subheading": "سیاهههایتان", "load_pending": "{count, plural, one {# مورد جدید} other {# مورد جدید}}", - "loading_indicator.label": "بار کردن…", + "loading_indicator.label": "در حال بارگذاری…", "media_gallery.toggle_visible": "{number, plural, one {نهفتن تصویر} other {نهفتن تصاویر}}", "moved_to_account_banner.text": "حسابتان {disabledAccount} اکنون از کار افتاده؛ چرا که به {movedToAccount} منتقل شدید.", "mute_modal.duration": "مدت زمان", @@ -479,6 +479,13 @@ "onboarding.follows.empty": "متأسفانه هماکنون نتیجهای قابل نمایش نیست. میتوانید استفاده از جستوجو یا مرور صفحهٔ کاوش را برای یافتن افرادی برای پیگیری آزموده یا دوباره تلاش کنید.", "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", "onboarding.follows.title": "Popular on Mastodon", + "onboarding.profile.discoverable": "معرفی نمایه و فرستهها در الگوریتمهای کشف", + "onboarding.profile.display_name": "نام نمایشی", + "onboarding.profile.display_name_hint": "نام کامل یا نام باحالتان…", + "onboarding.profile.note": "درباره شما", + "onboarding.profile.note_hint": "میتوانید افراد دیگر را @نامبردن یا #برچسب بزنید…", + "onboarding.profile.save_and_continue": "ذخیره کن و ادامه بده", + "onboarding.profile.title": "تنظیم نمایه", "onboarding.share.lead": "بگذارید افراد بدانند چگونه میتوانند در ماستادون بیابندتان!", "onboarding.share.message": "من {username} روی #ماستودون هستم! مرا در {url} پیبگیرید", "onboarding.share.next_steps": "گامهای ممکن بعدی:", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 9aa2e7355..849a7f463 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -390,7 +390,7 @@ "lists.search": "Etsi seuraamistasi henkilöistä", "lists.subheading": "Omat listasi", "load_pending": "{count, plural, one {# uusi kohde} other {# uutta kohdetta}}", - "loading_indicator.label": "Ladataan...", + "loading_indicator.label": "Ladataan…", "media_gallery.toggle_visible": "{number, plural, one {Piilota kuva} other {Piilota kuvat}}", "moved_to_account_banner.text": "Tilisi {disabledAccount} on tällä hetkellä poissa käytöstä, koska teit siirron tiliin {movedToAccount}.", "mute_modal.duration": "Kesto", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "Valitettavasti tuloksia ei voida näyttää juuri nyt. Voit kokeilla hakua tai selata tutustumissivua löytääksesi seurattavaa tai yrittää myöhemmin uudelleen.", "onboarding.follows.lead": "Kokoat oman kotisyötteesi itse. Mitä enemmän ihmisiä seuraat, sitä aktiivisempi ja kiinnostavampi syöte on. Nämä profiilit voivat olla alkuun hyvä lähtökohta — voit aina lopettaa niiden seuraamisen myöhemmin!", "onboarding.follows.title": "Mukauta kotisyötettäsi", + "onboarding.profile.discoverable": "Pidä profiilia ja julkaisuja esillä löytämisalgoritmeissa", + "onboarding.profile.display_name": "Näyttönimi", + "onboarding.profile.display_name_hint": "Koko nimesi tai lempinimesi…", + "onboarding.profile.indexable": "Sisällytä julkiset julkaisut hakutuloksiin", + "onboarding.profile.lead": "Voit viimeistellä tämän milloin tahansa asetuksissa, jossa on saatavilla vielä enemmän mukautusvalintoja.", + "onboarding.profile.note": "Elämäkerta", + "onboarding.profile.note_hint": "Voit @mainita muita käyttäjiä tai #aihetunnisteita…", + "onboarding.profile.save_and_continue": "Tallenna ja jatka", + "onboarding.profile.title": "Profiilin määritys", + "onboarding.profile.upload_avatar": "Lataa profiilikuva", + "onboarding.profile.upload_header": "Lataa profiilin otsakekuva", "onboarding.share.lead": "Kerro ihmisille, kuinka he voivat löytää sinut Mastodonista!", "onboarding.share.message": "Olen {username} #Mastodonissa! Seuraa minua osoitteessa {url}", "onboarding.share.next_steps": "Mahdolliset seuraavat vaiheet:", diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json index 42a1317db..61c1287ea 100644 --- a/app/javascript/mastodon/locales/fo.json +++ b/app/javascript/mastodon/locales/fo.json @@ -390,7 +390,6 @@ "lists.search": "Leita millum fólk, sum tú fylgir", "lists.subheading": "Tínir listar", "load_pending": "{count, plural, one {# nýtt evni} other {# nýggj evni}}", - "loading_indicator.label": "Innlesi...", "media_gallery.toggle_visible": "{number, plural, one {Fjal mynd} other {Fjal myndir}}", "moved_to_account_banner.text": "Konta tín {disabledAccount} er í løtuni óvirkin, tí tú flutti til {movedToAccount}.", "mute_modal.duration": "Tíðarbil", diff --git a/app/javascript/mastodon/locales/fr-QC.json b/app/javascript/mastodon/locales/fr-QC.json index a6dd91bec..e858882f8 100644 --- a/app/javascript/mastodon/locales/fr-QC.json +++ b/app/javascript/mastodon/locales/fr-QC.json @@ -390,7 +390,6 @@ "lists.search": "Rechercher parmi les gens que vous suivez", "lists.subheading": "Vos listes", "load_pending": "{count, plural, one {# nouvel élément} other {# nouveaux éléments}}", - "loading_indicator.label": "Chargement…", "media_gallery.toggle_visible": "{number, plural, one {Cacher l’image} other {Cacher les images}}", "moved_to_account_banner.text": "Votre compte {disabledAccount} est actuellement désactivé parce que vous avez déménagé sur {movedToAccount}.", "mute_modal.duration": "Durée", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 0d2ba2bb9..a7bb4a12f 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -390,7 +390,6 @@ "lists.search": "Rechercher parmi les gens que vous suivez", "lists.subheading": "Vos listes", "load_pending": "{count, plural, one {# nouvel élément} other {# nouveaux éléments}}", - "loading_indicator.label": "Chargement…", "media_gallery.toggle_visible": "{number, plural, one {Cacher l’image} other {Cacher les images}}", "moved_to_account_banner.text": "Votre compte {disabledAccount} est actuellement désactivé parce que vous l'avez déplacé à {movedToAccount}.", "mute_modal.duration": "Durée", diff --git a/app/javascript/mastodon/locales/fy.json b/app/javascript/mastodon/locales/fy.json index 9d3b41606..2128a045b 100644 --- a/app/javascript/mastodon/locales/fy.json +++ b/app/javascript/mastodon/locales/fy.json @@ -390,7 +390,6 @@ "lists.search": "Sykje nei minsken dy’t jo folgje", "lists.subheading": "Jo listen", "load_pending": "{count, plural, one {# nij item} other {# nije items}}", - "loading_indicator.label": "Lade…", "media_gallery.toggle_visible": "{number, plural, one {ôfbylding ferstopje} other {ôfbyldingen ferstopje}}", "moved_to_account_banner.text": "Omdat jo nei {movedToAccount} ferhuze binne is jo account {disabledAccount} op dit stuit útskeakele.", "mute_modal.duration": "Doer", diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json index 388a557e8..ee6b44c88 100644 --- a/app/javascript/mastodon/locales/ga.json +++ b/app/javascript/mastodon/locales/ga.json @@ -316,7 +316,6 @@ "lists.replies_policy.title": "Taispeáin freagraí:", "lists.search": "Cuardaigh i measc daoine atá á leanúint agat", "lists.subheading": "Do liostaí", - "loading_indicator.label": "Ag lódáil...", "mute_modal.duration": "Tréimhse", "mute_modal.hide_notifications": "Cuir póstalacha ón t-úsáideoir seo i bhfolach?", "mute_modal.indefinite": "Gan téarma", diff --git a/app/javascript/mastodon/locales/gd.json b/app/javascript/mastodon/locales/gd.json index 4f485cfc5..91333c1a0 100644 --- a/app/javascript/mastodon/locales/gd.json +++ b/app/javascript/mastodon/locales/gd.json @@ -389,7 +389,6 @@ "lists.search": "Lorg am measg nan daoine a leanas tu", "lists.subheading": "Na liostaichean agad", "load_pending": "{count, plural, one {# nì ùr} two {# nì ùr} few {# nithean ùra} other {# nì ùr}}", - "loading_indicator.label": "’Ga luchdadh…", "media_gallery.toggle_visible": "{number, plural, 1 {Falaich an dealbh} one {Falaich na dealbhan} two {Falaich na dealbhan} few {Falaich na dealbhan} other {Falaich na dealbhan}}", "moved_to_account_banner.text": "Tha an cunntas {disabledAccount} agad à comas on a rinn thu imrich gu {movedToAccount}.", "mute_modal.duration": "Faide", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index e79e54926..7d2c6dab9 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -390,7 +390,6 @@ "lists.search": "Procurar entre as persoas que segues", "lists.subheading": "As túas listaxes", "load_pending": "{count, plural, one {# novo elemento} other {# novos elementos}}", - "loading_indicator.label": "Estase a cargar...", "media_gallery.toggle_visible": "Agochar {number, plural, one {imaxe} other {imaxes}}", "moved_to_account_banner.text": "A túa conta {disabledAccount} está actualmente desactivada porque movéchela a {movedToAccount}.", "mute_modal.duration": "Duración", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index 485e8313a..2e0009170 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -390,7 +390,7 @@ "lists.search": "חיפוש בין אנשים שאני עוקב\\ת אחריהם", "lists.subheading": "הרשימות שלך", "load_pending": "{count, plural, one {# פריט חדש} other {# פריטים חדשים}}", - "loading_indicator.label": "טוען...", + "loading_indicator.label": "בטעינה…", "media_gallery.toggle_visible": "{number, plural, one {להסתיר תמונה} two {להסתיר תמונותיים} many {להסתיר תמונות} other {להסתיר תמונות}}", "moved_to_account_banner.text": "חשבונך {disabledAccount} אינו פעיל כרגע עקב מעבר ל{movedToAccount}.", "mute_modal.duration": "משך הזמן", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "למצער, תוצאות לחיפושך אינן בנמצא. ניתן להשתמש בחיפוש או בדף החקירות לשם מציאת אנשים ולעקבם. אפשר גם לנסות שוב אחר כך.", "onboarding.follows.lead": "אתם אוצרים את הזרם הבייתי שלכם. ככל שתעקבו אחרי יותר אנשים, הוא יהיה עשיר ופעיל יותר. הנה כמה פרופילים להתחיל בהם - תמיד ניתן להפסיק מעקב אחריהם בהמשך!", "onboarding.follows.title": "פופולארי על מסטודון", + "onboarding.profile.discoverable": "הצגת פרופיל והודעות במסך התגליות", + "onboarding.profile.display_name": "שם להצגה", + "onboarding.profile.display_name_hint": "שמך המלא או כינוי הכיף שלך…", + "onboarding.profile.indexable": "הכללת הודעות ציבוריות בתוצאות החיפוש", + "onboarding.profile.lead": "תמיד ניתן להשלים זאת אחר כך בהגדרות, שם יש אפילו עוד אפשרויות להתאמה אישית.", + "onboarding.profile.note": "אודות", + "onboarding.profile.note_hint": "ניתן @לאזכר משתמשים אחרים או #תגיות…", + "onboarding.profile.save_and_continue": "לשמור ולהמשיך", + "onboarding.profile.title": "הגדרת פרופיל", + "onboarding.profile.upload_avatar": "העלאת תמונת פרופיל", + "onboarding.profile.upload_header": "העלאת כותרת פרופיל", "onboarding.share.lead": "כדאי להודיע לחברים היכן למצוא אותך במסטודון!", "onboarding.share.message": "אני {username} ברשת #מסטודון! בואו לעקוב אחרי בכתובת {url}", "onboarding.share.next_steps": "לאיפה להמשיך מכאן:", @@ -518,7 +529,7 @@ "privacy.private.short": "לעוקבים בלבד", "privacy.public.long": "גלוי לכל", "privacy.public.short": "פומבי", - "privacy.unlisted.long": "גלוי לכל, אבל מוסתר מאמצעי גילוי", + "privacy.unlisted.long": "גלוי לכל, אבל מוסתר מאמצעי תגלית", "privacy.unlisted.short": "לא רשום (לא לפיד הכללי)", "privacy_policy.last_updated": "עודכן לאחרונה {date}", "privacy_policy.title": "מדיניות פרטיות", diff --git a/app/javascript/mastodon/locales/hi.json b/app/javascript/mastodon/locales/hi.json index 71694db76..f4473b716 100644 --- a/app/javascript/mastodon/locales/hi.json +++ b/app/javascript/mastodon/locales/hi.json @@ -360,7 +360,6 @@ "lists.replies_policy.none": "कोई नहीं", "lists.replies_policy.title": "इसके जवाब दिखाएं:", "lists.subheading": "आपकी सूचियाँ", - "loading_indicator.label": "लोड हो रहा है...", "mute_modal.duration": "अवधि", "mute_modal.hide_notifications": "इस सभ्य की ओरसे आनेवाली सूचनाए शांत करे", "mute_modal.indefinite": "अनिश्चितकालीन", diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json index 16d25e784..01524e553 100644 --- a/app/javascript/mastodon/locales/hr.json +++ b/app/javascript/mastodon/locales/hr.json @@ -306,7 +306,6 @@ "lists.replies_policy.none": "Nitko", "lists.search": "Traži među praćenim ljudima", "lists.subheading": "Vaše liste", - "loading_indicator.label": "Učitavanje...", "media_gallery.toggle_visible": "Sakrij {number, plural, one {sliku} other {slike}}", "mute_modal.duration": "Trajanje", "mute_modal.hide_notifications": "Sakrij obavijesti ovog korisnika?", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index ca2027161..96fc720a4 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -390,7 +390,7 @@ "lists.search": "Keresés a követett személyek között", "lists.subheading": "Saját listák", "load_pending": "{count, plural, one {# új elem} other {# új elem}}", - "loading_indicator.label": "Betöltés...", + "loading_indicator.label": "Betöltés…", "media_gallery.toggle_visible": "{number, plural, one {Kép elrejtése} other {Képek elrejtése}}", "moved_to_account_banner.text": "A(z) {disabledAccount} fiókod jelenleg le van tiltva, mert átköltöztél ide: {movedToAccount}.", "mute_modal.duration": "Időtartam", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "Sajnos jelenleg nem jeleníthető meg eredmény. Kipróbálhatod a keresést vagy böngészheted a felfedező oldalon a követni kívánt személyeket, vagy próbáld meg később.", "onboarding.follows.lead": "A saját hírfolyamod az elsődleges tapasztalás a Mastodonon. Minél több embert követsz, annál aktívabb és érdekesebb a dolog. Az induláshoz itt van néhány javaslat:", "onboarding.follows.title": "Népszerű a Mastodonon", + "onboarding.profile.discoverable": "Profil és bejegyzések szerepeltetése a felfedezési algoritmusokban", + "onboarding.profile.display_name": "Megjelenített név", + "onboarding.profile.display_name_hint": "Teljes név vagy becenév…", + "onboarding.profile.indexable": "Nyilvános bejegyzések is a keresési eredményekben", + "onboarding.profile.lead": "Ezt később bármikor elvégezhető a beállításoknál, ahol még több testreszabási lehetőség áll rendelkezésre.", + "onboarding.profile.note": "Biográfia", + "onboarding.profile.note_hint": "@említhetünk másokat vagy #hashtag elemeket…", + "onboarding.profile.save_and_continue": "Mentés és folytatás", + "onboarding.profile.title": "Profil beüzemelés", + "onboarding.profile.upload_avatar": "Profilkép feltöltése", + "onboarding.profile.upload_header": "Profil fejléc feltöltése", "onboarding.share.lead": "Tudassuk az emberekkel, hogyan találhatnak meg a Mastodonon!", "onboarding.share.message": "{username} vagyok a #Mastodon hálózaton! Kövess itt: {url}.", "onboarding.share.next_steps": "Lehetséges következő lépések:", diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json index 776991b01..f2548c7d3 100644 --- a/app/javascript/mastodon/locales/hy.json +++ b/app/javascript/mastodon/locales/hy.json @@ -314,7 +314,6 @@ "lists.search": "Փնտրել քո հետեւած մարդկանց մէջ", "lists.subheading": "Քո ցանկերը", "load_pending": "{count, plural, one {# նոր նիւթ} other {# նոր նիւթ}}", - "loading_indicator.label": "Բեռնւում է…", "media_gallery.toggle_visible": "Ցուցադրել/թաքցնել", "mute_modal.duration": "Տեւողութիւն", "mute_modal.hide_notifications": "Թաքցնե՞լ ծանուցումներն այս օգտատիրոջից։", diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json index 76542cc52..8ecf36125 100644 --- a/app/javascript/mastodon/locales/id.json +++ b/app/javascript/mastodon/locales/id.json @@ -353,7 +353,6 @@ "lists.search": "Cari di antara orang yang Anda ikuti", "lists.subheading": "Daftar Anda", "load_pending": "{count, plural, other {# item baru}}", - "loading_indicator.label": "Tunggu sebentar...", "media_gallery.toggle_visible": "Tampil/Sembunyikan", "moved_to_account_banner.text": "Akun {disabledAccount} Anda kini dinonaktifkan karena Anda pindah ke {movedToAccount}.", "mute_modal.duration": "Durasi", diff --git a/app/javascript/mastodon/locales/ig.json b/app/javascript/mastodon/locales/ig.json index 201bebc05..c24d28eea 100644 --- a/app/javascript/mastodon/locales/ig.json +++ b/app/javascript/mastodon/locales/ig.json @@ -88,7 +88,6 @@ "lists.delete": "Hichapụ ndepụta", "lists.edit": "Dezie ndepụta", "lists.subheading": "Ndepụta gị", - "loading_indicator.label": "Na-adọnye...", "navigation_bar.about": "Maka", "navigation_bar.bookmarks": "Ebenrụtụakā", "navigation_bar.domain_blocks": "Hidden domains", diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json index cfe1b4344..552debdb5 100644 --- a/app/javascript/mastodon/locales/io.json +++ b/app/javascript/mastodon/locales/io.json @@ -383,7 +383,6 @@ "lists.search": "Trovez inter personi quon vu sequas", "lists.subheading": "Vua listi", "load_pending": "{count, plural, one {# nova kozo} other {# nova kozi}}", - "loading_indicator.label": "Kargante...", "media_gallery.toggle_visible": "Chanjar videbleso", "moved_to_account_banner.text": "Vua konto {disabledAccount} es nune desaktiva pro ke vu movis a {movedToAccount}.", "mute_modal.duration": "Durado", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index 0b6a8012a..54123ae4c 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -390,7 +390,7 @@ "lists.search": "Leita meðal þeirra sem þú fylgist með", "lists.subheading": "Listarnir þínir", "load_pending": "{count, plural, one {# nýtt atriði} other {# ný atriði}}", - "loading_indicator.label": "Hleð inn...", + "loading_indicator.label": "Hleð inn…", "media_gallery.toggle_visible": "Víxla sýnileika", "moved_to_account_banner.text": "Aðgangurinn þinn {disabledAccount} er óvirkur í augnablikinu vegna þess að þú fluttir þig yfir á {movedToAccount}.", "mute_modal.duration": "Lengd", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "Því miður er ekki hægt að birta neinar niðurstöður í augnablikinu. Þú getur reynt að nota leitina eða skoðað könnunarsíðuna til að finna fólk til að fylgjast með, nú eða prófað aftur síðar.", "onboarding.follows.lead": "Þú ræktar heimastreymið þitt. Því fleira fólki sem þú fylgist með, því virkara og áhugaverðara verður það. Að fylgjast með þessum notendum gæti verið ágætt til að byrja með - þú getur alltaf hætt að fylgjast með þeim síðar!", "onboarding.follows.title": "Vinsælt á Mastodon", + "onboarding.profile.discoverable": "Hafa notandasnið og færslur með í reikniritum leitar", + "onboarding.profile.display_name": "Birtingarnafn", + "onboarding.profile.display_name_hint": "Fullt nafn þitt eða eitthvað til gamans…", + "onboarding.profile.indexable": "Hafa opinberar færslur með í leitarniðurstöðum", + "onboarding.profile.lead": "Þú getur alltaf klárað þetta seinna í stillingunum, þar sem enn fleiri möguleikar bjóðast á sérsníðingum.", + "onboarding.profile.note": "Æviágrip", + "onboarding.profile.note_hint": "Þú getur @minnst á annað fólk eða #myllumerki…", + "onboarding.profile.save_and_continue": "Vista og halda áfram", + "onboarding.profile.title": "Uppsetning notandasniðs", + "onboarding.profile.upload_avatar": "Sendu inn auðkennismynd", + "onboarding.profile.upload_header": "Sendu inn bakgrunnsmynd í haus notandasniðs", "onboarding.share.lead": "Láttu fólk vita hvernig það getur fundið þig á Mastodon!", "onboarding.share.message": "Ég heiti {username} á #Mastodon! Þú getur fylgst með mér á {url}", "onboarding.share.next_steps": "Möguleg næstu skref:", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 8ad791bfe..284d7739c 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -390,7 +390,7 @@ "lists.search": "Cerca tra le persone che segui", "lists.subheading": "Le tue liste", "load_pending": "{count, plural, one {# nuovo oggetto} other {# nuovi oggetti}}", - "loading_indicator.label": "Caricamento...", + "loading_indicator.label": "Caricamento…", "media_gallery.toggle_visible": "{number, plural, one {Nascondi immagine} other {Nascondi immagini}}", "moved_to_account_banner.text": "Il tuo profilo {disabledAccount} è correntemente disabilitato perché ti sei spostato a {movedToAccount}.", "mute_modal.duration": "Durata", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "Sfortunatamente, nessun risultato può essere mostrato in questo momento. Puoi provare a utilizzare la ricerca o sfogliare la pagina di esplorazione per trovare persone da seguire, oppure riprova più tardi.", "onboarding.follows.lead": "La cronologia della tua home è gestita da te. Più persone segui, più attiva e interessante sarà. Questi profili possono essere un buon punto di partenza; puoi sempre smettere di seguirli in seguito!", "onboarding.follows.title": "Popolare su Mastodon", + "onboarding.profile.discoverable": "Include il profilo e i post negli algoritmi di scoperta", + "onboarding.profile.display_name": "Nome da visualizzare", + "onboarding.profile.display_name_hint": "Il tuo nome completo o il tuo nome divertente…", + "onboarding.profile.indexable": "Includi i post pubblici nei risultati di ricerca", + "onboarding.profile.lead": "Puoi sempre completarlo in un secondo momento nelle impostazioni, dove sono disponibili ancora più opzioni di personalizzazione.", + "onboarding.profile.note": "Biografia", + "onboarding.profile.note_hint": "Puoi @menzionare altre persone o #hashtags…", + "onboarding.profile.save_and_continue": "Salva e continua", + "onboarding.profile.title": "Configurazione del profilo", + "onboarding.profile.upload_avatar": "Carica l'immagine del profilo", + "onboarding.profile.upload_header": "Carica l'intestazione del profilo", "onboarding.share.lead": "Fai sapere alle persone come possono trovarti su Mastodon!", "onboarding.share.message": "Sono {username} su #Mastodon! Vieni a seguirmi su {url}", "onboarding.share.next_steps": "Possibili passaggi successivi:", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 4cd7228c8..f8c85b774 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -390,7 +390,7 @@ "lists.search": "フォローしている人の中から検索", "lists.subheading": "あなたのリスト", "load_pending": "{count}件の新着", - "loading_indicator.label": "読み込み中...", + "loading_indicator.label": "", "media_gallery.toggle_visible": "{number, plural, one {画像を閉じる} other {画像を閉じる}}", "moved_to_account_banner.text": "あなたのアカウント『{disabledAccount}』は『{movedToAccount}』に移動したため現在無効になっています。", "mute_modal.duration": "ミュートする期間", diff --git a/app/javascript/mastodon/locales/ka.json b/app/javascript/mastodon/locales/ka.json index 256aa93f4..875ac3c19 100644 --- a/app/javascript/mastodon/locales/ka.json +++ b/app/javascript/mastodon/locales/ka.json @@ -165,7 +165,6 @@ "lists.new.title_placeholder": "ახალი სიის სათაური", "lists.search": "ძებნა ადამიანებს შორის რომელთაც მიჰყვებით", "lists.subheading": "თქვენი სიები", - "loading_indicator.label": "იტვირთება...", "media_gallery.toggle_visible": "ხილვადობის ჩართვა", "mute_modal.hide_notifications": "დავმალოთ შეტყობინებები ამ მომხმარებლისგან?", "navigation_bar.blocks": "დაბლოკილი მომხმარებლები", diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json index 8f9576c26..e9d4b57de 100644 --- a/app/javascript/mastodon/locales/kab.json +++ b/app/javascript/mastodon/locales/kab.json @@ -274,7 +274,6 @@ "lists.search": "Nadi gar yemdanen i teṭṭafaṛeḍ", "lists.subheading": "Tibdarin-ik·im", "load_pending": "{count, plural, one {# n uferdis amaynut} other {# n yiferdisen imaynuten}}", - "loading_indicator.label": "Yessalay-d…", "media_gallery.toggle_visible": "Ffer {number, plural, one {tugna} other {tugniwin}}", "mute_modal.duration": "Tanzagt", "mute_modal.hide_notifications": "Tebɣiḍ ad teffreḍ talɣutin n umseqdac-a?", diff --git a/app/javascript/mastodon/locales/kk.json b/app/javascript/mastodon/locales/kk.json index 6a4b2161b..189d792e3 100644 --- a/app/javascript/mastodon/locales/kk.json +++ b/app/javascript/mastodon/locales/kk.json @@ -242,7 +242,6 @@ "lists.search": "Сіз іздеген адамдар арасында іздеу", "lists.subheading": "Тізімдеріңіз", "load_pending": "{count, plural, one {# жаңа нәрсе} other {# жаңа нәрсе}}", - "loading_indicator.label": "Жүктеу...", "media_gallery.toggle_visible": "Көрінуді қосу", "mute_modal.hide_notifications": "Бұл қолданушы ескертпелерін жасырамыз ба?", "navigation_bar.blocks": "Бұғатталғандар", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 574d8e211..1420be8e0 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -479,6 +479,17 @@ "onboarding.follows.empty": "안타깝지만 아직은 아무 것도 보여드릴 수 없습니다. 검색을 이용하거나 발견하기 페이지에서 팔로우 할 사람을 찾을 수 있습니다. 아니면 잠시 후에 다시 시도하세요.", "onboarding.follows.lead": "홈 피드는 마스토돈을 경험하는 주된 경로입니다. 더 많은 사람들을 팔로우 할수록 더 활발하고 흥미로워질 것입니다. 여기 시작을 위한 몇몇 추천을 드립니다:", "onboarding.follows.title": "내게 맞는 홈 피드 꾸미기", + "onboarding.profile.discoverable": "발견하기 알고리즘에 프로필과 게시물을 추천하기", + "onboarding.profile.display_name": "표시되는 이름", + "onboarding.profile.display_name_hint": "진짜 이름 또는 재미난 이름…", + "onboarding.profile.indexable": "공개 게시물을 검색 결과에 포함시키기", + "onboarding.profile.lead": "언제든지 나중에 설정 메뉴에서 마저 할 수 있고, 그곳에서 더 많은 맞춤 옵션을 고를 수 있습니다.", + "onboarding.profile.note": "자기소개", + "onboarding.profile.note_hint": "남을 @mention 하거나 #hashtag 태그를 달 수 있습니다…", + "onboarding.profile.save_and_continue": "저장 및 계속", + "onboarding.profile.title": "프로필 설정", + "onboarding.profile.upload_avatar": "프로필 사진 업로드", + "onboarding.profile.upload_header": "프로필 헤더 업로드", "onboarding.share.lead": "여러 사람에게 마스토돈에서 나를 찾을 수 있는 방법을 알려주세요!", "onboarding.share.message": "#마스토돈 이용하는 {username}입니다! {url} 에서 저를 팔로우 해보세요", "onboarding.share.next_steps": "할만한 다음 단계:", diff --git a/app/javascript/mastodon/locales/ku.json b/app/javascript/mastodon/locales/ku.json index 8c9aaf3e8..b94054267 100644 --- a/app/javascript/mastodon/locales/ku.json +++ b/app/javascript/mastodon/locales/ku.json @@ -339,7 +339,6 @@ "lists.search": "Di navbera kesên ku te dişopînin bigere", "lists.subheading": "Lîsteyên te", "load_pending": "{count, plural, one {# hêmaneke nû} other {#hêmaneke nû}}", - "loading_indicator.label": "Tê barkirin...", "media_gallery.toggle_visible": "{number, plural, one {Wêneyê veşêre} other {Wêneyan veşêre}}", "moved_to_account_banner.text": "Ajimêrê te {disabledAccount} niha neçalak e ji ber ku te bar kir bo {movedToAccount}.", "mute_modal.duration": "Dem", diff --git a/app/javascript/mastodon/locales/kw.json b/app/javascript/mastodon/locales/kw.json index 6b46d8231..ca08ca836 100644 --- a/app/javascript/mastodon/locales/kw.json +++ b/app/javascript/mastodon/locales/kw.json @@ -235,7 +235,6 @@ "lists.search": "Hwilas yn-mysk tus a holyewgh", "lists.subheading": "Agas rolyow", "load_pending": "{count, plural, one {# daklennowydh} other {# a daklennow nowydh}}", - "loading_indicator.label": "Ow karga...", "media_gallery.toggle_visible": "Hide {number, plural, one {aven} other {aven}}", "mute_modal.duration": "Duryans", "mute_modal.hide_notifications": "Kudha gwarnyansow a'n devnydhyer ma?", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index 5675799e7..21aa797b4 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -26,6 +26,7 @@ "account.domain_blocked": "Užblokuotas domenas", "account.edit_profile": "Redaguoti profilį", "account.enable_notifications": "Pranešti man, kai @{name} paskelbia", + "account.endorse": "Savybė profilyje", "account.featured_tags.last_status_at": "Paskutinį kartą paskelbta {date}", "account.featured_tags.last_status_never": "Nėra įrašų", "account.follow": "Sekti", @@ -191,6 +192,7 @@ "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", "lightbox.close": "Uždaryti", + "loading_indicator.label": "Kraunama…", "media_gallery.toggle_visible": "{number, plural, one {Slėpti vaizdą} few {Slėpti vaizdus} many {Slėpti vaizdo} other {Slėpti vaizdų}}", "navigation_bar.compose": "Compose new toot", "navigation_bar.domain_blocks": "Hidden domains", @@ -217,6 +219,16 @@ "onboarding.actions.go_to_home": "Go to your home feed", "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", "onboarding.follows.title": "Popular on Mastodon", + "onboarding.profile.display_name": "Rodomas vardas", + "onboarding.profile.display_name_hint": "Tavo pilnas vardas arba linksmas vardas…", + "onboarding.profile.indexable": "Įtraukti viešus įrašus į paieškos rezultatus", + "onboarding.profile.lead": "Gali visada tai užbaigti vėliau nustatymuose, kur yra dar daugiau pritaikymo parinkčių.", + "onboarding.profile.note": "Biografija", + "onboarding.profile.note_hint": "Gali @paminėti kitus žmones arba #saitažodžius…", + "onboarding.profile.save_and_continue": "Išsaugoti ir tęsti", + "onboarding.profile.title": "Profilio konfigūravimas", + "onboarding.profile.upload_avatar": "Įkelti profilio nuotrauką", + "onboarding.profile.upload_header": "Įkelti profilio antraštę", "onboarding.share.message": "Aš {username} #Mastodon! Ateik sekti manęs adresu {url}", "onboarding.start.lead": "Dabar esi Mastodon dalis – unikalios decentralizuotos socialinės žiniasklaidos platformos, kurioje tu, o ne algoritmas, pats nustatai savo patirtį. Pradėkime tavo kelionę šioje naujoje socialinėje erdvėje:", "onboarding.start.skip": "Want to skip right ahead?", diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json index 5d681b829..63ec6275b 100644 --- a/app/javascript/mastodon/locales/lv.json +++ b/app/javascript/mastodon/locales/lv.json @@ -390,7 +390,6 @@ "lists.search": "Meklēt starp cilvēkiem, kuriem tu seko", "lists.subheading": "Tavi saraksti", "load_pending": "{count, plural, one {# jauna lieta} other {# jaunas lietas}}", - "loading_indicator.label": "Ielādē...", "media_gallery.toggle_visible": "{number, plural, one {Slēpt attēlu} other {Slēpt attēlus}}", "moved_to_account_banner.text": "Tavs konts {disabledAccount} pašlaik ir atspējots, jo pārcēlies uz kontu {movedToAccount}.", "mute_modal.duration": "Ilgums", diff --git a/app/javascript/mastodon/locales/ml.json b/app/javascript/mastodon/locales/ml.json index 6bf04ce75..b00cedc6f 100644 --- a/app/javascript/mastodon/locales/ml.json +++ b/app/javascript/mastodon/locales/ml.json @@ -237,7 +237,6 @@ "lists.replies_policy.none": "ആരുമില്ല", "lists.replies_policy.title": "ഇതിനുള്ള മറുപടികൾ കാണിക്കുക:", "lists.subheading": "എന്റെ പട്ടികകൾ", - "loading_indicator.label": "ലോഡിംഗ്...", "mute_modal.duration": "കാലാവധി", "mute_modal.indefinite": "അനിശ്ചിതകാല", "navigation_bar.blocks": "തടയപ്പെട്ട ഉപയോക്താക്കൾ", diff --git a/app/javascript/mastodon/locales/mr.json b/app/javascript/mastodon/locales/mr.json index ef0efbdec..75b75375b 100644 --- a/app/javascript/mastodon/locales/mr.json +++ b/app/javascript/mastodon/locales/mr.json @@ -196,7 +196,6 @@ "lists.search": "तुम्ही फॉलो करत असलेल्या लोकांमध्ये शोधा", "lists.subheading": "तुमच्या याद्या", "load_pending": "{count, plural, one {# new item} other {# new items}}", - "loading_indicator.label": "लोड करत आहे...", "navigation_bar.compose": "Compose new toot", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.pins": "Pinned toots", diff --git a/app/javascript/mastodon/locales/ms.json b/app/javascript/mastodon/locales/ms.json index a3bbd0067..724e07ae7 100644 --- a/app/javascript/mastodon/locales/ms.json +++ b/app/javascript/mastodon/locales/ms.json @@ -386,7 +386,6 @@ "lists.search": "Cari dalam kalangan orang yang anda ikuti", "lists.subheading": "Senarai anda", "load_pending": "{count, plural, one {# item baharu} other {# item baharu}}", - "loading_indicator.label": "Memuatkan...", "media_gallery.toggle_visible": "{number, plural, other {Sembunyikan imej}}", "moved_to_account_banner.text": "Akaun anda {disabledAccount} kini dinyahdayakan kerana anda berpindah ke {movedToAccount}.", "mute_modal.duration": "Tempoh", diff --git a/app/javascript/mastodon/locales/my.json b/app/javascript/mastodon/locales/my.json index 103f4e0f8..4078a4c06 100644 --- a/app/javascript/mastodon/locales/my.json +++ b/app/javascript/mastodon/locales/my.json @@ -389,7 +389,6 @@ "lists.search": "မိမိဖောလိုးထားသူများမှရှာဖွေမည်", "lists.subheading": "သင့်၏စာရင်းများ", "load_pending": "{count, plural, one {# new item} other {# new items}}", - "loading_indicator.label": "လုပ်ဆောင်နေသည်…", "media_gallery.toggle_visible": "{number, plural, one {Hide image} other {Hide images}}", "moved_to_account_banner.text": "{movedToAccount} အကောင့်သို့ပြောင်းလဲထားသဖြင့် {disabledAccount} အကောင့်မှာပိတ်ထားသည်", "mute_modal.duration": "ကြာချိန်", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index 131d2e4aa..6f941999f 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -479,6 +479,17 @@ "onboarding.follows.empty": "Helaas kunnen op dit moment geen resultaten worden getoond. Je kunt proberen te zoeken of op de verkenningspagina te bladeren om mensen te vinden die je kunt volgen, of probeer het later opnieuw.", "onboarding.follows.lead": "Jouw starttijdlijn is de belangrijkste manier om Mastodon te ervaren. Hoe meer mensen je volgt, hoe actiever en interessanter het zal zijn. Om te beginnen, zijn hier enkele suggesties:", "onboarding.follows.title": "Je starttijdlijn aan jouw wensen aanpassen", + "onboarding.profile.discoverable": "Profiel en berichten laten uitlichten in ontdekkingsalgoritmes", + "onboarding.profile.display_name": "Weergavenaam", + "onboarding.profile.display_name_hint": "Jouw volledige naam of een leuke bijnaam…", + "onboarding.profile.indexable": "Openbare berichten in zoekresultaten opnemen", + "onboarding.profile.lead": "Je kunt dit later altijd aanvullen in de instellingen, waar nog meer aanpassingsopties beschikbaar zijn.", + "onboarding.profile.note": "Biografie", + "onboarding.profile.note_hint": "Je kunt andere mensen @vermelden of #hashtags gebruiken…", + "onboarding.profile.save_and_continue": "Opslaan en doorgaan", + "onboarding.profile.title": "Profiel instellen", + "onboarding.profile.upload_avatar": "Profielfoto uploaden", + "onboarding.profile.upload_header": "Kop voor het profiel uploaden", "onboarding.share.lead": "Laat mensen weten hoe ze je kunnen vinden op Mastodon!", "onboarding.share.message": "Ik ben {username} op #Mastodon! Volg mij op {url}", "onboarding.share.next_steps": "Mogelijke volgende stappen:", diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json index aa30aef7d..a3402d660 100644 --- a/app/javascript/mastodon/locales/nn.json +++ b/app/javascript/mastodon/locales/nn.json @@ -390,7 +390,7 @@ "lists.search": "Søk blant folk du fylgjer", "lists.subheading": "Listene dine", "load_pending": "{count, plural, one {# nytt element} other {# nye element}}", - "loading_indicator.label": "Lastar...", + "loading_indicator.label": "Laster…", "media_gallery.toggle_visible": "{number, plural, one {Skjul bilete} other {Skjul bilete}}", "moved_to_account_banner.text": "Kontoen din, {disabledAccount} er for tida deaktivert fordi du har flytta til {movedToAccount}.", "mute_modal.duration": "Varigheit", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "Me kan ikkje visa deg nokon resultat no. Du kan prøva å søkja eller bla gjennom utforsk-sida for å finna folk å fylgja, eller du kan prøva att seinare.", "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", "onboarding.follows.title": "Popular on Mastodon", + "onboarding.profile.discoverable": "Fremhevede profiler og innlegg i oppdagelsealgoritmer", + "onboarding.profile.display_name": "Visningsnavn", + "onboarding.profile.display_name_hint": "Ditt fulle navn eller ditt morsomme navn…", + "onboarding.profile.indexable": "Inkluder offentlige innlegg i søkeresultatene", + "onboarding.profile.lead": "Du kan alltid fullføre dette senere i innstillingene, der enda flere tilpasningsalternativer er tilgjengelige.", + "onboarding.profile.note": "Om meg", + "onboarding.profile.note_hint": "Du kan @nevne andre eller #emneknagger…", + "onboarding.profile.save_and_continue": "Lagre og fortsett", + "onboarding.profile.title": "Konfigurering av profil", + "onboarding.profile.upload_avatar": "Last opp profilbilde", + "onboarding.profile.upload_header": "Last opp profiltoppbilde", "onboarding.share.lead": "La folk vita korleis dei kan finna deg på Mastodon!", "onboarding.share.message": "Eg er {username} på #Mastodon! Du kan fylgja meg på {url}", "onboarding.share.next_steps": "Dette kan du gjera no:", diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index 7421c780f..fe3979f0f 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -390,7 +390,7 @@ "lists.search": "Søk blant personer du følger", "lists.subheading": "Dine lister", "load_pending": "{count, plural,one {# ny gjenstand} other {# nye gjenstander}}", - "loading_indicator.label": "Laster...", + "loading_indicator.label": "Laster…", "media_gallery.toggle_visible": "Veksle synlighet", "moved_to_account_banner.text": "Din konto {disabledAccount} er for øyeblikket deaktivert fordi du flyttet til {movedToAccount}.", "mute_modal.duration": "Varighet", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "Dessverre kan ingen resultater vises akkurat nå. Du kan prøve å bruke søk eller bla gjennom utforske-siden for å finne folk å følge, eller prøve igjen senere.", "onboarding.follows.lead": "Hjem-skjermen din er den viktigste måten å oppleve Mastodon på. Jo flere du følger, jo mer aktiv og interessant blir det. For å komme i gang, er her noen forslag:", "onboarding.follows.title": "Populært på Mastodon", + "onboarding.profile.discoverable": "Fremhevede profiler og innlegg i oppdagelsealgoritmer", + "onboarding.profile.display_name": "Visningsnavn", + "onboarding.profile.display_name_hint": "Ditt fulle navn eller ditt morsomme navn…", + "onboarding.profile.indexable": "Inkluder offentlige innlegg i søkeresultatene", + "onboarding.profile.lead": "Du kan alltid fullføre dette senere i innstillingene, der enda flere tilpasningsalternativer er tilgjengelige.", + "onboarding.profile.note": "Om meg", + "onboarding.profile.note_hint": "Du kan @nevne andre eller #emneknagger…", + "onboarding.profile.save_and_continue": "Lagre og fortsett", + "onboarding.profile.title": "Konfigurering av profil", + "onboarding.profile.upload_avatar": "Last opp profilbilde", + "onboarding.profile.upload_header": "Last opp profiltoppbilde", "onboarding.share.lead": "La folk vite hvordan de kan finne deg på Mastodon!", "onboarding.share.message": "Jeg er {username} på #Mastodon! Kom og følg meg på {url}", "onboarding.share.next_steps": "Mulige neste trinn:", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index 6b8265450..3812057fb 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -340,7 +340,6 @@ "lists.search": "Cercar demest lo mond que seguètz", "lists.subheading": "Vòstras listas", "load_pending": "{count, plural, one {# nòu element} other {# nòu elements}}", - "loading_indicator.label": "Cargament…", "media_gallery.toggle_visible": "Modificar la visibilitat", "mute_modal.duration": "Durada", "mute_modal.hide_notifications": "Rescondre las notificacions d’aquesta persona ?", diff --git a/app/javascript/mastodon/locales/pa.json b/app/javascript/mastodon/locales/pa.json index 371f9622d..7faf27971 100644 --- a/app/javascript/mastodon/locales/pa.json +++ b/app/javascript/mastodon/locales/pa.json @@ -122,7 +122,6 @@ "lightbox.next": "ਅਗਲੀ", "lightbox.previous": "ਪਿਛਲੀ", "lists.delete": "ਸੂਚੀ ਮਿਟਾਓ", - "loading_indicator.label": "ਲੋਡ ਹੋ ਰਿਹਾ ਹੈ...", "mute_modal.duration": "ਮਿਆਦ", "navigation_bar.about": "ਸਾਡੇ ਬਾਰੇ", "navigation_bar.bookmarks": "ਬੁੱਕਮਾਰਕ", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index 643f78a88..a1cc0e26e 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -479,6 +479,17 @@ "onboarding.follows.empty": "Niestety w tej chwili nie można przedstawić żadnych wyników. Możesz spróbować wyszukać lub przeglądać stronę, aby znaleźć osoby do śledzenia, lub spróbować ponownie później.", "onboarding.follows.lead": "Zarządasz swoim własnym kanałem. Im więcej ludzi śledzisz, tym bardziej aktywny i ciekawy będzie Twój kanał. Te profile mogą być dobrym punktem wyjścia— możesz przestać je obserwować w dowolnej chwili!", "onboarding.follows.title": "Popularne na Mastodonie", + "onboarding.profile.discoverable": "Udostępniaj profil i wpisy funkcjom odkrywania", + "onboarding.profile.display_name": "Nazwa wyświetlana", + "onboarding.profile.display_name_hint": "Twoje imię lub pseudonim…", + "onboarding.profile.indexable": "Pokaż publiczne wpisy w wynikach wyszukiwania", + "onboarding.profile.lead": "Możesz wypełnić te dane później w menu ustawień, gdzie dostępnych jest jeszcze więcej opcji.", + "onboarding.profile.note": "O mnie", + "onboarding.profile.note_hint": "Możesz @wspomnieć użytkowników albo #hasztagi…", + "onboarding.profile.save_and_continue": "Zapisz i kontynuuj", + "onboarding.profile.title": "Ustawienia profilu", + "onboarding.profile.upload_avatar": "Dodaj zdjęcie profilowe", + "onboarding.profile.upload_header": "Dodaj zdjęcie nagłówkowe", "onboarding.share.lead": "Daj znać ludziom, jak mogą cię znaleźć na Mastodonie!", "onboarding.share.message": "Jestem {username} na #Mastodon! Śledź mnie tutaj {url}", "onboarding.share.next_steps": "Możliwe dalsze kroki:", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index 9c09e2d71..7ce63e9b1 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -390,7 +390,6 @@ "lists.search": "Procurar entre as pessoas que segue", "lists.subheading": "Suas listas", "load_pending": "{count, plural, one {# novo item} other {# novos items}}", - "loading_indicator.label": "Carregando...", "media_gallery.toggle_visible": "{number, plural, one {Ocultar mídia} other {Ocultar mídias}}", "moved_to_account_banner.text": "Sua conta {disabledAccount} está desativada porque você a moveu para {movedToAccount}.", "mute_modal.duration": "Duração", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index 988bec9b0..69e804878 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -390,7 +390,7 @@ "lists.search": "Pesquisa entre as pessoas que segues", "lists.subheading": "As tuas listas", "load_pending": "{count, plural, one {# novo item} other {# novos itens}}", - "loading_indicator.label": "A carregar...", + "loading_indicator.label": "A carregar…", "media_gallery.toggle_visible": "Alternar visibilidade", "moved_to_account_banner.text": "A sua conta {disabledAccount} está, no momento, desativada, porque você migrou para {movedToAccount}.", "mute_modal.duration": "Duração", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "Infelizmente, não é possível mostrar resultados neste momento. Pode tentar utilizar a pesquisa ou navegar na página \"Explorar\" para encontrar pessoas para seguir ou tentar novamente mais tarde.", "onboarding.follows.lead": "Você personaliza a sua própria página inicial. Quanto mais pessoas seguir, mais ativa e interessante ela será. Estes perfis podem ser um bom ponto de partida - pode sempre deixar de os seguir mais tarde!", "onboarding.follows.title": "Popular no Mastodon", + "onboarding.profile.discoverable": "Destacar perfil e publicações nos algoritmos de descoberta", + "onboarding.profile.display_name": "Nome a apresentar", + "onboarding.profile.display_name_hint": "O seu nome completo ou o seu nome divertido…", + "onboarding.profile.indexable": "Incluir publicações públicas nos resultados de pesquisa", + "onboarding.profile.lead": "Pode sempre completar isto mais tarde, nas configurações, onde ainda estão disponíveis mais opções de personalização.", + "onboarding.profile.note": "Bio", + "onboarding.profile.note_hint": "Pode @mencionar outras pessoas ou #hashtags…", + "onboarding.profile.save_and_continue": "Guardar e continuar", + "onboarding.profile.title": "Configuração do perfil", + "onboarding.profile.upload_avatar": "Carregar foto de perfil", + "onboarding.profile.upload_header": "Carregar cabeçalho do perfil", "onboarding.share.lead": "Deixe as pessoas saber como o podem encontrar no Mastodon!", "onboarding.share.message": "Eu sou {username} no #Mastodon! Venha seguir-me em {url}", "onboarding.share.next_steps": "Próximos passos possíveis:", diff --git a/app/javascript/mastodon/locales/ro.json b/app/javascript/mastodon/locales/ro.json index f0e84463b..5355f9935 100644 --- a/app/javascript/mastodon/locales/ro.json +++ b/app/javascript/mastodon/locales/ro.json @@ -353,7 +353,6 @@ "lists.search": "Caută printre persoanele la care ești abonat", "lists.subheading": "Listele tale", "load_pending": "{count, plural, one {# element nou} other {# elemente noi}}", - "loading_indicator.label": "Se încarcă...", "media_gallery.toggle_visible": "{number, plural, one {Ascunde imaginea} other {Ascunde imaginile}}", "moved_to_account_banner.text": "Contul tău {disabledAccount} este în acest moment dezactivat deoarece te-ai mutat la {movedToAccount}.", "mute_modal.duration": "Durata", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index 69db89dc8..5c98d906b 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -389,7 +389,6 @@ "lists.search": "Искать среди подписок", "lists.subheading": "Ваши списки", "load_pending": "{count, plural, one {# новый элемент} few {# новых элемента} other {# новых элементов}}", - "loading_indicator.label": "Загрузка...", "media_gallery.toggle_visible": "Показать/скрыть {number, plural, =1 {изображение} other {изображения}}", "moved_to_account_banner.text": "Ваша учетная запись {disabledAccount} в настоящее время заморожена, потому что вы переехали на {movedToAccount}.", "mute_modal.duration": "Продолжительность", diff --git a/app/javascript/mastodon/locales/sa.json b/app/javascript/mastodon/locales/sa.json index cb92914e5..59379343b 100644 --- a/app/javascript/mastodon/locales/sa.json +++ b/app/javascript/mastodon/locales/sa.json @@ -345,7 +345,6 @@ "lists.search": "त्वया अनुसारितजनेषु अन्विष्य", "lists.subheading": "तव सूचयः", "load_pending": "{count, plural, one {# नूतनवस्तु} other {# नूतनवस्तूनि}}", - "loading_indicator.label": "आरोपयति...", "media_gallery.toggle_visible": "{number, plural, one {चित्रं प्रच्छादय} other {चित्राणि प्रच्छादय}}", "moved_to_account_banner.text": "तव एकौण्ट् {disabledAccount} अधुना निष्कृतो यतोहि {movedToAccount} अस्मिन्त्वमसार्षीः।", "mute_modal.duration": "परिमाणम्", diff --git a/app/javascript/mastodon/locales/sc.json b/app/javascript/mastodon/locales/sc.json index 4528e161b..59c834b95 100644 --- a/app/javascript/mastodon/locales/sc.json +++ b/app/javascript/mastodon/locales/sc.json @@ -263,7 +263,6 @@ "lists.search": "Chirca intre sa gente chi ses sighende", "lists.subheading": "Is listas tuas", "load_pending": "{count, plural, one {# elementu nou} other {# elementos noos}}", - "loading_indicator.label": "Carrighende...", "media_gallery.toggle_visible": "Cua {number, plural, one {immàgine} other {immàgines}}", "mute_modal.duration": "Durada", "mute_modal.hide_notifications": "Boles cuare is notìficas de custa persone?", diff --git a/app/javascript/mastodon/locales/sco.json b/app/javascript/mastodon/locales/sco.json index 9b8e6a215..28dac9c2a 100644 --- a/app/javascript/mastodon/locales/sco.json +++ b/app/javascript/mastodon/locales/sco.json @@ -330,7 +330,6 @@ "lists.search": "Seirch amang the fowk ye ken", "lists.subheading": "Yer lists", "load_pending": "{count, plural, one {# new item} other {# new items}}", - "loading_indicator.label": "Loadin...", "media_gallery.toggle_visible": "{number, plural, one {Hide image} other {Hide images}}", "moved_to_account_banner.text": "Yer accoont {disabledAccount} is disabilt the noo acause ye flittit tae {movedToAccount}.", "mute_modal.duration": "Lenth", diff --git a/app/javascript/mastodon/locales/si.json b/app/javascript/mastodon/locales/si.json index 7b26a9b48..835f699b8 100644 --- a/app/javascript/mastodon/locales/si.json +++ b/app/javascript/mastodon/locales/si.json @@ -261,7 +261,6 @@ "lists.replies_policy.none": "කිසිවෙක් නැත", "lists.replies_policy.title": "පිළිතුරු පෙන්වන්න:", "lists.subheading": "ඔබගේ ලැයිස්තු", - "loading_indicator.label": "පූරණය වෙමින්...", "mute_modal.duration": "පරාසය", "mute_modal.hide_notifications": "මෙම පුද්ගලයාගේ දැනුම්දීම් සඟවන්නද?", "navigation_bar.about": "පිළිබඳව", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index f2616b31c..24186794b 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -389,7 +389,6 @@ "lists.search": "Vyhľadávaj medzi užívateľmi, ktorých sleduješ", "lists.subheading": "Tvoje zoznamy", "load_pending": "{count, plural, one {# nová položka} other {# nových položiek}}", - "loading_indicator.label": "Načítam...", "media_gallery.toggle_visible": "Zapni/Vypni viditeľnosť", "moved_to_account_banner.text": "Vaše konto {disabledAccount} je momentálne zablokované, pretože ste sa presunuli na {movedToAccount}.", "mute_modal.duration": "Trvanie", diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json index f16a91d65..d179a8656 100644 --- a/app/javascript/mastodon/locales/sl.json +++ b/app/javascript/mastodon/locales/sl.json @@ -390,7 +390,6 @@ "lists.search": "Iščite med ljudmi, katerim sledite", "lists.subheading": "Vaši seznami", "load_pending": "{count, plural, one {# nov element} two {# nova elementa} few {# novi elementi} other {# novih elementov}}", - "loading_indicator.label": "Nalaganje ...", "media_gallery.toggle_visible": "{number, plural,one {Skrij sliko} two {Skrij sliki} other {Skrij slike}}", "moved_to_account_banner.text": "Vaš račun {disabledAccount} je trenutno onemogočen, ker ste se prestavili na {movedToAccount}.", "mute_modal.duration": "Trajanje", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index 8d54ef41b..1417bed5f 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -479,6 +479,17 @@ "onboarding.follows.empty": "Mjerisht, s’mund të shfaqen përfundime tani. Mund të provoni të përdorni kërkimin, ose të shfletoni faqen e eksplorimit, që të gjeni persona për ndjekje, ose të riprovoni më vonë.", "onboarding.follows.lead": "Ju kujdeseni për prurjen tuaj. Sa më tepër persona të tjerë të ndiqni, aq më aktive dhe interesante do të bëhet ajo. Këto profile mund të jenë një pikënisje e mirë—mundeni përherë të ndërpritni ndjekjen e tyre më vonë!", "onboarding.follows.title": "Popullore në Mastodon", + "onboarding.profile.discoverable": "Profilin dhe postimet bëji objekt të algoritmeve të zbulimit", + "onboarding.profile.display_name": "Emër në ekran", + "onboarding.profile.display_name_hint": "Emri juaj i plotë, ose ç’të doni…", + "onboarding.profile.indexable": "Përfshi postime publike në përfundime kërkimi", + "onboarding.profile.lead": "Këtë mund ta plotësoni përherë më vonë, te rregullimet, ku ka edhe më tepër mundësi përshtatjeje.", + "onboarding.profile.note": "Jetëshkrim", + "onboarding.profile.note_hint": "Mund të @përmendni persona të tjerë, ose #hashtagë…", + "onboarding.profile.save_and_continue": "Ruaje dhe vazhdo", + "onboarding.profile.title": "Udjisje profili", + "onboarding.profile.upload_avatar": "Ngarkoni foto profili", + "onboarding.profile.upload_header": "Ngarkoni krye profili", "onboarding.share.lead": "Bëjuni të ditur njerëzve se si mund t’ju gjejnë në Mastodon!", "onboarding.share.message": "Jam {username} në #Mastodon! Ejani dhe ndiqmëni te {url}", "onboarding.share.next_steps": "Hapa pasues të mundshëm:", diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json index aa948b1f0..ea6e188cb 100644 --- a/app/javascript/mastodon/locales/sr-Latn.json +++ b/app/javascript/mastodon/locales/sr-Latn.json @@ -390,7 +390,6 @@ "lists.search": "Pretraži među ljudima koje pratite", "lists.subheading": "Vaše liste", "load_pending": "{count, plural, one {# nova stavka} few {# nove stavke} other {# novih stavki}}", - "loading_indicator.label": "Učitavanje...", "media_gallery.toggle_visible": "{number, plural, one {Sakrij sliku} few {Sakrij slike} other {Sakrij slike}}", "moved_to_account_banner.text": "Vaš nalog {disabledAccount} je trenutno onemogućen jer ste prešli na {movedToAccount}.", "mute_modal.duration": "Trajanje", diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json index 9e6716927..b0a76b32a 100644 --- a/app/javascript/mastodon/locales/sr.json +++ b/app/javascript/mastodon/locales/sr.json @@ -390,7 +390,6 @@ "lists.search": "Претражи међу људима које пратите", "lists.subheading": "Ваше листе", "load_pending": "{count, plural, one {# нова ставка} few {# нове ставке} other {# нових ставки}}", - "loading_indicator.label": "Учитавање...", "media_gallery.toggle_visible": "{number, plural, one {Сакриј слику} few {Сакриј слике} other {Сакриј слике}}", "moved_to_account_banner.text": "Ваш налог {disabledAccount} је тренутно онемогућен јер сте прешли на {movedToAccount}.", "mute_modal.duration": "Трајање", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index edf981e11..6f9f37ea1 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -390,7 +390,7 @@ "lists.search": "Sök bland personer du följer", "lists.subheading": "Dina listor", "load_pending": "{count, plural, one {# nytt objekt} other {# nya objekt}}", - "loading_indicator.label": "Laddar...", + "loading_indicator.label": "Laddar…", "media_gallery.toggle_visible": "Växla synlighet", "moved_to_account_banner.text": "Ditt konto {disabledAccount} är för närvarande inaktiverat eftersom du flyttat till {movedToAccount}.", "mute_modal.duration": "Varaktighet", @@ -479,6 +479,14 @@ "onboarding.follows.empty": "Tyvärr kan inga resultat visas just nu. Du kan prova att använda sökfunktionen eller utforska sidan för att hitta personer att följa, eller försök igen senare.", "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", "onboarding.follows.title": "Popular on Mastodon", + "onboarding.profile.display_name": "Visningsnamn", + "onboarding.profile.display_name_hint": "Fullständigt namn eller ditt roliga namn…", + "onboarding.profile.indexable": "Inkludera offentliga inlägg i sökresultaten", + "onboarding.profile.lead": "Du kan alltid slutföra detta senare i inställningarna, där ännu fler anpassningsalternativ finns tillgängliga.", + "onboarding.profile.note": "Bio", + "onboarding.profile.note_hint": "Du kan @nämna andra personer eller #hashtags…", + "onboarding.profile.save_and_continue": "Spara och fortsätt", + "onboarding.profile.upload_avatar": "Ladda upp profilbild", "onboarding.share.lead": "Låt folk veta hur de kan hitta dig på Mastodon!", "onboarding.share.message": "Jag är {username} på #Mastodon! Följ mig på {url}", "onboarding.share.next_steps": "Möjliga nästa steg:", diff --git a/app/javascript/mastodon/locales/ta.json b/app/javascript/mastodon/locales/ta.json index f62088f62..ce9042e62 100644 --- a/app/javascript/mastodon/locales/ta.json +++ b/app/javascript/mastodon/locales/ta.json @@ -248,7 +248,6 @@ "lists.search": "நீங்கள் பின்தொடரும் நபர்கள் மத்தியில் தேடுதல்", "lists.subheading": "உங்கள் பட்டியல்கள்", "load_pending": "{count, plural,one {# புதியது}other {# புதியவை}}", - "loading_indicator.label": "ஏற்றுதல்...", "media_gallery.toggle_visible": "நிலைமாற்று தெரியும்", "mute_modal.hide_notifications": "இந்த பயனரின் அறிவிப்புகளை மறைக்கவா?", "navigation_bar.blocks": "தடுக்கப்பட்ட பயனர்கள்", diff --git a/app/javascript/mastodon/locales/te.json b/app/javascript/mastodon/locales/te.json index 6cf735991..f21c0ef57 100644 --- a/app/javascript/mastodon/locales/te.json +++ b/app/javascript/mastodon/locales/te.json @@ -180,7 +180,6 @@ "lists.new.title_placeholder": "కొత్త జాబితా శీర్షిక", "lists.search": "మీరు అనుసరించే వ్యక్తులలో శోధించండి", "lists.subheading": "మీ జాబితాలు", - "loading_indicator.label": "లోడ్ అవుతోంది...", "media_gallery.toggle_visible": "దృశ్యమానతను టోగుల్ చేయండి", "mute_modal.hide_notifications": "ఈ వినియోగదారు నుండి నోటిఫికేషన్లను దాచాలా?", "navigation_bar.blocks": "బ్లాక్ చేయబడిన వినియోగదారులు", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index 80f862cbe..2166cd2dd 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -390,7 +390,7 @@ "lists.search": "ค้นหาในหมู่ผู้คนที่คุณติดตาม", "lists.subheading": "รายการของคุณ", "load_pending": "{count, plural, other {# รายการใหม่}}", - "loading_indicator.label": "กำลังโหลด...", + "loading_indicator.label": "กำลังโหลด…", "media_gallery.toggle_visible": "{number, plural, other {ซ่อนภาพ}}", "moved_to_account_banner.text": "มีการปิดใช้งานบัญชีของคุณ {disabledAccount} ในปัจจุบันเนื่องจากคุณได้ย้ายไปยัง {movedToAccount}", "mute_modal.duration": "ระยะเวลา", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "น่าเสียดาย ไม่สามารถแสดงผลลัพธ์ได้ในตอนนี้ คุณสามารถลองใช้การค้นหาหรือเรียกดูหน้าสำรวจเพื่อค้นหาผู้คนที่จะติดตาม หรือลองอีกครั้งในภายหลัง", "onboarding.follows.lead": "ฟีดหน้าแรกของคุณเป็นวิธีหลักในการสัมผัส Mastodon ยิ่งคุณติดตามผู้คนมากเท่าไร ฟีดหน้าแรกก็จะยิ่งมีการใช้งานและน่าสนใจมากขึ้นเท่านั้น เพื่อช่วยให้คุณเริ่มต้นใช้งาน นี่คือข้อเสนอแนะบางส่วน:", "onboarding.follows.title": "ปรับแต่งฟีดหน้าแรกของคุณ", + "onboarding.profile.discoverable": "แสดงโปรไฟล์และโพสต์ในอัลกอริทึมการค้นพบ", + "onboarding.profile.display_name": "ชื่อที่แสดง", + "onboarding.profile.display_name_hint": "ชื่อเต็มหรือชื่อแบบสนุกสนานของคุณ", + "onboarding.profile.indexable": "รวมโพสต์สาธารณะในผลลัพธ์การค้นหา", + "onboarding.profile.lead": "คุณสามารถกลับมาทำต่อได้เสมอในการตั้งค่า ซึ่งจะมีตัวเลือกในการปรับแต่งมากกว่า", + "onboarding.profile.note": "ชีวประวัติ", + "onboarding.profile.note_hint": "คุณสามารถ @กล่าวถึง ผู้คนอื่น ๆ หรือ #แฮชแท็ก", + "onboarding.profile.save_and_continue": "บันทึกและดำเนินการต่อ", + "onboarding.profile.title": "การตั้งค่าโปรไฟล์", + "onboarding.profile.upload_avatar": "อัปโหลดรูปโปรไฟล์", + "onboarding.profile.upload_header": "อัปโหลดรูปส่วนหัวโปรไฟล์", "onboarding.share.lead": "แจ้งให้ผู้คนทราบวิธีที่เขาสามารถค้นหาคุณใน Mastodon!", "onboarding.share.message": "ฉันคือ {username} ใน #Mastodon! มาติดตามฉันที่ {url}", "onboarding.share.next_steps": "ขั้นตอนถัดไปที่เป็นไปได้:", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index 9ad259493..505b16f4b 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -390,7 +390,7 @@ "lists.search": "Takip ettiğiniz kişiler arasından arayın", "lists.subheading": "Listeleriniz", "load_pending": "{count, plural, one {# yeni öğe} other {# yeni öğe}}", - "loading_indicator.label": "Yükleniyor...", + "loading_indicator.label": "Yükleniyor…", "media_gallery.toggle_visible": "{number, plural, one {Resmi} other {Resimleri}} gizle", "moved_to_account_banner.text": "{disabledAccount} hesabınız, {movedToAccount} hesabına taşıdığınız için şu an devre dışı.", "mute_modal.duration": "Süre", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "Maalesef şu an bir sonuç gösterilemiyor. Takip edilecek kişileri bulmak için arama veya keşfet sayfasına gözatmayı kullanabilirsiniz veya daha sonra tekrar deneyin.", "onboarding.follows.lead": "Kendi ana akışınızı siz düzenliyorsunuz. Siz daha fazla insanı takip ettikçe, daha etkin ve ilgi çekici olacaktır. Bu profiller iyi bir başlangıç olabilir, isterseniz izlemeyi daha sonra bırakabilirsiniz:", "onboarding.follows.title": "Mastodon'da Popüler", + "onboarding.profile.discoverable": "Profil ve gönderileri keşif algoritmalarında kullan", + "onboarding.profile.display_name": "Görünen isim", + "onboarding.profile.display_name_hint": "Tam adınız veya kullanıcı adınız…", + "onboarding.profile.indexable": "Herkese açık gönderileri arama sonuçlarına ekle", + "onboarding.profile.lead": "Bunu her zaman daha sonra ayarlardan tamamlayabilirsiniz, hatta daha fazla özelleştirme seçeneğine de ulaşabilirsiniz.", + "onboarding.profile.note": "Kişisel bilgiler", + "onboarding.profile.note_hint": "Diğer insanlara @değinebilir veya #etiketler kullanabilirsiniz…", + "onboarding.profile.save_and_continue": "Kaydet ve ilerle", + "onboarding.profile.title": "Profilini ayarla", + "onboarding.profile.upload_avatar": "Profil resmi yükle", + "onboarding.profile.upload_header": "Profil başlığı yükle", "onboarding.share.lead": "Kullanıcılara Mastodon'da size nasıl ulaşabileceklerini ifade edin!", "onboarding.share.message": "#Mastodon'da kullanıcı adım {username}! Beni takip etmek için {url} bağlantısını kullanın", "onboarding.share.next_steps": "Olası sonraki adımlar:", diff --git a/app/javascript/mastodon/locales/tt.json b/app/javascript/mastodon/locales/tt.json index e84bc66cc..6727f3e59 100644 --- a/app/javascript/mastodon/locales/tt.json +++ b/app/javascript/mastodon/locales/tt.json @@ -314,7 +314,6 @@ "lists.replies_policy.none": "Һичкем", "lists.subheading": "Исемлегегегезләр", "load_pending": "{count, plural, one {# яңа элемент} other {# яңа элемент}}", - "loading_indicator.label": "Йөкләү...", "mute_modal.duration": "Дәвамлык", "mute_modal.indefinite": "Билгесез", "navigation_bar.about": "Проект турында", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index 09fa58fbf..8649d9310 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -390,7 +390,7 @@ "lists.search": "Шукати серед людей, на яких ви підписані", "lists.subheading": "Ваші списки", "load_pending": "{count, plural, one {# новий елемент} other {# нових елементів}}", - "loading_indicator.label": "Завантаження...", + "loading_indicator.label": "Завантаження…", "media_gallery.toggle_visible": "{number, plural, one {Приховати зображення} other {Приховати зображення}}", "moved_to_account_banner.text": "Ваш обліковий запис {disabledAccount} наразі вимкнений, оскільки вас перенесено до {movedToAccount}.", "mute_modal.duration": "Тривалість", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "На жаль, жоден результат не може бути показаний просто зараз. Ви можете спробувати скористатися пошуком або переглядом сторінки огляду, щоб знайти людей для слідкування або повторіть спробу пізніше.", "onboarding.follows.lead": "Ваша домашня стрічка - основний спосіб роботи Mastodon. Чим більше людей, які ви підписані, тим активнішою і цікавою. Ось деякі пропозиції на початок:", "onboarding.follows.title": "Персоналізуйте домашню стрічку", + "onboarding.profile.discoverable": "Враховувати профіль та дописи в алгоритмах пошуку", + "onboarding.profile.display_name": "Видиме ім'я", + "onboarding.profile.display_name_hint": "Ваше повне ім'я або ваш псевдонім…", + "onboarding.profile.indexable": "Включити загальнодоступні дописи в результати пошуку", + "onboarding.profile.lead": "Ви завжди можете завершити це пізніше в Налаштуваннях, де доступно ще більше опцій налаштування.", + "onboarding.profile.note": "Біографія", + "onboarding.profile.note_hint": "Ви можете @згадувати інших людей або #гештеґи…", + "onboarding.profile.save_and_continue": "Зберегти і продовжити", + "onboarding.profile.title": "Налаштування профілю", + "onboarding.profile.upload_avatar": "Завантажити зображення профілю", + "onboarding.profile.upload_header": "Завантажити заголовок профілю", "onboarding.share.lead": "Розкажіть людям про те, як вони можуть знайти вас на Mastodon!", "onboarding.share.message": "Я {username} на #Mastodon! Стежте за мною на {url}", "onboarding.share.next_steps": "Можливі такі кроки:", diff --git a/app/javascript/mastodon/locales/uz.json b/app/javascript/mastodon/locales/uz.json index afb4c4c4c..026cc115c 100644 --- a/app/javascript/mastodon/locales/uz.json +++ b/app/javascript/mastodon/locales/uz.json @@ -325,7 +325,6 @@ "lists.search": "Siz kuzatadigan odamlar orasidan qidiring", "lists.subheading": "Sizning ro'yxatlaringiz", "load_pending": "{count, plural, one {# yangi element} other {# yangi elementlar}}", - "loading_indicator.label": "Yuklanmoqda...", "media_gallery.toggle_visible": "{number, plural, one {Rasmni yashirish} other {Rasmlarni yashirish}}", "moved_to_account_banner.text": "{movedToAccount} hisobiga koʻchganingiz uchun {disabledAccount} hisobingiz hozirda oʻchirib qoʻyilgan.", "mute_modal.duration": "Davomiyligi", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index aa229eceb..c98be2b74 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -390,7 +390,6 @@ "lists.search": "Tìm kiếm những người mà bạn quan tâm", "lists.subheading": "Danh sách của bạn", "load_pending": "{count, plural, one {# tút mới} other {# tút mới}}", - "loading_indicator.label": "Đang tải...", "media_gallery.toggle_visible": "{number, plural, other {Ẩn hình ảnh}}", "moved_to_account_banner.text": "Tài khoản {disabledAccount} của bạn hiện không khả dụng vì bạn đã chuyển sang {movedToAccount}.", "mute_modal.duration": "Thời hạn", diff --git a/app/javascript/mastodon/locales/zgh.json b/app/javascript/mastodon/locales/zgh.json index 7b19e0e6a..5896a25b0 100644 --- a/app/javascript/mastodon/locales/zgh.json +++ b/app/javascript/mastodon/locales/zgh.json @@ -127,7 +127,6 @@ "lists.replies_policy.title": "ⵙⴽⵏ ⵜⵉⵔⴰⵔⵉⵏ ⵉ:", "lists.subheading": "ⵜⵉⵍⴳⴰⵎⵉⵏ ⵏⵏⴽ", "load_pending": "{count, plural, one {# ⵓⴼⵔⴷⵉⵙ ⴰⵎⴰⵢⵏⵓ} other {# ⵉⴼⵔⴷⴰⵙ ⵉⵎⴰⵢⵏⵓⵜⵏ}}", - "loading_indicator.label": "ⴰⵣⴷⴰⵎ...", "media_gallery.toggle_visible": "ⴼⴼⵔ {number, plural, one {ⵜⴰⵡⵍⴰⴼⵜ} other {ⵜⵉⵡⵍⴰⴼⵉⵏ}}", "navigation_bar.compose": "Compose new toot", "navigation_bar.domain_blocks": "Hidden domains", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index f830aa298..cc6d8994d 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -193,7 +193,7 @@ "conversation.with": "与 {names}", "copypaste.copied": "已复制", "copypaste.copy_to_clipboard": "复制到剪贴板", - "directory.federated": "来自已知联邦宇宙", + "directory.federated": "来自已知的联邦宇宙", "directory.local": "仅来自 {domain}", "directory.new_arrivals": "新来者", "directory.recently_active": "最近活跃", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "很抱歉,现在无法显示任何结果。您可以尝试使用搜索或浏览探索页面来查找要关注的人,或稍后再试。", "onboarding.follows.lead": "你管理你自己的家庭饲料。你关注的人越多,它将越活跃和有趣。 这些配置文件可能是一个很好的起点——你可以随时取消关注它们!", "onboarding.follows.title": "定制您的主页动态", + "onboarding.profile.discoverable": "在发现算法中展示您的个人资料和嘟文", + "onboarding.profile.display_name": "昵称", + "onboarding.profile.display_name_hint": "您的全名或昵称…", + "onboarding.profile.indexable": "将您的公开嘟文纳入搜索范围", + "onboarding.profile.lead": "您可以稍后在设置中完成此操作,设置中有更多的自定义选项。", + "onboarding.profile.note": "简介", + "onboarding.profile.note_hint": "您可以提及 @其他人 或 #标签…", + "onboarding.profile.save_and_continue": "保存并继续", + "onboarding.profile.title": "设置个人资料", + "onboarding.profile.upload_avatar": "上传头像", + "onboarding.profile.upload_header": "上传资料卡头图", "onboarding.share.lead": "让人们知道他们如何在Mastodon找到你!", "onboarding.share.message": "我是来自 #Mastodon 的 {username}!请在 {url} 关注我。", "onboarding.share.next_steps": "可能的下一步:", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index 7121a7d03..ea932bc5a 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -390,7 +390,7 @@ "lists.search": "從你關注的人搜索", "lists.subheading": "列表", "load_pending": "{count, plural, other {# 個新項目}}", - "loading_indicator.label": "載入中...", + "loading_indicator.label": "載入中…", "media_gallery.toggle_visible": "隱藏圖片", "moved_to_account_banner.text": "您的帳號 {disabledAccount} 目前已停用,因為您已搬家至 {movedToAccount}。", "mute_modal.duration": "時間", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "很遺憾,現在無法顯示任何結果。你可以嘗試搜尋或瀏覽探索頁面來找使用者來追蹤,或者稍後再試。", "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", "onboarding.follows.title": "Popular on Mastodon", + "onboarding.profile.discoverable": "在探索的演算法中展示個人檔案和帖文", + "onboarding.profile.display_name": "顯示名稱", + "onboarding.profile.display_name_hint": "你的全名或暱稱…", + "onboarding.profile.indexable": "將公開帖文納入搜尋結果中", + "onboarding.profile.lead": "你可以隨時在設定中完成此動作,那裏有更多自訂選項。", + "onboarding.profile.note": "簡介", + "onboarding.profile.note_hint": "你可以 @提及他人 或使用 #標籤…", + "onboarding.profile.save_and_continue": "儲存並繼續", + "onboarding.profile.title": "個人檔案設定", + "onboarding.profile.upload_avatar": "上載個人檔案頭像", + "onboarding.profile.upload_header": "上載個人檔案橫幅圖片", "onboarding.share.lead": "讓大家知道如何在 Mastodon 上找到你吧!", "onboarding.share.message": "我在 #Mastodon 的使用者名稱是 {username}!快來追蹤我吧 {url}", "onboarding.share.next_steps": "接下來你可以:", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 974096d2f..4c52693cc 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -390,7 +390,7 @@ "lists.search": "搜尋您跟隨的使用者", "lists.subheading": "您的列表", "load_pending": "{count, plural, one {# 個新項目} other {# 個新項目}}", - "loading_indicator.label": "讀取中...", + "loading_indicator.label": "正在載入...", "media_gallery.toggle_visible": "切換可見性", "moved_to_account_banner.text": "您的帳號 {disabledAccount} 目前已停用,因為您已搬家至 {movedToAccount}。", "mute_modal.duration": "持續時間", @@ -479,6 +479,17 @@ "onboarding.follows.empty": "很遺憾,目前未能顯示任何結果。您可以嘗試使用搜尋、瀏覽探索頁面以找尋人們跟隨、或稍候再試。", "onboarding.follows.lead": "您的首頁時間軸是 Mastodon 的核心體驗。若您跟隨更多人的話,它將會變得更活躍有趣。這些個人檔案也許是個好起點,您可以隨時取消跟隨他們!", "onboarding.follows.title": "客製化您的首頁時間軸", + "onboarding.profile.discoverable": "於探索演算法中推薦個人檔案及嘟文", + "onboarding.profile.display_name": "顯示名稱", + "onboarding.profile.display_name_hint": "完整名稱或暱稱...", + "onboarding.profile.indexable": "允許公開嘟文顯示於搜尋結果中", + "onboarding.profile.lead": "您隨時可以稍候於設定中完成此操作,將有更多自訂選項可使用。", + "onboarding.profile.note": "個人簡介", + "onboarding.profile.note_hint": "您可以 @mention 其他人或者使用 #主題標籤...", + "onboarding.profile.save_and_continue": "儲存並繼續", + "onboarding.profile.title": "個人檔案設定", + "onboarding.profile.upload_avatar": "上傳個人檔案大頭貼", + "onboarding.profile.upload_header": "上傳個人檔案封面圖片", "onboarding.share.lead": "讓其他人知道他們如何於 Mastodon 上面找到您!", "onboarding.share.message": "我是 #Mastodon 上的 {username}!歡迎於 {url} 跟隨我", "onboarding.share.next_steps": "可能的下一步:", diff --git a/config/locales/be.yml b/config/locales/be.yml index 223b4d1df..fa43373f6 100644 --- a/config/locales/be.yml +++ b/config/locales/be.yml @@ -635,6 +635,7 @@ be: created_at: Створана delete_and_resolve: Выдаліць допісы forwarded: Пераслана + forwarded_replies_explanation: Гэтае паведамленне паступіла ад выдаленага карыстальніка і дакранаецца выдаленага змесціва. Яно было накіраванае вам, бо змесціва паведамлення з'яўляецца адказам аднаму з вашых карыстальнікаў. forwarded_to: Пераслана на %{domain} mark_as_resolved: Пазначыць як вырашаную mark_as_sensitive: Пазначыць як далікатны diff --git a/config/locales/bg.yml b/config/locales/bg.yml index 9da834301..dfff7058d 100644 --- a/config/locales/bg.yml +++ b/config/locales/bg.yml @@ -1368,6 +1368,7 @@ bg: '86400': 1 ден expires_in_prompt: Никога generate: Генериране на линк за покана + invalid: Тази покана не е валидна invited_by: 'Бяхте поканени от:' max_uses: one: 1 използване diff --git a/config/locales/da.yml b/config/locales/da.yml index 13010e1ad..7344d789f 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -611,6 +611,7 @@ da: created_at: Anmeldt delete_and_resolve: Slet indlæg forwarded: Videresendt + forwarded_replies_explanation: Denne anmeldelse er fra en ekstern bruger og om eksternt indhold. Den er videresendt til dig, da det anmeldte indhold er som svar til en af dine brugere. forwarded_to: Videresendt til %{domain} mark_as_resolved: Markér som løst mark_as_sensitive: Markér som sensitiv diff --git a/config/locales/de.yml b/config/locales/de.yml index 81fa4b57f..69309737d 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -611,6 +611,7 @@ de: created_at: Gemeldet delete_and_resolve: Beiträge löschen forwarded: Weitergeleitet + forwarded_replies_explanation: Diese Meldung stammt von einem externen Profil und betrifft einen externen Inhalt. Der Inhalt wurde an Dich weitergeleitet, weil er eine Antwort auf ein bei Dir registriertes Profil ist. forwarded_to: Weitergeleitet an %{domain} mark_as_resolved: Als geklärt markieren mark_as_sensitive: Mit einer Inhaltswarnung versehen diff --git a/config/locales/es-AR.yml b/config/locales/es-AR.yml index 9175a1fc1..88248d098 100644 --- a/config/locales/es-AR.yml +++ b/config/locales/es-AR.yml @@ -611,6 +611,7 @@ es-AR: created_at: Denunciado delete_and_resolve: Eliminar mensajes forwarded: Reenviado + forwarded_replies_explanation: Esta denuncia es de un usuario remoto y sobre contenido remoto. Se te reenvió porque el contenido denunciado es en respuesta a uno de tus usuarios. forwarded_to: Reenviado a %{domain} mark_as_resolved: Marcar como resuelta mark_as_sensitive: Marcar como sensible diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml index 75d329b0a..4ecb666b0 100644 --- a/config/locales/es-MX.yml +++ b/config/locales/es-MX.yml @@ -611,6 +611,7 @@ es-MX: created_at: Denunciado delete_and_resolve: Eliminar publicaciones forwarded: Reenviado + forwarded_replies_explanation: Este reporte es de un usuario remoto y sobre contenido remoto. Se le ha enviado porque el contenido reportado es en respuesta a uno de sus usuarios. forwarded_to: Reenviado a %{domain} mark_as_resolved: Marcar como resuelto mark_as_sensitive: Marcar como sensible diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 4fe0179df..73442396f 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -611,6 +611,7 @@ fi: created_at: Raportoitu delete_and_resolve: Poista julkaisut forwarded: Välitetty + forwarded_replies_explanation: Tämä raportti on etäkäyttäjältä ja koskee etäsisältöä. Se on välitetty sinulle, koska raportoitu sisältö on vastaus jollekin käyttäjällesi. forwarded_to: Välitetty %{domain} mark_as_resolved: Merkitse ratkaistuksi mark_as_sensitive: Merkitse arkaluonteiseksi @@ -1368,6 +1369,7 @@ fi: '86400': 1 vuorokausi expires_in_prompt: Ei koskaan generate: Luo + invalid: Tämä kutsu ei ole kelvollinen invited_by: 'Sinut kutsui:' max_uses: one: kertakäyttöinen diff --git a/config/locales/he.yml b/config/locales/he.yml index 11e5db453..35dc99650 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -635,6 +635,7 @@ he: created_at: מדווח delete_and_resolve: מחיקת הודעות forwarded: קודם + forwarded_replies_explanation: דווח זה הגיע מחשבון משתמש חיצוני על תוכן חיצוני. הוא הועבר אליך כיוון שהתוכן שדווח הוא בתשובה למשתמש.ת שלך. forwarded_to: קודם ל-%{domain} mark_as_resolved: סימון כפתור mark_as_sensitive: סימון כרגיש @@ -786,7 +787,7 @@ he: public_timelines: פידים פומביים publish_discovered_servers: פרסום שרתים שנתגלו publish_statistics: פרסום הסטטיסטיקות בפומבי - title: גילוי + title: תגליות trends: נושאים חמים domain_blocks: all: לכולם @@ -1048,7 +1049,7 @@ he: advanced_web_interface_hint: 'אם ברצונך לעשות שימוש במלוא רוחב המסך, ממשק הווב המתקדם מאפשר לך להגדיר עמודות רבות ושונות כדי לראות בו זמנית כמה מידע שתרצה/י: פיד הבית, התראות, פרהסיה ומספר כלשהו של רשימות ותגיות.' animations_and_accessibility: הנפשות ונגישות confirmation_dialogs: חלונות אישור - discovery: גילוי + discovery: תגליות localization: body: מסטודון מתורגם על ידי מתנדבים. guide_link: https://crowdin.com/project/mastodon @@ -1336,7 +1337,7 @@ he: deselect: בטל בחירה של הכל none: כלום order_by: מיין לפי - save_changes: שמור שינויים + save_changes: לשמור שינויים select_all_matching_items: many: בחר.י %{count} פריטים שתאמו לחיפוש שלך. one: בחר.י פריט %{count} שתאם לחיפוש שלך. @@ -1780,7 +1781,7 @@ he: keep_media_hint: לא מוחק את הודעותיך שמצורפים אליהן קבצי מדיה keep_pinned: שמור הודעות מוצמדות keep_pinned_hint: לא מוחק אף אחד מההודעות המוצמדות שלך - keep_polls: שמור סקרים + keep_polls: לשמור סקרים keep_polls_hint: לא מוחר אף אחד מהסקרים שלך keep_self_bookmark: שמור הודעות שסימנת keep_self_bookmark_hint: לא מוחק הודעות שסימנת @@ -1868,7 +1869,7 @@ he: disable: אינך יכול/ה יותר להשתמש בחשבונך, אבל הפרופיל ושאר המידע נשארו על עומדם. ניתן לבקש גיבוי של המידע, לשנות את הגדרות החשבון או למחוק אותו. mark_statuses_as_sensitive: כמה מהודעותיך סומנו כרגישות על ידי מנחי הקהילה של %{instance}. זה אומר שאנשים יצטרכו להקיש על המדיה בהודעות לפני שתופיע תצוגה מקדימה. ניתן לסמן את המידע כרגיש בעצמך בהודעותיך העתידיות. sensitive: מעתה ואילך כל קבצי המדיה שיועלו על ידך יסומנו כרגישים ויוסתרו מאחורי אזהרה. - silence: ניתן עדיין להשתמש בחשבונך אבל רק אנשים שכבר עוקבים אחריך יראו את הודעותיך בשרת זה, וייתכן שתוחרג/י מאמצעי גילוי משתמשים. עם זאת, אחרים יוכלו עדיין לעקוב אחריך. + silence: ניתן עדיין להשתמש בחשבונך אבל רק אנשים שכבר עוקבים אחריך יראו את הודעותיך בשרת זה, וייתכן שתוחרג/י ממסכי התגליות. עם זאת, אחרים יוכלו עדיין לעקוב אחריך. suspend: לא ניתן יותר להשתמש בחשבונך, ופרופילך וכל מידע אחר לא נגישים יותר. ניתן עדיין להתחבר על מנת לבקש גיבוי של המידע שלך עד שיוסר סופית בעוד כ-30 יום, אבל מידע מסויים ישמר על מנת לוודא שלא תחמוק/י מההשעיה. reason: 'סיבה:' statuses: 'הודעות מצוטטות:' diff --git a/config/locales/hu.yml b/config/locales/hu.yml index 48f9d5b9d..a0ff3061f 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -611,6 +611,7 @@ hu: created_at: Jelentve delete_and_resolve: Bejegyzések törlése forwarded: Továbbítva + forwarded_replies_explanation: Ez a jelentés egy távoli felhasználótól származik, és távoli tartalomról szól. Azért lett neked továbbítva, mert a jelentett tartalom az egyik felhasználódnak küldött válasz. forwarded_to: 'Továbbítva ide: %{domain}' mark_as_resolved: Megjelölés megoldottként mark_as_sensitive: Érzékenynek jelölés diff --git a/config/locales/is.yml b/config/locales/is.yml index 390ce0ac0..a4706ee51 100644 --- a/config/locales/is.yml +++ b/config/locales/is.yml @@ -611,6 +611,7 @@ is: created_at: Tilkynnt delete_and_resolve: Eyða færslum forwarded: Áframsent + forwarded_replies_explanation: Þessi kæra er frá fjartengdum notanda og er um fjartengt efni. Hún hefur verið framsend til þín þar sem kærða efnið er í svari til eins af notendunum þínum. forwarded_to: Áframsent á %{domain} mark_as_resolved: Merkja sem leyst mark_as_sensitive: Merkja sem viðkvæmt diff --git a/config/locales/it.yml b/config/locales/it.yml index f35e9e42b..82bbf7251 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -611,6 +611,7 @@ it: created_at: Segnalato delete_and_resolve: Cancella post forwarded: Inoltrato + forwarded_replies_explanation: Questa segnalazione proviene da un utente remoto e tratta di contenuti remoti. È stato inoltrato a voi perché il contenuto riportato è in risposta a uno dei vostri utenti. forwarded_to: Inoltrato a %{domain} mark_as_resolved: Segna come risolto mark_as_sensitive: Segna come sensibile diff --git a/config/locales/ko.yml b/config/locales/ko.yml index fb193c75f..e11081fcd 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -601,6 +601,7 @@ ko: created_at: 신고 시각 delete_and_resolve: 게시물 삭제 forwarded: 전달됨 + forwarded_replies_explanation: 이 신고는 리모트 사용자가 리모트 컨텐츠에 대해 신고한 것입니다. 이것은 신고된 내용이 로컬 사용자에 대한 답글이기 때문에 첨부되었습니다. forwarded_to: "%{domain}에게 전달됨" mark_as_resolved: 해결로 표시 mark_as_sensitive: 민감함으로 설정 diff --git a/config/locales/lt.yml b/config/locales/lt.yml index 529eb5a44..035b04462 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -214,6 +214,7 @@ lt: comment: none: Nėra created_at: Reportuotas + forwarded_replies_explanation: Šis ataskaita yra iš nuotolinio naudotojo ir susijusi su nuotoliniu turiniu. Jis buvo persiųstas tau, nes turinys, apie kurį pranešta, yra atsakymas vienam iš tavo naudotojų. mark_as_resolved: Pažymėti kaip išsprestą mark_as_unresolved: Pažymėti kaip neišsprestą notes: diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 94a1f29f7..4147078d3 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -611,6 +611,7 @@ nl: created_at: Gerapporteerd op delete_and_resolve: Bericht verwijderen forwarded: Doorgestuurd + forwarded_replies_explanation: Dit rapport komt van een externe gebruiker en gaat over externe inhoud. Het is naar u doorgestuurd omdat de gerapporteerde inhoud een reactie is op een van uw gebruikers. forwarded_to: Doorgestuurd naar %{domain} mark_as_resolved: Markeer als opgelost mark_as_sensitive: Als gevoelig markeren diff --git a/config/locales/nn.yml b/config/locales/nn.yml index 4925d4463..09de24a67 100644 --- a/config/locales/nn.yml +++ b/config/locales/nn.yml @@ -611,6 +611,7 @@ nn: created_at: Rapportert delete_and_resolve: Slett innlegg forwarded: Videresendt + forwarded_replies_explanation: Denne rapporten er fra en ekstern bruker og handler om eksternt innhold. Den er videresendt til deg fordi det rapporterte innholdet svarer til en av brukerne dine. forwarded_to: Videresendt til %{domain} mark_as_resolved: Merk som løyst mark_as_sensitive: Marker som ømtolig diff --git a/config/locales/no.yml b/config/locales/no.yml index a1058bf9f..3cf2df3a1 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -611,6 +611,7 @@ created_at: Rapportert delete_and_resolve: Slettede innlegg forwarded: Videresendt + forwarded_replies_explanation: Denne rapporten er fra en ekstern bruker og handler om eksternt innhold. Den er videresendt til deg fordi det rapporterte innholdet svarer til en av brukerne dine. forwarded_to: Videresendt til %{domain} mark_as_resolved: Merk som løst mark_as_sensitive: Merk som følsomt diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 69b1aa0a9..4ff81e11e 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -635,6 +635,7 @@ pl: created_at: Zgłoszono delete_and_resolve: Usuń posty forwarded: Przekazano + forwarded_replies_explanation: Ten raport nadszedł od zdalnego użytkownika i dotyczy zdalnej treści. Został ci przekazany, bo raportowana treść jest odpowiedzią na jednego z twoich użytkowników. forwarded_to: Przekazano do %{domain} mark_as_resolved: Oznacz jako rozwiązane mark_as_sensitive: Oznacz jako wrażliwe diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml index ce7479aa8..8e147ce4c 100644 --- a/config/locales/pt-PT.yml +++ b/config/locales/pt-PT.yml @@ -611,6 +611,7 @@ pt-PT: created_at: Denunciado delete_and_resolve: Eliminar publicações forwarded: Encaminhado + forwarded_replies_explanation: Esta denúncia é de um utilizador remoto e sobre conteúdo remoto. Foi encaminhada para si porque o conteúdo denunciado é em resposta a um dos seus utilizadores. forwarded_to: Encaminhado para %{domain} mark_as_resolved: Marcar como resolvido mark_as_sensitive: Marcar como problemático diff --git a/config/locales/simple_form.fi.yml b/config/locales/simple_form.fi.yml index bada4c3d0..29f2398a9 100644 --- a/config/locales/simple_form.fi.yml +++ b/config/locales/simple_form.fi.yml @@ -188,7 +188,7 @@ fi: email: Sähköpostiosoite expires_in: Vanhenee fields: Lisäkentät - header: Otsikkokuva + header: Otsakekuva honeypot: "%{label} (älä täytä)" inbox_url: Välittäjän postilaatikon URL-osoite irreversible: Pudota piilottamisen sijaan diff --git a/config/locales/simple_form.he.yml b/config/locales/simple_form.he.yml index 13ea8a0c4..04b21cd1b 100644 --- a/config/locales/simple_form.he.yml +++ b/config/locales/simple_form.he.yml @@ -85,7 +85,7 @@ he: mascot: בחירת ציור למנשק הווב המתקדם. media_cache_retention_period: קבצי מדיה שהורדו ימחקו אחרי מספר הימים שיצוינו אם נבחר מספר חיובי, או-אז יורדו שוב מחדש בהתאם לצורך. peers_api_enabled: רשימת השרתים ששרת זה פגש בפדיוורס. לא כולל מידע לגבי קשר ישיר עם שרת נתון, אלא רק שידוע לשרת זה על קיומו. מידע זה משמש שירותים האוספים סטטיסטיקות כלליות על הפדרציה. - profile_directory: מדריך הפרופילים מפרט את כל המשתמשים שביקשו להיות ניתנים לגילוי. + profile_directory: ספריית הפרופילים מציגה ברשימה את כל המשתמשים שביקשו להיות ניתנים לגילוי. require_invite_text: כאשר הרשמות דורשות אישור ידני, הפיכת טקסט ה"מדוע את/ה רוצה להצטרף" להכרחי במקום אופציונלי site_contact_email: מה היא הדרך ליצור איתך קשר לצורך תמיכה או לצורך תאימות עם החוק. site_contact_username: כיצד יכולים אחרים ליצור איתך קשר על רשת מסטודון. @@ -140,7 +140,7 @@ he: url: היעד שאליו יישלחו אירועים labels: account: - discoverable: חשיפת פרופיל משתמש והודעות לאלגוריתם של האתר + discoverable: הצג משתמש ופוסטים בעמוד התגליות fields: name: תווית value: תוכן @@ -188,7 +188,7 @@ he: email: כתובת דוא"ל expires_in: תפוגה לאחר fields: מטא-נתונים על הפרופיל - header: כותרת + header: תמונת נושא honeypot: "%{label} (לא למלא)" inbox_url: קישורית לתיבת ממסר irreversible: הסרה במקום הסתרה diff --git a/config/locales/simple_form.ko.yml b/config/locales/simple_form.ko.yml index 7b2656155..720012a31 100644 --- a/config/locales/simple_form.ko.yml +++ b/config/locales/simple_form.ko.yml @@ -4,7 +4,7 @@ ko: hints: account: discoverable: 내 공개 게시물과 프로필이 마스토돈의 다양한 추천 기능에 나타날 수 있고 프로필이 다른 사용자에게 제안될 수 있습니다 - display_name: 실명 혹은 별명. + display_name: 진짜 이름 또는 재미난 이름. fields: 홈페이지, 호칭, 나이, 뭐든지 적고 싶은 것들. indexable: 내 공개 게시물이 마스토돈의 검색 결과에 나타날 수 있습니다. 내 게시물과 상호작용했던 사람들은 이 설정과 관계 없이 그 게시물을 검색할 수 있습니다. note: '남을 @mention 하거나 #hashtag 태그를 달 수 있습니다.' @@ -195,7 +195,7 @@ ko: locale: 인터페이스 언어 max_uses: 사용 횟수 제한 new_password: 새로운 암호 입력 - note: 소개 + note: 자기소개 otp_attempt: 이중 인증 코드 password: 암호 phrase: 키워드 또는 문장 diff --git a/config/locales/sq.yml b/config/locales/sq.yml index bd01a8089..b6a7736df 100644 --- a/config/locales/sq.yml +++ b/config/locales/sq.yml @@ -1,18 +1,18 @@ --- sq: about: - about_mastodon_html: 'Rrjeti shoqëror i së ardhmes: Pa reklama, pa survejim nga korporata, konceptim etik dhe decentralizim! Jini zot i të dhënave tuaja, me Mastodon-in!' - contact_missing: I parregulluar + about_mastodon_html: 'Rrjeti social i së ardhmes: Pa reklama, pa sy vëzhguese nga korporata, etik dhe i decentralizuar! Merrni sërisht zotësinë e të dhënave tuaja, me Mastodon!' + contact_missing: E pacaktuar contact_unavailable: N/A - hosted_on: Mastodon i strehuar në %{domain} + hosted_on: Server Mastodon i strehuar në %{domain} title: Mbi accounts: follow: Ndiqeni followers: one: Ndjekës other: Ndjekës - following: Ndjekje - instance_actor_flash: Kjo llogari është një aktor virtual, i përdorur për të përfaqësuar vetë shërbyesin dhe jo ndonjë përdorues individual. Përdoret për qëllime federimi dhe s’duhet pezulluar. + following: Po ndjek + instance_actor_flash: Kjo llogari është një aktor virtual, i përdorur për të përfaqësuar vetë serverin dhe jo ndonjë përdorues. Përdoret për qëllime federimi dhe s’duhet pezulluar. last_active: aktiv së fundi link_verified_on: Pronësia e kësaj lidhjeje qe kontrolluar më %{date} nothing_here: S’ka gjë këtu! @@ -610,6 +610,7 @@ sq: created_at: Raportuar më delete_and_resolve: Fshiji postimet forwarded: U përcoll + forwarded_replies_explanation: Ky raportim është nga një përdorues i largët dhe rreth lënde të largët. Ju është përcjellë ngaqë lënda e raportuar gjendet në përgjigje ndaj njërit prej përdoruesve tuaj. forwarded_to: U përcoll te %{domain} mark_as_resolved: Vëri shenjë si i zgjidhur mark_as_sensitive: Vëri shenjë si rezervat @@ -925,6 +926,7 @@ sq: peaked_on_and_decaying: Kulmoi më %{date}, tani në rënie title: Hashtag-ë në modë trendable: Mund të shfaqet nën të modës + trending_rank: 'Trending #%{rank}' usable: Mund të përdoret usage_comparison: Përdorur %{today} herë sot, krahasuar me %{yesterday} dje used_by_over_week: diff --git a/config/locales/th.yml b/config/locales/th.yml index 661899896..79d668d3e 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -599,6 +599,7 @@ th: created_at: รายงานเมื่อ delete_and_resolve: ลบโพสต์ forwarded: ส่งต่อแล้ว + forwarded_replies_explanation: รายงานนี้มาจากผู้ใช้ระยะไกล และเป็นรายงานเกี่ยวกับเนื้อหาระยะไกล ซึ่งถูกส่งต่อมาหาคุณเนื่องจากเนื้อหาที่ถูกรายงานอยู่ในการตอบกลับไปยังหนึ่งในผู้ใช้ของคุณ forwarded_to: ส่งต่อไปยัง %{domain} แล้ว mark_as_resolved: ทำเครื่องหมายว่าแก้ปัญหาแล้ว mark_as_sensitive: ทำเครื่องหมายว่าละเอียดอ่อน diff --git a/config/locales/uk.yml b/config/locales/uk.yml index 2261c647b..e9eee14a1 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -635,6 +635,7 @@ uk: created_at: Створено delete_and_resolve: Видалити дописи forwarded: Переслано + forwarded_replies_explanation: Цей звіт належить віддаленому користувачеві і про віддалений вміст. Контент був пересланий вам, тому що він містить повідомлення у відповідь одному з ваших користувачів. forwarded_to: Переслано до %{domain} mark_as_resolved: Позначити вирішеним mark_as_sensitive: Позначити делікатним diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml index f13cedad6..5dd0d2e61 100644 --- a/config/locales/zh-HK.yml +++ b/config/locales/zh-HK.yml @@ -599,6 +599,7 @@ zh-HK: created_at: 日期 delete_and_resolve: 刪除帖文 forwarded: 已轉寄 + forwarded_replies_explanation: 這份檢舉來自一位遠端使用者,並涉及遠端內容。之所以轉交給你,是因為被檢舉的內容是回覆你其中一位使用者。 forwarded_to: 已轉寄到 %{domain} mark_as_resolved: 標示為「已處理」 mark_as_sensitive: 標記為敏感內容 diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 7259afdbe..2f65855fc 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -70,7 +70,7 @@ zh-TW: enabled_msg: 成功解除 %{username} 帳號的凍結 followers: 跟隨者 follows: 正在跟隨 - header: 開頭 + header: 封面圖片 inbox_url: 收件匣 (Inbox) URL invite_request_text: 加入原因 invited_by: 邀請者 @@ -117,7 +117,7 @@ zh-TW: remote_suspension_irreversible: 此帳號之資料已被不可逆地刪除。 remote_suspension_reversible_hint_html: 這個帳號已於此伺服器被停權,所有資料將會於 %{date} 被刪除。於此之前,遠端伺服器可以完全回復此的帳號。如果您想即時刪除這個帳號的資料,您能於下面進行操作。 remove_avatar: 取消大頭貼 - remove_header: 移除開頭 + remove_header: 移除封面圖片 removed_avatar_msg: 已成功刪除 %{username} 的大頭貼 removed_header_msg: 已成功刪除 %{username} 的封面圖片 resend_confirmation: @@ -599,6 +599,7 @@ zh-TW: created_at: 日期 delete_and_resolve: 刪除嘟文 forwarded: 已轉寄 + forwarded_replies_explanation: 此報告來自聯邦宇宙中非本伺服器帳號,關於非本伺服器內容。此報告轉發給您,因為報告之內容是回覆給您的伺服器上某位使用者。 forwarded_to: 轉寄到 %{domain} mark_as_resolved: 標記為「已解決」 mark_as_sensitive: 標記為敏感內容 From 87696ea26e832fda3cb7a335db6c5899c5ddbf6d Mon Sep 17 00:00:00 2001 From: Eugen Rochko <eugen@zeonfederated.com> Date: Thu, 16 Nov 2023 11:23:14 +0100 Subject: [PATCH 49/63] Add prominent share/copy button on profiles in web UI (#27865) --- .../mastodon/components/copy_icon_button.jsx | 44 +++++++++++++++++++ .../features/account/components/header.jsx | 23 ++++++---- app/javascript/mastodon/locales/en.json | 2 + .../styles/mastodon/components.scss | 21 +++++++++ 4 files changed, 81 insertions(+), 9 deletions(-) create mode 100644 app/javascript/mastodon/components/copy_icon_button.jsx diff --git a/app/javascript/mastodon/components/copy_icon_button.jsx b/app/javascript/mastodon/components/copy_icon_button.jsx new file mode 100644 index 000000000..9b1a36d83 --- /dev/null +++ b/app/javascript/mastodon/components/copy_icon_button.jsx @@ -0,0 +1,44 @@ +import PropTypes from 'prop-types'; +import { useState, useCallback } from 'react'; + +import { defineMessages } from 'react-intl'; + +import classNames from 'classnames'; + +import { useDispatch } from 'react-redux'; + +import { ReactComponent as ContentCopyIcon } from '@material-symbols/svg-600/outlined/content_copy.svg'; + +import { showAlert } from 'mastodon/actions/alerts'; +import { IconButton } from 'mastodon/components/icon_button'; + +const messages = defineMessages({ + copied: { id: 'copy_icon_button.copied', defaultMessage: 'Copied to clipboard' }, +}); + +export const CopyIconButton = ({ title, value, className }) => { + const [copied, setCopied] = useState(false); + const dispatch = useDispatch(); + + const handleClick = useCallback(() => { + navigator.clipboard.writeText(value); + setCopied(true); + dispatch(showAlert({ message: messages.copied })); + setTimeout(() => setCopied(false), 700); + }, [setCopied, value, dispatch]); + + return ( + <IconButton + className={classNames(className, copied ? 'copied' : 'copyable')} + title={title} + onClick={handleClick} + iconComponent={ContentCopyIcon} + /> + ); +}; + +CopyIconButton.propTypes = { + title: PropTypes.string, + value: PropTypes.string, + className: PropTypes.string, +}; diff --git a/app/javascript/mastodon/features/account/components/header.jsx b/app/javascript/mastodon/features/account/components/header.jsx index 7594135a4..29b46cb43 100644 --- a/app/javascript/mastodon/features/account/components/header.jsx +++ b/app/javascript/mastodon/features/account/components/header.jsx @@ -14,10 +14,12 @@ import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/l import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg'; import { ReactComponent as NotificationsIcon } from '@material-symbols/svg-600/outlined/notifications.svg'; import { ReactComponent as NotificationsActiveIcon } from '@material-symbols/svg-600/outlined/notifications_active-fill.svg'; +import { ReactComponent as ShareIcon } from '@material-symbols/svg-600/outlined/share.svg'; import { Avatar } from 'mastodon/components/avatar'; import { Badge, AutomatedBadge, GroupBadge } from 'mastodon/components/badge'; import { Button } from 'mastodon/components/button'; +import { CopyIconButton } from 'mastodon/components/copy_icon_button'; import { FollowersCounter, FollowingCounter, StatusesCounter } from 'mastodon/components/counters'; import { Icon } from 'mastodon/components/icon'; import { IconButton } from 'mastodon/components/icon_button'; @@ -46,6 +48,7 @@ const messages = defineMessages({ mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' }, report: { id: 'account.report', defaultMessage: 'Report @{name}' }, share: { id: 'account.share', defaultMessage: 'Share @{name}\'s profile' }, + copy: { id: 'account.copy', defaultMessage: 'Copy link to profile' }, media: { id: 'account.media', defaultMessage: 'Media' }, blockDomain: { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' }, unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' }, @@ -245,11 +248,10 @@ class Header extends ImmutablePureComponent { const isRemote = account.get('acct') !== account.get('username'); const remoteDomain = isRemote ? account.get('acct').split('@')[1] : null; - let info = []; - let actionBtn = ''; - let bellBtn = ''; - let lockedIcon = ''; - let menu = []; + let actionBtn, bellBtn, lockedIcon, shareBtn; + + let info = []; + let menu = []; if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) { info.push(<span key='followed_by' className='relationship-tag'><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span>); @@ -267,6 +269,12 @@ class Header extends ImmutablePureComponent { bellBtn = <IconButton icon={account.getIn(['relationship', 'notifying']) ? 'bell' : 'bell-o'} iconComponent={account.getIn(['relationship', 'notifying']) ? NotificationsActiveIcon : NotificationsIcon} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />; } + if ('share' in navigator) { + shareBtn = <IconButton className='optional' iconComponent={ShareIcon} title={intl.formatMessage(messages.share, { name: account.get('username') })} onClick={this.handleShare} />; + } else { + shareBtn = <CopyIconButton className='optional' title={intl.formatMessage(messages.copy)} value={account.get('url')} />; + } + if (me !== account.get('id')) { if (signedIn && !account.get('relationship')) { // Wait until the relationship is loaded actionBtn = ''; @@ -297,10 +305,6 @@ class Header extends ImmutablePureComponent { if (isRemote) { menu.push({ text: intl.formatMessage(messages.openOriginalPage), href: account.get('url') }); - } - - if ('share' in navigator && !account.get('suspended')) { - menu.push({ text: intl.formatMessage(messages.share, { name: account.get('username') }), action: this.handleShare }); menu.push(null); } @@ -414,6 +418,7 @@ class Header extends ImmutablePureComponent { <> {actionBtn} {bellBtn} + {shareBtn} </> )} diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 041446037..16941e2ca 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -21,6 +21,7 @@ "account.blocked": "Blocked", "account.browse_more_on_origin_server": "Browse more on the original profile", "account.cancel_follow_request": "Cancel follow", + "account.copy": "Copy link to profile", "account.direct": "Privately mention @{name}", "account.disable_notifications": "Stop notifying me when @{name} posts", "account.domain_blocked": "Domain blocked", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Mark as read", "conversation.open": "View conversation", "conversation.with": "With {names}", + "copy_icon_button.copied": "Copied to clipboard", "copypaste.copied": "Copied", "copypaste.copy_to_clipboard": "Copy to clipboard", "directory.federated": "From known fediverse", diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 9f87352f5..cc9b54d9e 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -286,6 +286,17 @@ font-size: 12px; font-weight: 500; } + + &.copyable { + transition: all 300ms linear; + } + + &.copied { + border-color: $valid-value-color; + color: $valid-value-color; + transition: none; + background-color: rgba($valid-value-color, 0.15); + } } .text-icon-button { @@ -7373,6 +7384,16 @@ noscript { width: 24px; height: 24px; } + + &.copied { + border-color: $valid-value-color; + } + } + + @media screen and (width <= 427px) { + .optional { + display: none; + } } } From c94bedf4e6e5987864b42d63aa90920f80d3644e Mon Sep 17 00:00:00 2001 From: Claire <claire.github-309c@sitedethib.com> Date: Thu, 16 Nov 2023 12:59:45 +0100 Subject: [PATCH 50/63] Use container queries to hide profile share button (#27889) --- app/javascript/styles/mastodon/components.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index cc9b54d9e..8a79eddf0 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -7289,6 +7289,7 @@ noscript { .account__header { overflow: hidden; + container: account-header / inline-size; &.inactive { opacity: 0.5; @@ -7390,7 +7391,7 @@ noscript { } } - @media screen and (width <= 427px) { + @container account-header (max-width: 372px) { .optional { display: none; } From 0a6ec048a8123a26ba9e00896cecbd03c5e3a990 Mon Sep 17 00:00:00 2001 From: Claire <claire.github-309c@sitedethib.com> Date: Thu, 16 Nov 2023 14:43:02 +0100 Subject: [PATCH 51/63] Fix upper border radius of onboarding columns (#27890) --- app/javascript/styles/mastodon/components.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 8a79eddf0..9c3d9dc2c 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2299,8 +2299,7 @@ $ui-header-height: 55px; > .scrollable { background: $ui-base-color; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; + border-radius: 0 0 4px 4px; } } From 155fb8414150e78b4e61aa33d483cc7713161134 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Thu, 16 Nov 2023 09:36:59 -0500 Subject: [PATCH 52/63] Improve spec coverage for collection of `workers/` classes (#27874) --- .../account_deletion_request_fabricator.rb | 5 ++ spec/fabricators/import_fabricator.rb | 7 +++ spec/workers/account_refresh_worker_spec.rb | 52 ++++++++++++++++ .../activitypub/post_upgrade_worker_spec.rb | 18 ++++++ ...ze_featured_tags_collection_worker_spec.rb | 29 +++++++++ spec/workers/admin/suspension_worker_spec.rb | 28 +++++++++ .../after_account_domain_block_worker_spec.rb | 29 +++++++++ spec/workers/backup_worker_spec.rb | 36 +++++++++++ spec/workers/delete_mute_worker_spec.rb | 42 +++++++++++++ spec/workers/feed_insert_worker_spec.rb | 21 ++++++- spec/workers/import_worker_spec.rb | 23 +++++++ .../workers/post_process_media_worker_spec.rb | 34 +++++++++-- ...blish_announcement_reaction_worker_spec.rb | 38 ++++++++++++ spec/workers/removal_worker_spec.rb | 28 +++++++++ .../scheduler/self_destruct_scheduler_spec.rb | 60 +++++++++++++++++++ spec/workers/webhooks/delivery_worker_spec.rb | 18 +++++- 16 files changed, 460 insertions(+), 8 deletions(-) create mode 100644 spec/fabricators/account_deletion_request_fabricator.rb create mode 100644 spec/fabricators/import_fabricator.rb create mode 100644 spec/workers/account_refresh_worker_spec.rb create mode 100644 spec/workers/activitypub/post_upgrade_worker_spec.rb create mode 100644 spec/workers/activitypub/synchronize_featured_tags_collection_worker_spec.rb create mode 100644 spec/workers/admin/suspension_worker_spec.rb create mode 100644 spec/workers/after_account_domain_block_worker_spec.rb create mode 100644 spec/workers/backup_worker_spec.rb create mode 100644 spec/workers/delete_mute_worker_spec.rb create mode 100644 spec/workers/import_worker_spec.rb create mode 100644 spec/workers/publish_announcement_reaction_worker_spec.rb create mode 100644 spec/workers/removal_worker_spec.rb create mode 100644 spec/workers/scheduler/self_destruct_scheduler_spec.rb diff --git a/spec/fabricators/account_deletion_request_fabricator.rb b/spec/fabricators/account_deletion_request_fabricator.rb new file mode 100644 index 000000000..3d3d37398 --- /dev/null +++ b/spec/fabricators/account_deletion_request_fabricator.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +Fabricator(:account_deletion_request) do + account +end diff --git a/spec/fabricators/import_fabricator.rb b/spec/fabricators/import_fabricator.rb new file mode 100644 index 000000000..4951bb9a4 --- /dev/null +++ b/spec/fabricators/import_fabricator.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +Fabricator(:import) do + account + type :following + data { attachment_fixture('imports.txt') } +end diff --git a/spec/workers/account_refresh_worker_spec.rb b/spec/workers/account_refresh_worker_spec.rb new file mode 100644 index 000000000..361d69aa0 --- /dev/null +++ b/spec/workers/account_refresh_worker_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe AccountRefreshWorker do + let(:worker) { described_class.new } + let(:service) { instance_double(ResolveAccountService, call: true) } + + describe '#perform' do + before do + allow(ResolveAccountService).to receive(:new).and_return(service) + end + + context 'when account does not exist' do + it 'returns immediately without processing' do + worker.perform(123_123_123) + + expect(service).to_not have_received(:call) + end + end + + context 'when account exists' do + context 'when account does not need refreshing' do + let(:account) { Fabricate(:account, last_webfingered_at: recent_webfinger_at) } + + it 'returns immediately without processing' do + worker.perform(account.id) + + expect(service).to_not have_received(:call) + end + end + + context 'when account needs refreshing' do + let(:account) { Fabricate(:account, last_webfingered_at: outdated_webfinger_at) } + + it 'schedules an account update' do + worker.perform(account.id) + + expect(service).to have_received(:call) + end + end + + def recent_webfinger_at + (Account::BACKGROUND_REFRESH_INTERVAL - 3.days).ago + end + + def outdated_webfinger_at + (Account::BACKGROUND_REFRESH_INTERVAL + 3.days).ago + end + end + end +end diff --git a/spec/workers/activitypub/post_upgrade_worker_spec.rb b/spec/workers/activitypub/post_upgrade_worker_spec.rb new file mode 100644 index 000000000..08de150ad --- /dev/null +++ b/spec/workers/activitypub/post_upgrade_worker_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ActivityPub::PostUpgradeWorker do + let(:worker) { described_class.new } + + describe '#perform' do + let(:domain) { 'host.example' } + + it 'updates relevant values' do + account = Fabricate(:account, domain: domain, last_webfingered_at: 1.day.ago, protocol: :ostatus) + worker.perform(domain) + + expect(account.reload.last_webfingered_at).to be_nil + end + end +end diff --git a/spec/workers/activitypub/synchronize_featured_tags_collection_worker_spec.rb b/spec/workers/activitypub/synchronize_featured_tags_collection_worker_spec.rb new file mode 100644 index 000000000..8cf13cb90 --- /dev/null +++ b/spec/workers/activitypub/synchronize_featured_tags_collection_worker_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ActivityPub::SynchronizeFeaturedTagsCollectionWorker do + let(:worker) { described_class.new } + let(:service) { instance_double(ActivityPub::FetchFeaturedTagsCollectionService, call: true) } + + describe '#perform' do + before do + allow(ActivityPub::FetchFeaturedTagsCollectionService).to receive(:new).and_return(service) + end + + let(:account) { Fabricate(:account) } + let(:url) { 'https://host.example' } + + it 'sends the account and url to the service' do + worker.perform(account.id, url) + + expect(service).to have_received(:call).with(account, url) + end + + it 'returns true for non-existent record' do + result = worker.perform(123_123_123, url) + + expect(result).to be(true) + end + end +end diff --git a/spec/workers/admin/suspension_worker_spec.rb b/spec/workers/admin/suspension_worker_spec.rb new file mode 100644 index 000000000..da12037ed --- /dev/null +++ b/spec/workers/admin/suspension_worker_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::SuspensionWorker do + let(:worker) { described_class.new } + let(:service) { instance_double(SuspendAccountService, call: true) } + + describe '#perform' do + before do + allow(SuspendAccountService).to receive(:new).and_return(service) + end + + let(:account) { Fabricate(:account) } + + it 'sends the account to the service' do + worker.perform(account.id) + + expect(service).to have_received(:call).with(account) + end + + it 'returns true for non-existent record' do + result = worker.perform(123_123_123) + + expect(result).to be(true) + end + end +end diff --git a/spec/workers/after_account_domain_block_worker_spec.rb b/spec/workers/after_account_domain_block_worker_spec.rb new file mode 100644 index 000000000..54a113a2b --- /dev/null +++ b/spec/workers/after_account_domain_block_worker_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe AfterAccountDomainBlockWorker do + let(:worker) { described_class.new } + let(:service) { instance_double(AfterBlockDomainFromAccountService, call: true) } + + describe '#perform' do + before do + allow(AfterBlockDomainFromAccountService).to receive(:new).and_return(service) + end + + let(:account) { Fabricate(:account) } + let(:domain) { 'host.example' } + + it 'sends the account and domain to the service' do + worker.perform(account.id, domain) + + expect(service).to have_received(:call).with(account, domain) + end + + it 'returns true for non-existent record' do + result = worker.perform(123_123_123, domain) + + expect(result).to be(true) + end + end +end diff --git a/spec/workers/backup_worker_spec.rb b/spec/workers/backup_worker_spec.rb new file mode 100644 index 000000000..1a169513e --- /dev/null +++ b/spec/workers/backup_worker_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe BackupWorker do + let(:worker) { described_class.new } + let(:service) { instance_double(BackupService, call: true) } + + describe '#perform' do + before do + allow(BackupService).to receive(:new).and_return(service) + end + + let(:backup) { Fabricate(:backup) } + let!(:other_backup) { Fabricate(:backup, user: backup.user) } + + it 'sends the backup to the service and removes other backups' do + expect do + worker.perform(backup.id) + end.to change(UserMailer.deliveries, :size).by(1) + + expect(service).to have_received(:call).with(backup) + expect { other_backup.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + + context 'when sidekiq retries are exhausted' do + it 'destroys the backup' do + described_class.within_sidekiq_retries_exhausted_block({ 'args' => [backup.id] }) do + worker.perform(backup.id) + end + + expect { backup.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end +end diff --git a/spec/workers/delete_mute_worker_spec.rb b/spec/workers/delete_mute_worker_spec.rb new file mode 100644 index 000000000..1fc84491c --- /dev/null +++ b/spec/workers/delete_mute_worker_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe DeleteMuteWorker do + let(:worker) { described_class.new } + let(:service) { instance_double(UnmuteService, call: true) } + + describe '#perform' do + before do + allow(UnmuteService).to receive(:new).and_return(service) + end + + context 'with an expired mute' do + let(:mute) { Fabricate(:mute, expires_at: 1.day.ago) } + + it 'sends the mute to the service' do + worker.perform(mute.id) + + expect(service).to have_received(:call).with(mute.account, mute.target_account) + end + end + + context 'with an unexpired mute' do + let(:mute) { Fabricate(:mute, expires_at: 1.day.from_now) } + + it 'does not send the mute to the service' do + worker.perform(mute.id) + + expect(service).to_not have_received(:call) + end + end + + context 'with a non-existent mute' do + it 'does not send the mute to the service' do + worker.perform(123_123_123) + + expect(service).to_not have_received(:call) + end + end + end +end diff --git a/spec/workers/feed_insert_worker_spec.rb b/spec/workers/feed_insert_worker_spec.rb index 97c73c599..e9484879f 100644 --- a/spec/workers/feed_insert_worker_spec.rb +++ b/spec/workers/feed_insert_worker_spec.rb @@ -8,6 +8,7 @@ describe FeedInsertWorker do describe 'perform' do let(:follower) { Fabricate(:account) } let(:status) { Fabricate(:status) } + let(:list) { Fabricate(:list) } context 'when there are no records' do it 'skips push with missing status' do @@ -42,11 +43,29 @@ describe FeedInsertWorker do it 'pushes the status onto the home timeline without filter' do instance = instance_double(FeedManager, push_to_home: nil, filter?: false) allow(FeedManager).to receive(:instance).and_return(instance) - result = subject.perform(status.id, follower.id) + result = subject.perform(status.id, follower.id, :home) expect(result).to be_nil expect(instance).to have_received(:push_to_home).with(follower, status, update: nil) end + + it 'pushes the status onto the tags timeline without filter' do + instance = instance_double(FeedManager, push_to_home: nil, filter?: false) + allow(FeedManager).to receive(:instance).and_return(instance) + result = subject.perform(status.id, follower.id, :tags) + + expect(result).to be_nil + expect(instance).to have_received(:push_to_home).with(follower, status, update: nil) + end + + it 'pushes the status onto the list timeline without filter' do + instance = instance_double(FeedManager, push_to_list: nil, filter?: false) + allow(FeedManager).to receive(:instance).and_return(instance) + result = subject.perform(status.id, list.id, :list) + + expect(result).to be_nil + expect(instance).to have_received(:push_to_list).with(list, status, update: nil) + end end end end diff --git a/spec/workers/import_worker_spec.rb b/spec/workers/import_worker_spec.rb new file mode 100644 index 000000000..4095a5d35 --- /dev/null +++ b/spec/workers/import_worker_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ImportWorker do + let(:worker) { described_class.new } + let(:service) { instance_double(ImportService, call: true) } + + describe '#perform' do + before do + allow(ImportService).to receive(:new).and_return(service) + end + + let(:import) { Fabricate(:import) } + + it 'sends the import to the service' do + worker.perform(import.id) + + expect(service).to have_received(:call).with(import) + expect { import.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + end +end diff --git a/spec/workers/post_process_media_worker_spec.rb b/spec/workers/post_process_media_worker_spec.rb index 33072704b..828da5244 100644 --- a/spec/workers/post_process_media_worker_spec.rb +++ b/spec/workers/post_process_media_worker_spec.rb @@ -2,12 +2,38 @@ require 'rails_helper' -describe PostProcessMediaWorker do +describe PostProcessMediaWorker, :paperclip_processing do let(:worker) { described_class.new } - describe 'perform' do - it 'runs without error for missing record' do - expect { worker.perform(nil) }.to_not raise_error + describe '#perform' do + let(:media_attachment) { Fabricate(:media_attachment) } + + it 'reprocesses and updates the media attachment' do + worker.perform(media_attachment.id) + + expect(media_attachment.processing).to eq('complete') + end + + it 'returns true for non-existent record' do + result = worker.perform(123_123_123) + + expect(result).to be(true) + end + + context 'when sidekiq retries are exhausted' do + it 'sets state to failed' do + described_class.within_sidekiq_retries_exhausted_block({ 'args' => [media_attachment.id] }) do + worker.perform(media_attachment.id) + end + + expect(media_attachment.reload.processing).to eq('failed') + end + + it 'returns true for non-existent record' do + described_class.within_sidekiq_retries_exhausted_block({ 'args' => [123_123_123] }) do + expect(worker.perform(123_123_123)).to be(true) + end + end end end end diff --git a/spec/workers/publish_announcement_reaction_worker_spec.rb b/spec/workers/publish_announcement_reaction_worker_spec.rb new file mode 100644 index 000000000..91668b5ad --- /dev/null +++ b/spec/workers/publish_announcement_reaction_worker_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe PublishAnnouncementReactionWorker do + let(:worker) { described_class.new } + + describe '#perform' do + before { Fabricate(:account, user: Fabricate(:user, current_sign_in_at: 1.hour.ago)) } + + let(:announcement) { Fabricate(:announcement) } + let(:name) { 'name value' } + + it 'sends the announcement and name to the service when subscribed' do + allow(redis).to receive(:exists?).and_return(true) + allow(redis).to receive(:publish) + + worker.perform(announcement.id, name) + + expect(redis).to have_received(:publish) + end + + it 'does not send the announcement and name to the service when not subscribed' do + allow(redis).to receive(:exists?).and_return(false) + allow(redis).to receive(:publish) + + worker.perform(announcement.id, name) + + expect(redis).to_not have_received(:publish) + end + + it 'returns true for non-existent record' do + result = worker.perform(123_123_123, name) + + expect(result).to be(true) + end + end +end diff --git a/spec/workers/removal_worker_spec.rb b/spec/workers/removal_worker_spec.rb new file mode 100644 index 000000000..5071e882b --- /dev/null +++ b/spec/workers/removal_worker_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe RemovalWorker do + let(:worker) { described_class.new } + let(:service) { instance_double(RemoveStatusService, call: true) } + + describe '#perform' do + before do + allow(RemoveStatusService).to receive(:new).and_return(service) + end + + let(:status) { Fabricate(:status) } + + it 'sends the status to the service' do + worker.perform(status.id) + + expect(service).to have_received(:call).with(status) + end + + it 'returns true for non-existent record' do + result = worker.perform(123_123_123) + + expect(result).to be(true) + end + end +end diff --git a/spec/workers/scheduler/self_destruct_scheduler_spec.rb b/spec/workers/scheduler/self_destruct_scheduler_spec.rb new file mode 100644 index 000000000..2bf578357 --- /dev/null +++ b/spec/workers/scheduler/self_destruct_scheduler_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Scheduler::SelfDestructScheduler do + let(:worker) { described_class.new } + + describe '#perform' do + let!(:account) { Fabricate(:account, domain: nil, suspended_at: nil) } + + context 'when not in self destruct mode' do + before do + allow(SelfDestructHelper).to receive(:self_destruct?).and_return(false) + end + + it 'returns without processing' do + worker.perform + + expect(account.reload.suspended_at).to be_nil + end + end + + context 'when in self-destruct mode' do + before do + allow(SelfDestructHelper).to receive(:self_destruct?).and_return(true) + end + + context 'when sidekiq is overwhelmed' do + before do + stats = instance_double(Sidekiq::Stats, enqueued: described_class::MAX_ENQUEUED**2) + allow(Sidekiq::Stats).to receive(:new).and_return(stats) + end + + it 'returns without processing' do + worker.perform + + expect(account.reload.suspended_at).to be_nil + end + end + + context 'when sidekiq is operational' do + it 'suspends local non-suspended accounts' do + worker.perform + + expect(account.reload.suspended_at).to_not be_nil + end + + it 'suspends local suspended accounts marked for deletion' do + account.update(suspended_at: 10.days.ago) + deletion_request = Fabricate(:account_deletion_request, account: account) + + worker.perform + + expect(account.reload.suspended_at).to be > 1.day.ago + expect { deletion_request.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end + end +end diff --git a/spec/workers/webhooks/delivery_worker_spec.rb b/spec/workers/webhooks/delivery_worker_spec.rb index daf8a3e28..6a5483d1d 100644 --- a/spec/workers/webhooks/delivery_worker_spec.rb +++ b/spec/workers/webhooks/delivery_worker_spec.rb @@ -5,9 +5,21 @@ require 'rails_helper' describe Webhooks::DeliveryWorker do let(:worker) { described_class.new } - describe 'perform' do - it 'runs without error' do - expect { worker.perform(nil, nil) }.to_not raise_error + describe '#perform' do + let(:webhook) { Fabricate(:webhook) } + + it 'reprocesses and updates the webhook' do + stub_request(:post, webhook.url).to_return(status: 200, body: '') + + worker.perform(webhook.id, 'body') + + expect(a_request(:post, webhook.url)).to have_been_made.at_least_once + end + + it 'returns true for non-existent record' do + result = worker.perform(123_123_123, '') + + expect(result).to be(true) end end end From cb1a4a8713499a0ca7d8e53ce5cfd32939247f0c Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Thu, 16 Nov 2023 09:37:52 -0500 Subject: [PATCH 53/63] Reduce expectations for `RSpec/MultipleExpectations` cop in `spec/presenters` specs (#27881) --- .../account_relationships_presenter_spec.rb | 14 +++-- .../familiar_followers_presenter_spec.rb | 27 ++++++--- .../status_relationships_presenter_spec.rb | 60 +++++++++++++------ 3 files changed, 69 insertions(+), 32 deletions(-) diff --git a/spec/presenters/account_relationships_presenter_spec.rb b/spec/presenters/account_relationships_presenter_spec.rb index 5c2ba54e0..5b05ac800 100644 --- a/spec/presenters/account_relationships_presenter_spec.rb +++ b/spec/presenters/account_relationships_presenter_spec.rb @@ -23,12 +23,14 @@ RSpec.describe AccountRelationshipsPresenter do let(:options) { {} } it 'sets default maps' do - expect(presenter.following).to eq default_map - expect(presenter.followed_by).to eq default_map - expect(presenter.blocking).to eq default_map - expect(presenter.muting).to eq default_map - expect(presenter.requested).to eq default_map - expect(presenter.domain_blocking).to eq default_map + expect(presenter).to have_attributes( + following: default_map, + followed_by: default_map, + blocking: default_map, + muting: default_map, + requested: default_map, + domain_blocking: default_map + ) end end diff --git a/spec/presenters/familiar_followers_presenter_spec.rb b/spec/presenters/familiar_followers_presenter_spec.rb index c21ffd36e..853babb84 100644 --- a/spec/presenters/familiar_followers_presenter_spec.rb +++ b/spec/presenters/familiar_followers_presenter_spec.rb @@ -22,9 +22,12 @@ RSpec.describe FamiliarFollowersPresenter do it 'returns followers you follow' do result = subject.accounts.first - expect(result).to_not be_nil - expect(result.id).to eq requested_accounts.first.id - expect(result.accounts).to contain_exactly(familiar_follower) + expect(result) + .to be_present + .and have_attributes( + id: requested_accounts.first.id, + accounts: contain_exactly(familiar_follower) + ) end context 'when requested account hides followers' do @@ -35,9 +38,12 @@ RSpec.describe FamiliarFollowersPresenter do it 'does not return followers you follow' do result = subject.accounts.first - expect(result).to_not be_nil - expect(result.id).to eq requested_accounts.first.id - expect(result.accounts).to be_empty + expect(result) + .to be_present + .and have_attributes( + id: requested_accounts.first.id, + accounts: be_empty + ) end end @@ -49,9 +55,12 @@ RSpec.describe FamiliarFollowersPresenter do it 'does not return followers you follow' do result = subject.accounts.first - expect(result).to_not be_nil - expect(result.id).to eq requested_accounts.first.id - expect(result.accounts).to be_empty + expect(result) + .to be_present + .and have_attributes( + id: requested_accounts.first.id, + accounts: be_empty + ) end end end diff --git a/spec/presenters/status_relationships_presenter_spec.rb b/spec/presenters/status_relationships_presenter_spec.rb index 7746c8cd7..af6a93b82 100644 --- a/spec/presenters/status_relationships_presenter_spec.rb +++ b/spec/presenters/status_relationships_presenter_spec.rb @@ -22,11 +22,13 @@ RSpec.describe StatusRelationshipsPresenter do let(:options) { {} } it 'sets default maps' do - expect(presenter.reblogs_map).to eq default_map - expect(presenter.favourites_map).to eq default_map - expect(presenter.bookmarks_map).to eq default_map - expect(presenter.mutes_map).to eq default_map - expect(presenter.pins_map).to eq default_map + expect(presenter).to have_attributes( + reblogs_map: eq(default_map), + favourites_map: eq(default_map), + bookmarks_map: eq(default_map), + mutes_map: eq(default_map), + pins_map: eq(default_map) + ) end end @@ -80,18 +82,30 @@ RSpec.describe StatusRelationshipsPresenter do it 'sets @filters_map to filter top-level status' do matched_filters = presenter.filters_map[statuses[0].id] - expect(matched_filters.size).to eq 1 - expect(matched_filters[0].filter.title).to eq 'filter1' - expect(matched_filters[0].keyword_matches).to eq ['banned'] + expect(matched_filters) + .to be_an(Array) + .and have_attributes(size: 1) + .and contain_exactly( + have_attributes( + filter: have_attributes(title: 'filter1'), + keyword_matches: contain_exactly('banned') + ) + ) end it 'sets @filters_map to filter reblogged status' do matched_filters = presenter.filters_map[statuses[1].reblog_of_id] - expect(matched_filters.size).to eq 1 - expect(matched_filters[0].filter.title).to eq 'filter1' - expect(matched_filters[0].keyword_matches).to eq ['irrelevant'] + expect(matched_filters) + .to be_an(Array) + .and have_attributes(size: 1) + .and contain_exactly( + have_attributes( + filter: have_attributes(title: 'filter1'), + keyword_matches: contain_exactly('irrelevant') + ) + ) end end @@ -107,18 +121,30 @@ RSpec.describe StatusRelationshipsPresenter do it 'sets @filters_map to filter top-level status' do matched_filters = presenter.filters_map[statuses[0].id] - expect(matched_filters.size).to eq 1 - expect(matched_filters[0].filter.title).to eq 'filter1' - expect(matched_filters[0].status_matches).to eq [statuses[0].id] + expect(matched_filters) + .to be_an(Array) + .and have_attributes(size: 1) + .and contain_exactly( + have_attributes( + filter: have_attributes(title: 'filter1'), + status_matches: contain_exactly(statuses.first.id) + ) + ) end it 'sets @filters_map to filter reblogged status' do matched_filters = presenter.filters_map[statuses[1].reblog_of_id] - expect(matched_filters.size).to eq 1 - expect(matched_filters[0].filter.title).to eq 'filter1' - expect(matched_filters[0].status_matches).to eq [statuses[1].reblog_of_id] + expect(matched_filters) + .to be_an(Array) + .and have_attributes(size: 1) + .and contain_exactly( + have_attributes( + filter: have_attributes(title: 'filter1'), + status_matches: contain_exactly(statuses.second.reblog_of_id) + ) + ) end end end From 94178e2f68db9d23b1be1f3b39489564fcb9d50e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 17 Nov 2023 09:56:23 +0100 Subject: [PATCH 54/63] Update dependency react-redux-loading-bar to v5.0.5 (#27916) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index 32c2546e7..b55bbde3e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13777,17 +13777,17 @@ __metadata: linkType: hard "react-redux-loading-bar@npm:^5.0.4": - version: 5.0.4 - resolution: "react-redux-loading-bar@npm:5.0.4" + version: 5.0.5 + resolution: "react-redux-loading-bar@npm:5.0.5" dependencies: prop-types: "npm:^15.7.2" react-lifecycles-compat: "npm:^3.0.4" peerDependencies: react: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 react-dom: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - react-redux: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - redux: ^3.0.0 || ^4.0.0 - checksum: 11eea2ef6dfae232e278eceb83d07f9f57a2ece3ef23ce888dccf24964b669c9ee83a6db12b1f3c757b5b3410f7a7ccda96a4b4216c4ad9b42bf831ccea7a4a2 + react-redux: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + redux: ^3.0.0 || ^4.0.0 || ^5.0.0 + checksum: 0dbac046c5b8b6bd209ccfc25ccc55dc9158cd737b42b68fd1900dfe46a59c9c7e2b0082d8901b749e7cf2d7e23074590aae74f350a814f205105f47895a6214 languageName: node linkType: hard From 6c2e78f1b11874583ae9e6988cf6d2a2eb1e1b71 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 17 Nov 2023 09:56:39 +0100 Subject: [PATCH 55/63] Update dependency @material-symbols/svg-600 to v0.14.1 (#27907) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index b55bbde3e..864ebfc87 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2550,9 +2550,9 @@ __metadata: linkType: soft "@material-symbols/svg-600@npm:^0.14.0": - version: 0.14.0 - resolution: "@material-symbols/svg-600@npm:0.14.0" - checksum: e6547a9a0b2072f4109f2e4e0863367ea2507efce740c427a8544100db02ffff52f33608aac1a355f4977e2c0b2ce6cdd6bfee9177bb13cee0b28418f948b5a5 + version: 0.14.1 + resolution: "@material-symbols/svg-600@npm:0.14.1" + checksum: fb5252285bbeccc45a4b131e8b165470b5b57e146bc7ea586eb82e580037d1218f6dad5fee4e6822c357041ff547f34c9c7432cce0a811b14f7e41d8ae23009b languageName: node linkType: hard From 1526e54ac6f08431c8c833f20b4be8882355967c Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Fri, 17 Nov 2023 04:03:46 -0500 Subject: [PATCH 56/63] Add spec coverage for `workers/redownload_*` worker classes (#27892) --- spec/workers/redownload_avatar_worker_spec.rb | 45 +++++++++++++++++-- spec/workers/redownload_header_worker_spec.rb | 45 +++++++++++++++++-- spec/workers/redownload_media_worker_spec.rb | 37 +++++++++++++++ 3 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 spec/workers/redownload_media_worker_spec.rb diff --git a/spec/workers/redownload_avatar_worker_spec.rb b/spec/workers/redownload_avatar_worker_spec.rb index b44ae9f03..4ab368e12 100644 --- a/spec/workers/redownload_avatar_worker_spec.rb +++ b/spec/workers/redownload_avatar_worker_spec.rb @@ -5,9 +5,48 @@ require 'rails_helper' describe RedownloadAvatarWorker do let(:worker) { described_class.new } - describe 'perform' do - it 'runs without error for missing record' do - expect { worker.perform(nil) }.to_not raise_error + describe '#perform' do + it 'returns nil for non-existent record' do + result = worker.perform(123_123_123) + + expect(result).to be_nil + end + + it 'returns nil for suspended account' do + account = Fabricate(:account, suspended_at: 10.days.ago) + + expect(worker.perform(account.id)).to be_nil + end + + it 'returns nil with a domain block' do + account = Fabricate(:account, domain: 'host.example') + Fabricate(:domain_block, domain: account.domain, reject_media: true) + + expect(worker.perform(account.id)).to be_nil + end + + it 'returns nil without an avatar remote url' do + account = Fabricate(:account, avatar_remote_url: '') + + expect(worker.perform(account.id)).to be_nil + end + + it 'returns nil when avatar file name is present' do + stub_request(:get, 'https://example.host/file').to_return request_fixture('avatar.txt') + account = Fabricate(:account, avatar_remote_url: 'https://example.host/file', avatar_file_name: 'test.jpg') + + expect(worker.perform(account.id)).to be_nil + end + + it 'reprocesses a remote avatar' do + stub_request(:get, 'https://example.host/file').to_return request_fixture('avatar.txt') + account = Fabricate(:account, avatar_remote_url: 'https://example.host/file') + account.update_column(:avatar_file_name, nil) # rubocop:disable Rails/SkipsModelValidations + + result = worker.perform(account.id) + + expect(result).to be(true) + expect(account.reload.avatar_file_name).to_not be_nil end end end diff --git a/spec/workers/redownload_header_worker_spec.rb b/spec/workers/redownload_header_worker_spec.rb index 767ae7a5a..3b6f497bb 100644 --- a/spec/workers/redownload_header_worker_spec.rb +++ b/spec/workers/redownload_header_worker_spec.rb @@ -5,9 +5,48 @@ require 'rails_helper' describe RedownloadHeaderWorker do let(:worker) { described_class.new } - describe 'perform' do - it 'runs without error for missing record' do - expect { worker.perform(nil) }.to_not raise_error + describe '#perform' do + it 'returns nil for non-existent record' do + result = worker.perform(123_123_123) + + expect(result).to be_nil + end + + it 'returns nil for suspended account' do + account = Fabricate(:account, suspended_at: 10.days.ago) + + expect(worker.perform(account.id)).to be_nil + end + + it 'returns nil with a domain block' do + account = Fabricate(:account, domain: 'host.example') + Fabricate(:domain_block, domain: account.domain, reject_media: true) + + expect(worker.perform(account.id)).to be_nil + end + + it 'returns nil without an header remote url' do + account = Fabricate(:account, header_remote_url: '') + + expect(worker.perform(account.id)).to be_nil + end + + it 'returns nil when header file name is present' do + stub_request(:get, 'https://example.host/file').to_return request_fixture('avatar.txt') + account = Fabricate(:account, header_remote_url: 'https://example.host/file', header_file_name: 'test.jpg') + + expect(worker.perform(account.id)).to be_nil + end + + it 'reprocesses a remote header' do + stub_request(:get, 'https://example.host/file').to_return request_fixture('avatar.txt') + account = Fabricate(:account, header_remote_url: 'https://example.host/file') + account.update_column(:header_file_name, nil) # rubocop:disable Rails/SkipsModelValidations + + result = worker.perform(account.id) + + expect(result).to be(true) + expect(account.reload.header_file_name).to_not be_nil end end end diff --git a/spec/workers/redownload_media_worker_spec.rb b/spec/workers/redownload_media_worker_spec.rb new file mode 100644 index 000000000..cd561d148 --- /dev/null +++ b/spec/workers/redownload_media_worker_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe RedownloadMediaWorker do + let(:worker) { described_class.new } + + describe '#perform' do + it 'returns nil for non-existent record' do + result = worker.perform(123_123_123) + + expect(result).to be_nil + end + + it 'returns nil without a remote_url' do + media_attachment = Fabricate(:media_attachment, remote_url: '') + + result = worker.perform(media_attachment.id) + + expect(result).to be_nil + end + + context 'with a valid remote url' do + let(:url) { 'https://example.host/file.txt' } + + before { stub_request(:get, url).to_return(status: 200) } + + it 'processes downloads for valid record' do + media_attachment = Fabricate(:media_attachment, remote_url: url) + + worker.perform(media_attachment.id) + + expect(a_request(:get, url)).to have_been_made + end + end + end +end From 9c68741f464b7cffa8b70a6be38cd333a795672b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 17 Nov 2023 10:16:12 +0100 Subject: [PATCH 57/63] New Crowdin Translations (automated) (#27914) Co-authored-by: GitHub Actions <noreply@github.com> --- app/javascript/mastodon/locales/be.json | 2 ++ app/javascript/mastodon/locales/cs.json | 2 ++ app/javascript/mastodon/locales/cy.json | 7 ++++++ app/javascript/mastodon/locales/da.json | 2 ++ app/javascript/mastodon/locales/de.json | 2 ++ app/javascript/mastodon/locales/es-AR.json | 2 ++ app/javascript/mastodon/locales/es-MX.json | 2 ++ app/javascript/mastodon/locales/es.json | 12 ++++++++++ app/javascript/mastodon/locales/eu.json | 2 ++ app/javascript/mastodon/locales/fi.json | 2 ++ app/javascript/mastodon/locales/fr-QC.json | 4 ++++ app/javascript/mastodon/locales/fr.json | 6 ++++- app/javascript/mastodon/locales/gl.json | 12 ++++++++++ app/javascript/mastodon/locales/he.json | 2 ++ app/javascript/mastodon/locales/hu.json | 14 +++++++----- app/javascript/mastodon/locales/is.json | 2 ++ app/javascript/mastodon/locales/it.json | 2 ++ app/javascript/mastodon/locales/ko.json | 2 ++ app/javascript/mastodon/locales/lt.json | 26 +++++++++++++++++++--- app/javascript/mastodon/locales/nl.json | 2 ++ app/javascript/mastodon/locales/nn.json | 24 +++++++++++--------- app/javascript/mastodon/locales/no.json | 2 ++ app/javascript/mastodon/locales/pl.json | 2 ++ app/javascript/mastodon/locales/pt-PT.json | 2 ++ app/javascript/mastodon/locales/sq.json | 2 ++ app/javascript/mastodon/locales/sr.json | 3 +++ app/javascript/mastodon/locales/sv.json | 2 ++ app/javascript/mastodon/locales/th.json | 10 +++++---- app/javascript/mastodon/locales/uk.json | 2 ++ app/javascript/mastodon/locales/zh-CN.json | 2 ++ app/javascript/mastodon/locales/zh-HK.json | 2 ++ app/javascript/mastodon/locales/zh-TW.json | 2 ++ config/locales/es.yml | 1 + config/locales/eu.yml | 1 + config/locales/gl.yml | 3 ++- config/locales/hu.yml | 2 +- config/locales/ko.yml | 2 +- config/locales/nn.yml | 26 +++++++++++----------- config/locales/no.yml | 2 +- config/locales/sr.yml | 1 + config/locales/th.yml | 4 ++-- config/locales/tr.yml | 1 + config/locales/zh-CN.yml | 1 + 43 files changed, 162 insertions(+), 44 deletions(-) diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json index 4f5b247b6..59b1ca50b 100644 --- a/app/javascript/mastodon/locales/be.json +++ b/app/javascript/mastodon/locales/be.json @@ -21,6 +21,7 @@ "account.blocked": "Заблакіраваны", "account.browse_more_on_origin_server": "Глядзіце больш у арыгінальным профілі", "account.cancel_follow_request": "Скасаваць запыт на падпіску", + "account.copy": "Скапіраваць спасылку на профіль", "account.direct": "Згадаць асабіста @{name}", "account.disable_notifications": "Не паведамляць мне пра публікацыі @{name}", "account.domain_blocked": "Дамен заблакаваны", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Адзначыць прачытаным", "conversation.open": "Прагледзець размову", "conversation.with": "З {names}", + "copy_icon_button.copied": "Скапіявана ў буфер абмену", "copypaste.copied": "Скапіравана", "copypaste.copy_to_clipboard": "Капіраваць у буфер абмену", "directory.federated": "З вядомага федэсвету", diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json index 20ef43733..16bf5020c 100644 --- a/app/javascript/mastodon/locales/cs.json +++ b/app/javascript/mastodon/locales/cs.json @@ -21,6 +21,7 @@ "account.blocked": "Blokovaný", "account.browse_more_on_origin_server": "Více na původním profilu", "account.cancel_follow_request": "Zrušit žádost o sledování", + "account.copy": "Kopírovat odkaz na profil", "account.direct": "Soukromě zmínit @{name}", "account.disable_notifications": "Přestat mě upozorňovat, když @{name} zveřejní příspěvek", "account.domain_blocked": "Doména blokována", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Označit jako přečtené", "conversation.open": "Zobrazit konverzaci", "conversation.with": "S {names}", + "copy_icon_button.copied": "Zkopírováno do schránky", "copypaste.copied": "Zkopírováno", "copypaste.copy_to_clipboard": "Zkopírovat do schránky", "directory.federated": "Ze známého fedivesmíru", diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json index dcd5406d0..11d4de440 100644 --- a/app/javascript/mastodon/locales/cy.json +++ b/app/javascript/mastodon/locales/cy.json @@ -191,6 +191,7 @@ "conversation.mark_as_read": "Nodi fel wedi'i ddarllen", "conversation.open": "Gweld sgwrs", "conversation.with": "Gyda {names}", + "copy_icon_button.copied": "Copïwyd i'r clipfwrdd", "copypaste.copied": "Wedi ei gopïo", "copypaste.copy_to_clipboard": "Copïo i'r clipfwrdd", "directory.federated": "O'r ffedysawd cyfan", @@ -390,6 +391,7 @@ "lists.search": "Chwilio ymysg pobl rydych yn eu dilyn", "lists.subheading": "Eich rhestrau", "load_pending": "{count, plural, one {# eitem newydd} other {# eitem newydd}}", + "loading_indicator.label": "Yn llwytho…", "media_gallery.toggle_visible": "{number, plural, one {Cuddio delwedd} other {Cuddio delwedd}}", "moved_to_account_banner.text": "Ar hyn y bryd, mae eich cyfrif {disabledAccount} wedi ei analluogi am i chi symud i {movedToAccount}.", "mute_modal.duration": "Hyd", @@ -478,6 +480,11 @@ "onboarding.follows.empty": "Yn anffodus, nid oes modd dangos unrhyw ganlyniadau ar hyn o bryd. Gallwch geisio defnyddio chwilio neu bori'r dudalen archwilio i ddod o hyd i bobl i'w dilyn, neu ceisio eto yn nes ymlaen.", "onboarding.follows.lead": "Rydych chi'n curadu eich ffrwd gartref eich hun. Po fwyaf o bobl y byddwch chi'n eu dilyn, y mwyaf egnïol a diddorol fydd hi. Gall y proffiliau hyn fod yn fan cychwyn da - gallwch chi bob amser eu dad-ddilyn yn nes ymlaen:", "onboarding.follows.title": "Yn boblogaidd ar Mastodon", + "onboarding.profile.display_name_hint": "Eich enw llawn neu'ch enw hwyl…", + "onboarding.profile.note": "Bywgraffiad", + "onboarding.profile.note_hint": "Gallwch @grybwyll pobl eraill neu #hashnodau…", + "onboarding.profile.save_and_continue": "Cadw a pharhau", + "onboarding.profile.title": "Gosodiad proffil", "onboarding.share.lead": "Cofiwch ddweud wrth bobl sut y gallan nhw ddod o hyd i chi ar Mastodon!", "onboarding.share.message": "Fi yw {username} ar #Mastodon! Dewch i'm dilyn i yn {url}", "onboarding.share.next_steps": "Camau nesaf posib:", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index 33eda6f43..bde12a7ae 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -21,6 +21,7 @@ "account.blocked": "Blokeret", "account.browse_more_on_origin_server": "Se mere på den oprindelige profil", "account.cancel_follow_request": "Annullér anmodning om at følge", + "account.copy": "Kopiér link til profil", "account.direct": "Privat omtale @{name}", "account.disable_notifications": "Advisér mig ikke længere, når @{name} poster", "account.domain_blocked": "Domæne blokeret", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Markér som læst", "conversation.open": "Vis samtale", "conversation.with": "Med {names}", + "copy_icon_button.copied": "Kopieret til udklipsholderen", "copypaste.copied": "Kopieret", "copypaste.copy_to_clipboard": "Kopiér til udklipsholder", "directory.federated": "Fra kendt fedivers", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 5c1ccf46d..10fd9b4c5 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -21,6 +21,7 @@ "account.blocked": "Blockiert", "account.browse_more_on_origin_server": "Mehr auf dem Originalprofil durchsuchen", "account.cancel_follow_request": "Folgeanfrage zurückziehen", + "account.copy": "Link zum Profil kopieren", "account.direct": "@{name} privat erwähnen", "account.disable_notifications": "Höre auf mich zu benachrichtigen wenn @{name} etwas postet", "account.domain_blocked": "Domain versteckt", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Als gelesen markieren", "conversation.open": "Unterhaltung anzeigen", "conversation.with": "Mit {names}", + "copy_icon_button.copied": "In die Zwischenablage kopiert", "copypaste.copied": "Kopiert", "copypaste.copy_to_clipboard": "In die Zwischenablage kopieren", "directory.federated": "Aus bekanntem Fediverse", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index 680336369..3c99334c3 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -21,6 +21,7 @@ "account.blocked": "Bloqueado", "account.browse_more_on_origin_server": "Explorar más en el perfil original", "account.cancel_follow_request": "Dejar de seguir", + "account.copy": "Copiar enlace al perfil", "account.direct": "Mención privada a @{name}", "account.disable_notifications": "Dejar de notificarme cuando @{name} envíe mensajes", "account.domain_blocked": "Dominio bloqueado", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Marcar como leída", "conversation.open": "Ver conversación", "conversation.with": "Con {names}", + "copy_icon_button.copied": "Copiado en el portapapeles", "copypaste.copied": "Copiado", "copypaste.copy_to_clipboard": "Copiar al portapapeles", "directory.federated": "Desde fediverso conocido", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index aa8a21edb..70c4efcad 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -21,6 +21,7 @@ "account.blocked": "Bloqueado", "account.browse_more_on_origin_server": "Ver más en el perfil original", "account.cancel_follow_request": "Retirar solicitud de seguimiento", + "account.copy": "Copiar enlace al perfil", "account.direct": "Mención privada @{name}", "account.disable_notifications": "Dejar de notificarme cuando @{name} publique algo", "account.domain_blocked": "Dominio oculto", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Marcar como leído", "conversation.open": "Ver conversación", "conversation.with": "Con {names}", + "copy_icon_button.copied": "Copiado al portapapeles", "copypaste.copied": "Copiado", "copypaste.copy_to_clipboard": "Copiar al portapapeles", "directory.federated": "Desde el fediverso conocido", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 5d1aa004b..3c0d8cd57 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -21,6 +21,7 @@ "account.blocked": "Bloqueado", "account.browse_more_on_origin_server": "Ver más en el perfil original", "account.cancel_follow_request": "Retirar solicitud de seguimiento", + "account.copy": "Copiar enlace al perfil", "account.direct": "Mención privada a @{name}", "account.disable_notifications": "Dejar de notificarme cuando @{name} publique algo", "account.domain_blocked": "Dominio bloqueado", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Marcar como leído", "conversation.open": "Ver conversación", "conversation.with": "Con {names}", + "copy_icon_button.copied": "Copiado al portapapeles", "copypaste.copied": "Copiado", "copypaste.copy_to_clipboard": "Copiar al portapapeles", "directory.federated": "Desde el fediverso conocido", @@ -479,7 +481,17 @@ "onboarding.follows.empty": "Desafortunadamente, no se pueden mostrar resultados en este momento. Puedes intentar usar la búsqueda o navegar por la página de exploración para encontrar personas a las que seguir, o inténtalo de nuevo más tarde.", "onboarding.follows.lead": "Tu línea de inicio es la forma principal de experimentar Mastodon. Cuanta más personas sigas, más activa e interesante será. Para empezar, aquí hay algunas sugerencias:", "onboarding.follows.title": "Personaliza tu línea de inicio", + "onboarding.profile.discoverable": "Destacar perfil y publicaciones en algoritmos de descubrimiento", "onboarding.profile.display_name": "Nombre para mostrar", + "onboarding.profile.display_name_hint": "Tu nombre completo o tu apodo…", + "onboarding.profile.indexable": "Incluir publicaciones públicas en los resultados de búsqueda", + "onboarding.profile.lead": "Siempre puedes completar esto más tarde en los ajustes, donde hay aún más opciones de personalización disponibles.", + "onboarding.profile.note": "Biografía", + "onboarding.profile.note_hint": "Puedes @mencionar a otras personas o #etiquetas…", + "onboarding.profile.save_and_continue": "Guardar y continuar", + "onboarding.profile.title": "Configuración del perfil", + "onboarding.profile.upload_avatar": "Subir foto de perfil", + "onboarding.profile.upload_header": "Subir encabezado de perfil", "onboarding.share.lead": "¡Cuéntale a otras personas cómo te pueden encontrar en Mastodon!", "onboarding.share.message": "¡Soy {username} en #Mastodon! Ven a seguirme en {url}", "onboarding.share.next_steps": "Posibles siguientes pasos:", diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json index 88f70cffb..5bca1cfef 100644 --- a/app/javascript/mastodon/locales/eu.json +++ b/app/javascript/mastodon/locales/eu.json @@ -21,6 +21,7 @@ "account.blocked": "Blokeatuta", "account.browse_more_on_origin_server": "Arakatu gehiago jatorrizko profilean", "account.cancel_follow_request": "Baztertu jarraitzeko eskaera", + "account.copy": "Kopiatu profilerako esteka", "account.direct": "Aipatu pribatuki @{name}", "account.disable_notifications": "Utzi jakinarazteari @{name} erabiltzailearen bidalketetan", "account.domain_blocked": "Ezkutatutako domeinua", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Markatu irakurrita bezala", "conversation.open": "Ikusi elkarrizketa", "conversation.with": "Hauekin: {names}", + "copy_icon_button.copied": "Arbelera kopiatu da", "copypaste.copied": "Kopiatuta", "copypaste.copy_to_clipboard": "Kopiatu arbelera", "directory.federated": "Fedibertso ezagunekoak", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 849a7f463..344e37c97 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -21,6 +21,7 @@ "account.blocked": "Estetty", "account.browse_more_on_origin_server": "Selaile lisää alkuperäisellä palvelimella", "account.cancel_follow_request": "Peruuta seurantapyyntö", + "account.copy": "Kopioi profiilin linkki", "account.direct": "Mainitse @{name} yksityisesti", "account.disable_notifications": "Lopeta ilmoittamasta minulle, kun @{name} julkaisee", "account.domain_blocked": "Verkkotunnus estetty", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Merkitse luetuksi", "conversation.open": "Näytä keskustelu", "conversation.with": "{names} kanssa", + "copy_icon_button.copied": "Kopioitu leikepöydälle", "copypaste.copied": "Kopioitu", "copypaste.copy_to_clipboard": "Kopioi leikepöydälle", "directory.federated": "Koko tunnettu fediversumi", diff --git a/app/javascript/mastodon/locales/fr-QC.json b/app/javascript/mastodon/locales/fr-QC.json index e858882f8..d85a6d428 100644 --- a/app/javascript/mastodon/locales/fr-QC.json +++ b/app/javascript/mastodon/locales/fr-QC.json @@ -390,6 +390,7 @@ "lists.search": "Rechercher parmi les gens que vous suivez", "lists.subheading": "Vos listes", "load_pending": "{count, plural, one {# nouvel élément} other {# nouveaux éléments}}", + "loading_indicator.label": "Chargement…", "media_gallery.toggle_visible": "{number, plural, one {Cacher l’image} other {Cacher les images}}", "moved_to_account_banner.text": "Votre compte {disabledAccount} est actuellement désactivé parce que vous avez déménagé sur {movedToAccount}.", "mute_modal.duration": "Durée", @@ -478,6 +479,9 @@ "onboarding.follows.empty": "Malheureusement, aucun résultat ne peut être affiché pour le moment. Vous pouvez essayer de rechercher ou de parcourir la page \"Explorer\" pour trouver des personnes à suivre, ou réessayer plus tard.", "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", "onboarding.follows.title": "Popular on Mastodon", + "onboarding.profile.discoverable": "Autoriser des algorithmes de découverte à mettre en avant votre profil et vos messages", + "onboarding.profile.save_and_continue": "Enregistrer et continuer", + "onboarding.profile.upload_avatar": "Importer une photo de profil", "onboarding.share.lead": "Faites savoir aux gens comment vous trouver sur Mastodon!", "onboarding.share.message": "Je suis {username} sur #Mastodon! Suivez-moi sur {url}", "onboarding.share.next_steps": "Étapes suivantes possibles:", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index a7bb4a12f..14e0e13ba 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -107,7 +107,7 @@ "closed_registrations_modal.preamble": "Mastodon est décentralisé : peu importe où vous créez votre compte, vous serez en mesure de suivre et d'interagir avec quiconque sur ce serveur. Vous pouvez même l'héberger !", "closed_registrations_modal.title": "Inscription sur Mastodon", "column.about": "À propos", - "column.blocks": "Comptes bloqués", + "column.blocks": "Utilisateurs bloqués", "column.bookmarks": "Marque-pages", "column.community": "Fil public local", "column.direct": "Mentions privées", @@ -390,6 +390,7 @@ "lists.search": "Rechercher parmi les gens que vous suivez", "lists.subheading": "Vos listes", "load_pending": "{count, plural, one {# nouvel élément} other {# nouveaux éléments}}", + "loading_indicator.label": "Chargement…", "media_gallery.toggle_visible": "{number, plural, one {Cacher l’image} other {Cacher les images}}", "moved_to_account_banner.text": "Votre compte {disabledAccount} est actuellement désactivé parce que vous l'avez déplacé à {movedToAccount}.", "mute_modal.duration": "Durée", @@ -478,6 +479,9 @@ "onboarding.follows.empty": "Malheureusement, aucun résultat ne peut être affiché pour le moment. Vous pouvez essayer d'utiliser la recherche ou parcourir la page de découverte pour trouver des personnes à suivre, ou réessayez plus tard.", "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", "onboarding.follows.title": "Personnaliser votre flux principal", + "onboarding.profile.discoverable": "Autoriser des algorithmes de découverte à mettre en avant votre profil et vos messages", + "onboarding.profile.save_and_continue": "Enregistrer et continuer", + "onboarding.profile.upload_avatar": "Importer une photo de profil", "onboarding.share.lead": "Faites savoir aux gens comment ils peuvent vous trouver sur Mastodon!", "onboarding.share.message": "Je suis {username} sur #Mastodon ! Suivez-moi sur {url}", "onboarding.share.next_steps": "Étapes suivantes possibles :", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 7d2c6dab9..1f199bc45 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -390,6 +390,7 @@ "lists.search": "Procurar entre as persoas que segues", "lists.subheading": "As túas listaxes", "load_pending": "{count, plural, one {# novo elemento} other {# novos elementos}}", + "loading_indicator.label": "Estase a cargar…", "media_gallery.toggle_visible": "Agochar {number, plural, one {imaxe} other {imaxes}}", "moved_to_account_banner.text": "A túa conta {disabledAccount} está actualmente desactivada porque movéchela a {movedToAccount}.", "mute_modal.duration": "Duración", @@ -478,6 +479,17 @@ "onboarding.follows.empty": "Desgraciadamente agora mesmo non hai nada que mostrar. Podes intentalo coa busca ou na páxina descubrir para atopar persoas ás que seguir, ou intentalo máis tarde.", "onboarding.follows.lead": "Podes facer que a túa cronoloxía de inicio sexa como ti a queres. Canta máis xente sigas máis interesante será. Estes perfís poderían axudarche a comezar —sempre poderás deixar de seguilos despois!", "onboarding.follows.title": "Popular en Mastodon", + "onboarding.profile.discoverable": "Perfil destacado e publicacións nos algoritmos de descubrimento", + "onboarding.profile.display_name": "Nome público", + "onboarding.profile.display_name_hint": "O teu nome completo ou un nome divertido…", + "onboarding.profile.indexable": "Incluír publicacións públicas nos resultados das buscas", + "onboarding.profile.lead": "Sempre poderás incluír esta información mais tarde nos axustes, onde terás máis opcións dispoñibles.", + "onboarding.profile.note": "Acerca de ti", + "onboarding.profile.note_hint": "Podes @mencionar a outras persoas ou usar #cancelos…", + "onboarding.profile.save_and_continue": "Gardar e continuar", + "onboarding.profile.title": "Configuración do perfil", + "onboarding.profile.upload_avatar": "Subir imaxe do perfil", + "onboarding.profile.upload_header": "Subir cabeceira para o perfil", "onboarding.share.lead": "Fai que as persoas saiban como atoparte en Mastodon!", "onboarding.share.message": "Son {username} en #Mastodon! Ségueme en {url}", "onboarding.share.next_steps": "Seguintes pasos:", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index 2e0009170..b6716f796 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -21,6 +21,7 @@ "account.blocked": "לחסום", "account.browse_more_on_origin_server": "ראה יותר בפרופיל המקורי", "account.cancel_follow_request": "משיכת בקשת מעקב", + "account.copy": "להעתיק קישור לפרופיל", "account.direct": "הודעה פרטית אל @{name}", "account.disable_notifications": "הפסק לשלוח לי התראות כש@{name} מפרסמים", "account.domain_blocked": "הדומיין חסום", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "סמן כנקרא", "conversation.open": "צפו בשיחה", "conversation.with": "עם {names}", + "copy_icon_button.copied": "הועתק ללוח", "copypaste.copied": "הועתק", "copypaste.copy_to_clipboard": "העתקה ללוח הגזירים", "directory.federated": "מהפדרציה הידועה", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 96fc720a4..83948b178 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -21,6 +21,7 @@ "account.blocked": "Letiltva", "account.browse_more_on_origin_server": "További böngészés az eredeti profilon", "account.cancel_follow_request": "Követési kérés visszavonása", + "account.copy": "Hivatkozás másolása a profilba", "account.direct": "@{name} személyes említése", "account.disable_notifications": "Ne figyelmeztessen, ha @{name} bejegyzést tesz közzé", "account.domain_blocked": "Letiltott domain", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Megjelölés olvasottként", "conversation.open": "Beszélgetés megtekintése", "conversation.with": "Velük: {names}", + "copy_icon_button.copied": "A szöveg a vágólapra másolva", "copypaste.copied": "Másolva", "copypaste.copy_to_clipboard": "Másolás vágólapra", "directory.federated": "Az ismert fediverzumból", @@ -481,13 +483,13 @@ "onboarding.follows.title": "Népszerű a Mastodonon", "onboarding.profile.discoverable": "Profil és bejegyzések szerepeltetése a felfedezési algoritmusokban", "onboarding.profile.display_name": "Megjelenített név", - "onboarding.profile.display_name_hint": "Teljes név vagy becenév…", - "onboarding.profile.indexable": "Nyilvános bejegyzések is a keresési eredményekben", - "onboarding.profile.lead": "Ezt később bármikor elvégezhető a beállításoknál, ahol még több testreszabási lehetőség áll rendelkezésre.", - "onboarding.profile.note": "Biográfia", - "onboarding.profile.note_hint": "@említhetünk másokat vagy #hashtag elemeket…", + "onboarding.profile.display_name_hint": "Teljes neved vagy vicces neved…", + "onboarding.profile.indexable": "Nyilvános bejegyzések szerepeltetése a keresési eredményekben", + "onboarding.profile.lead": "Ezt később bármikor befejezheted a beállításokban, ahol még több testreszabási lehetőség áll rendelkezésre.", + "onboarding.profile.note": "Bemutatkozás", + "onboarding.profile.note_hint": "Megemlíthetsz @másokat vagy #hashtag-eket…", "onboarding.profile.save_and_continue": "Mentés és folytatás", - "onboarding.profile.title": "Profil beüzemelés", + "onboarding.profile.title": "Profilbeállítás", "onboarding.profile.upload_avatar": "Profilkép feltöltése", "onboarding.profile.upload_header": "Profil fejléc feltöltése", "onboarding.share.lead": "Tudassuk az emberekkel, hogyan találhatnak meg a Mastodonon!", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index 54123ae4c..b3c55d4f6 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -21,6 +21,7 @@ "account.blocked": "Útilokaður", "account.browse_more_on_origin_server": "Skoða nánari upplýsingar á notandasniðinu", "account.cancel_follow_request": "Taka fylgjendabeiðni til baka", + "account.copy": "Afrita tengil í notandasnið", "account.direct": "Einkaspjall við @{name}", "account.disable_notifications": "Hætta að láta mig vita þegar @{name} sendir inn", "account.domain_blocked": "Lén útilokað", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Merkja sem lesið", "conversation.open": "Skoða samtal", "conversation.with": "Við {names}", + "copy_icon_button.copied": "Afritað á klippispjald", "copypaste.copied": "Afritað", "copypaste.copy_to_clipboard": "Afrita á klippispjald", "directory.federated": "Frá samtengdum vefþjónum", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 284d7739c..02e5991ab 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -21,6 +21,7 @@ "account.blocked": "Bloccato", "account.browse_more_on_origin_server": "Sfoglia di più sul profilo originale", "account.cancel_follow_request": "Annulla la richiesta di seguire", + "account.copy": "Copia link del profilo", "account.direct": "Menziona privatamente @{name}", "account.disable_notifications": "Smetti di avvisarmi quando @{name} pubblica un post", "account.domain_blocked": "Dominio bloccato", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Segna come letto", "conversation.open": "Visualizza conversazione", "conversation.with": "Con {names}", + "copy_icon_button.copied": "Copiato negli appunti", "copypaste.copied": "Copiato", "copypaste.copy_to_clipboard": "Copia negli Appunti", "directory.federated": "Da un fediverse noto", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 1420be8e0..de72e382c 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -21,6 +21,7 @@ "account.blocked": "차단함", "account.browse_more_on_origin_server": "원본 프로필에서 더 탐색하기", "account.cancel_follow_request": "팔로우 취소", + "account.copy": "프로필 링크 복사", "account.direct": "@{name} 님에게 개인적으로 멘션", "account.disable_notifications": "@{name} 의 게시물 알림 끄기", "account.domain_blocked": "도메인 차단함", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "읽은 상태로 표시", "conversation.open": "대화 보기", "conversation.with": "{names} 님과", + "copy_icon_button.copied": "클립보드에 복사함", "copypaste.copied": "복사됨", "copypaste.copy_to_clipboard": "클립보드에 복사", "directory.federated": "알려진 연합우주로부터", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index 21aa797b4..af9a3eaa0 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -21,6 +21,7 @@ "account.blocked": "Užblokuota", "account.browse_more_on_origin_server": "Naršyti daugiau originaliame profilyje", "account.cancel_follow_request": "Atšaukti sekimą", + "account.copy": "Kopijuoti nuorodą į profilį", "account.direct": "Privačiai paminėti @{name}", "account.disable_notifications": "Nustoti man pranešti, kai @{name} paskelbia", "account.domain_blocked": "Užblokuotas domenas", @@ -91,11 +92,28 @@ "bundle_column_error.routing.body": "Prašyto puslapio nepavyko rasti. Ar esi tikras (-a), kad adreso juostoje nurodytas URL adresas yra teisingas?", "bundle_column_error.routing.title": "404", "bundle_modal_error.close": "Uždaryti", + "bundle_modal_error.retry": "Bandyti dar kartą", + "closed_registrations.other_server_instructions": "Kadangi Mastodon yra decentralizuotas, gali susikurti paskyrą kitame serveryje ir vis tiek bendrauti su šiuo serveriu.", + "closed_registrations_modal.description": "Sukurti paskyrą {domain} šiuo metu neįmanoma, tačiau nepamiršk, kad norint naudotis Mastodon nebūtina turėti paskyrą {domain}.", "closed_registrations_modal.find_another_server": "Rasti kitą serverį", - "column.domain_blocks": "Hidden domains", + "closed_registrations_modal.preamble": "Mastodon yra decentralizuotas, todėl nesvarbu, kur susikursi paskyrą, galėsi sekti ir bendrauti su bet kuriuo šiame serveryje esančiu asmeniu. Jį gali net savarankiškai talpinti!", + "closed_registrations_modal.title": "Užsiregistravimas į Mastodon", + "column.about": "Apie", + "column.blocks": "Užblokuoti naudotojai", + "column.bookmarks": "Žymės", + "column.community": "Vietinė laiko skalė", + "column.direct": "Privatūs paminėjimai", + "column.directory": "Naršyti profilius", + "column.domain_blocks": "Užblokuoti domenai", + "column.favourites": "Mėgstamiausi", + "column.firehose": "Tiesioginiai padavimai", + "column.follow_requests": "Sekti prašymus", + "column.home": "Pradžia", "column.lists": "Sąrašai", - "column.mutes": "Užtildyti vartotojai", - "column.pins": "Pinned toot", + "column.mutes": "Užtildyti naudotojai", + "column.notifications": "Pranešimai", + "column.pins": "Prisegti įrašai", + "column.public": "Federacinė laiko skalė", "column_back_button.label": "Atgal", "column_header.hide_settings": "Slėpti nustatymus", "column_header.pin": "Prisegti", @@ -106,6 +124,7 @@ "compose.language.change": "Keisti kalbą", "compose.language.search": "Ieškoti kalbų...", "compose.published.body": "Įrašas paskelbtas.", + "compose_form.direct_message_warning_learn_more": "Sužinoti daugiau", "compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.", "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.", "compose_form.placeholder": "Kas tavo mintyse?", @@ -137,6 +156,7 @@ "confirmations.reply.confirm": "Atsakyti", "confirmations.reply.message": "Atsakydamas (-a) dabar perrašysi šiuo metu rašomą žinutę. Ar tikrai nori tęsti?", "confirmations.unfollow.confirm": "Nebesekti", + "copy_icon_button.copied": "Nukopijuota į iškarpinę", "dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.", "dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.", "embed.instructions": "Embed this status on your website by copying the code below.", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index 6f941999f..e0fa73ef6 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -21,6 +21,7 @@ "account.blocked": "Geblokkeerd", "account.browse_more_on_origin_server": "Zie meer op het originele profiel", "account.cancel_follow_request": "Ontvolgen", + "account.copy": "Link naar profiel kopiëren", "account.direct": "@{name} een privébericht sturen", "account.disable_notifications": "Geen melding meer geven wanneer @{name} een bericht plaatst", "account.domain_blocked": "Domein geblokkeerd", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Als gelezen markeren", "conversation.open": "Gesprek tonen", "conversation.with": "Met {names}", + "copy_icon_button.copied": "Gekopieerd naar klembord", "copypaste.copied": "Gekopieerd", "copypaste.copy_to_clipboard": "Naar klembord kopiëren", "directory.federated": "Fediverse (wat bekend is)", diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json index a3402d660..5e4459e43 100644 --- a/app/javascript/mastodon/locales/nn.json +++ b/app/javascript/mastodon/locales/nn.json @@ -21,6 +21,7 @@ "account.blocked": "Blokkert", "account.browse_more_on_origin_server": "Sjå gjennom meir på den opphavlege profilen", "account.cancel_follow_request": "Trekk attende fylgeførespurnad", + "account.copy": "Kopier lenka til profilen", "account.direct": "Nevn @{name} privat", "account.disable_notifications": "Slutt å varsle meg når @{name} skriv innlegg", "account.domain_blocked": "Domenet er sperra", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Marker som lesen", "conversation.open": "Sjå samtale", "conversation.with": "Med {names}", + "copy_icon_button.copied": "Kopiert til utklyppstavla", "copypaste.copied": "Kopiert", "copypaste.copy_to_clipboard": "Kopier til utklyppstavla", "directory.federated": "Frå den kjende allheimen", @@ -390,7 +392,7 @@ "lists.search": "Søk blant folk du fylgjer", "lists.subheading": "Listene dine", "load_pending": "{count, plural, one {# nytt element} other {# nye element}}", - "loading_indicator.label": "Laster…", + "loading_indicator.label": "Lastar…", "media_gallery.toggle_visible": "{number, plural, one {Skjul bilete} other {Skjul bilete}}", "moved_to_account_banner.text": "Kontoen din, {disabledAccount} er for tida deaktivert fordi du har flytta til {movedToAccount}.", "mute_modal.duration": "Varigheit", @@ -479,17 +481,17 @@ "onboarding.follows.empty": "Me kan ikkje visa deg nokon resultat no. Du kan prøva å søkja eller bla gjennom utforsk-sida for å finna folk å fylgja, eller du kan prøva att seinare.", "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", "onboarding.follows.title": "Popular on Mastodon", - "onboarding.profile.discoverable": "Fremhevede profiler og innlegg i oppdagelsealgoritmer", - "onboarding.profile.display_name": "Visningsnavn", - "onboarding.profile.display_name_hint": "Ditt fulle navn eller ditt morsomme navn…", - "onboarding.profile.indexable": "Inkluder offentlige innlegg i søkeresultatene", - "onboarding.profile.lead": "Du kan alltid fullføre dette senere i innstillingene, der enda flere tilpasningsalternativer er tilgjengelige.", + "onboarding.profile.discoverable": "Ta med profilen og innlegga i oppdagingsalgoritmar", + "onboarding.profile.display_name": "Synleg namn", + "onboarding.profile.display_name_hint": "Det fulle namnet eller kallenamnet ditt…", + "onboarding.profile.indexable": "Ta med offentlege innlegg i søkjeresultat", + "onboarding.profile.lead": "Du kan alltid fullføra dette seinare i innstillingane, og der er det endå fleire tilpassingsalternativ.", "onboarding.profile.note": "Om meg", - "onboarding.profile.note_hint": "Du kan @nevne andre eller #emneknagger…", - "onboarding.profile.save_and_continue": "Lagre og fortsett", - "onboarding.profile.title": "Konfigurering av profil", - "onboarding.profile.upload_avatar": "Last opp profilbilde", - "onboarding.profile.upload_header": "Last opp profiltoppbilde", + "onboarding.profile.note_hint": "Du kan @nemna folk eller #emneknaggar…", + "onboarding.profile.save_and_continue": "Lagre og hald fram", + "onboarding.profile.title": "Profiloppsett", + "onboarding.profile.upload_avatar": "Last opp profilbilete", + "onboarding.profile.upload_header": "Last opp profiltoppbilete", "onboarding.share.lead": "La folk vita korleis dei kan finna deg på Mastodon!", "onboarding.share.message": "Eg er {username} på #Mastodon! Du kan fylgja meg på {url}", "onboarding.share.next_steps": "Dette kan du gjera no:", diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index fe3979f0f..0dce1e666 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -21,6 +21,7 @@ "account.blocked": "Blokkert", "account.browse_more_on_origin_server": "Bla mer på den opprinnelige profilen", "account.cancel_follow_request": "Avbryt følgeforespørselen", + "account.copy": "Kopier lenke til profil", "account.direct": "Nevn @{name} privat", "account.disable_notifications": "Slutt å varsle meg når @{name} legger ut innlegg", "account.domain_blocked": "Domene blokkert", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Marker som lest", "conversation.open": "Vis samtale", "conversation.with": "Med {names}", + "copy_icon_button.copied": "Kopiert til utklippstavlen", "copypaste.copied": "Kopiert", "copypaste.copy_to_clipboard": "Kopier til utklippstavle", "directory.federated": "Fra det kjente strømiverset", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index a1cc0e26e..d316143e1 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -21,6 +21,7 @@ "account.blocked": "Zablokowany(-a)", "account.browse_more_on_origin_server": "Zobacz więcej na oryginalnym profilu", "account.cancel_follow_request": "Wycofaj żądanie obserwowania", + "account.copy": "Skopiuj odnośnik do profilu", "account.direct": "Prywatna wzmianka @{name}", "account.disable_notifications": "Przestań powiadamiać mnie o wpisach @{name}", "account.domain_blocked": "Ukryto domenę", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Oznacz jako przeczytane", "conversation.open": "Zobacz konwersację", "conversation.with": "Z {names}", + "copy_icon_button.copied": "Skopiowano do schowka", "copypaste.copied": "Skopiowano", "copypaste.copy_to_clipboard": "Skopiuj do schowka", "directory.federated": "Ze znanego fediwersum", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index 69e804878..0ad315152 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -21,6 +21,7 @@ "account.blocked": "Bloqueado(a)", "account.browse_more_on_origin_server": "Encontrar mais no perfil original", "account.cancel_follow_request": "Retirar pedido para seguir", + "account.copy": "Copiar hiperligação para o perfil", "account.direct": "Mencionar @{name} em privado", "account.disable_notifications": "Parar de me notificar das publicações de @{name}", "account.domain_blocked": "Domínio bloqueado", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Marcar como lida", "conversation.open": "Ver conversa", "conversation.with": "Com {names}", + "copy_icon_button.copied": "Copiado para a área de transferência", "copypaste.copied": "Copiado", "copypaste.copy_to_clipboard": "Copiar para a área de transferência", "directory.federated": "Do fediverso conhecido", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index 1417bed5f..c3110aa56 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -21,6 +21,7 @@ "account.blocked": "E bllokuar", "account.browse_more_on_origin_server": "Shfletoni më tepër rreth profilit origjinal", "account.cancel_follow_request": "Tërhiq mbrapsht kërkesë për ndjekje", + "account.copy": "Kopjoje lidhjen te profili", "account.direct": "Përmendje private për @{name}", "account.disable_notifications": "Resht së njoftuari mua, kur poston @{name}", "account.domain_blocked": "Përkatësia u bllokua", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Vëri shenjë si të lexuar", "conversation.open": "Shfaq bisedën", "conversation.with": "Me {names}", + "copy_icon_button.copied": "U kopjua në të papastër", "copypaste.copied": "U kopjua", "copypaste.copy_to_clipboard": "Kopjoje në të papastër", "directory.federated": "Nga fedivers i njohur", diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json index b0a76b32a..c614215aa 100644 --- a/app/javascript/mastodon/locales/sr.json +++ b/app/javascript/mastodon/locales/sr.json @@ -21,6 +21,7 @@ "account.blocked": "Блокиран", "account.browse_more_on_origin_server": "Прегледајте још на оригиналном профилу", "account.cancel_follow_request": "Откажи праћење", + "account.copy": "Копирај везу у профил", "account.direct": "Приватно помени @{name}", "account.disable_notifications": "Заустави обавештавање за објаве корисника @{name}", "account.domain_blocked": "Домен је блокиран", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Означи као прочитано", "conversation.open": "Прикажи разговор", "conversation.with": "Са {names}", + "copy_icon_button.copied": "Копирано", "copypaste.copied": "Копирано", "copypaste.copy_to_clipboard": "Копирај", "directory.federated": "Са знаног федиверзума", @@ -390,6 +392,7 @@ "lists.search": "Претражи међу људима које пратите", "lists.subheading": "Ваше листе", "load_pending": "{count, plural, one {# нова ставка} few {# нове ставке} other {# нових ставки}}", + "loading_indicator.label": "Учитавање…", "media_gallery.toggle_visible": "{number, plural, one {Сакриј слику} few {Сакриј слике} other {Сакриј слике}}", "moved_to_account_banner.text": "Ваш налог {disabledAccount} је тренутно онемогућен јер сте прешли на {movedToAccount}.", "mute_modal.duration": "Трајање", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 6f9f37ea1..0b1a7f6ad 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -21,6 +21,7 @@ "account.blocked": "Blockerad", "account.browse_more_on_origin_server": "Läs mer på den ursprungliga profilen", "account.cancel_follow_request": "Återkalla din begäran om att få följa", + "account.copy": "Kopiera länk till profil", "account.direct": "Nämn @{name} privat", "account.disable_notifications": "Sluta notifiera mig när @{name} gör inlägg", "account.domain_blocked": "Domän blockerad", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Markera som läst", "conversation.open": "Visa konversation", "conversation.with": "Med {names}", + "copy_icon_button.copied": "Kopierad till urklipp", "copypaste.copied": "Kopierad", "copypaste.copy_to_clipboard": "Kopiera till urklipp", "directory.federated": "Från känt fediversum", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index 2166cd2dd..c86b7c379 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -21,6 +21,7 @@ "account.blocked": "ปิดกั้นอยู่", "account.browse_more_on_origin_server": "เรียกดูเพิ่มเติมในโปรไฟล์ดั้งเดิม", "account.cancel_follow_request": "ยกเลิกการติดตาม", + "account.copy": "คัดลอกลิงก์ไปยังโปรไฟล์", "account.direct": "กล่าวถึง @{name} แบบส่วนตัว", "account.disable_notifications": "หยุดแจ้งเตือนฉันเมื่อ @{name} โพสต์", "account.domain_blocked": "ปิดกั้นโดเมนอยู่", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "ทำเครื่องหมายว่าอ่านแล้ว", "conversation.open": "ดูการสนทนา", "conversation.with": "กับ {names}", + "copy_icon_button.copied": "คัดลอกไปยังคลิปบอร์ดแล้ว", "copypaste.copied": "คัดลอกแล้ว", "copypaste.copy_to_clipboard": "คัดลอกไปยังคลิปบอร์ด", "directory.federated": "จากจักรวาลสหพันธ์ที่รู้จัก", @@ -481,15 +483,15 @@ "onboarding.follows.title": "ปรับแต่งฟีดหน้าแรกของคุณ", "onboarding.profile.discoverable": "แสดงโปรไฟล์และโพสต์ในอัลกอริทึมการค้นพบ", "onboarding.profile.display_name": "ชื่อที่แสดง", - "onboarding.profile.display_name_hint": "ชื่อเต็มหรือชื่อแบบสนุกสนานของคุณ", + "onboarding.profile.display_name_hint": "ชื่อเต็มของคุณหรือชื่อแบบสนุกสนานของคุณ…", "onboarding.profile.indexable": "รวมโพสต์สาธารณะในผลลัพธ์การค้นหา", "onboarding.profile.lead": "คุณสามารถกลับมาทำต่อได้เสมอในการตั้งค่า ซึ่งจะมีตัวเลือกในการปรับแต่งมากกว่า", "onboarding.profile.note": "ชีวประวัติ", - "onboarding.profile.note_hint": "คุณสามารถ @กล่าวถึง ผู้คนอื่น ๆ หรือ #แฮชแท็ก", + "onboarding.profile.note_hint": "คุณสามารถ @กล่าวถึง ผู้คนอื่น ๆ หรือ #แฮชแท็ก…", "onboarding.profile.save_and_continue": "บันทึกและดำเนินการต่อ", "onboarding.profile.title": "การตั้งค่าโปรไฟล์", - "onboarding.profile.upload_avatar": "อัปโหลดรูปโปรไฟล์", - "onboarding.profile.upload_header": "อัปโหลดรูปส่วนหัวโปรไฟล์", + "onboarding.profile.upload_avatar": "อัปโหลดรูปภาพโปรไฟล์", + "onboarding.profile.upload_header": "อัปโหลดส่วนหัวโปรไฟล์", "onboarding.share.lead": "แจ้งให้ผู้คนทราบวิธีที่เขาสามารถค้นหาคุณใน Mastodon!", "onboarding.share.message": "ฉันคือ {username} ใน #Mastodon! มาติดตามฉันที่ {url}", "onboarding.share.next_steps": "ขั้นตอนถัดไปที่เป็นไปได้:", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index 8649d9310..1307d7ea5 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -21,6 +21,7 @@ "account.blocked": "Заблоковані", "account.browse_more_on_origin_server": "Переглянути більше в оригінальному профілі", "account.cancel_follow_request": "Відкликати запит на стеження", + "account.copy": "Копіювати посилання на профіль", "account.direct": "Особиста згадка @{name}", "account.disable_notifications": "Не повідомляти мене про дописи @{name}", "account.domain_blocked": "Домен заблоковано", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Позначити як прочитане", "conversation.open": "Переглянути бесіду", "conversation.with": "З {names}", + "copy_icon_button.copied": "Скопійовано до буфера обміну", "copypaste.copied": "Скопійовано", "copypaste.copy_to_clipboard": "Копіювати до буфера обміну", "directory.federated": "З відомого федесвіту", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index cc6d8994d..79f463772 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -21,6 +21,7 @@ "account.blocked": "已屏蔽", "account.browse_more_on_origin_server": "在原始个人资料页面上浏览详情", "account.cancel_follow_request": "撤回关注请求", + "account.copy": "复制资料卡链接", "account.direct": "私下提及 @{name}", "account.disable_notifications": "当 @{name} 发布嘟文时不要通知我", "account.domain_blocked": "域名已屏蔽", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "标记为已读", "conversation.open": "查看对话", "conversation.with": "与 {names}", + "copy_icon_button.copied": "已复制到剪贴板", "copypaste.copied": "已复制", "copypaste.copy_to_clipboard": "复制到剪贴板", "directory.federated": "来自已知的联邦宇宙", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index ea932bc5a..e7af27c5f 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -21,6 +21,7 @@ "account.blocked": "已封鎖", "account.browse_more_on_origin_server": "前往原始的個人檔案頁瀏覽更多", "account.cancel_follow_request": "撤回追蹤請求", + "account.copy": "複製個人檔案連結", "account.direct": "私下提及 @{name}", "account.disable_notifications": "當 @{name} 發文時不要再通知我", "account.domain_blocked": "網域被封鎖", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "標為已讀", "conversation.open": "檢視對話", "conversation.with": "與 {names}", + "copy_icon_button.copied": "已複製到剪貼簿", "copypaste.copied": "已複製", "copypaste.copy_to_clipboard": "複製到剪貼簿", "directory.federated": "來自已知的聯盟網絡", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 4c52693cc..d4f3010c9 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -21,6 +21,7 @@ "account.blocked": "已封鎖", "account.browse_more_on_origin_server": "在該伺服器上的個人檔案頁面瀏覽更多", "account.cancel_follow_request": "收回跟隨請求", + "account.copy": "複製個人檔案連結", "account.direct": "私訊 @{name}", "account.disable_notifications": "取消來自 @{name} 嘟文的通知", "account.domain_blocked": "已封鎖網域", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "標記為已讀", "conversation.open": "檢視對話", "conversation.with": "與 {names}", + "copy_icon_button.copied": "已複製到剪貼簿", "copypaste.copied": "已複製", "copypaste.copy_to_clipboard": "複製到剪貼簿", "directory.federated": "來自已知聯邦宇宙", diff --git a/config/locales/es.yml b/config/locales/es.yml index 34d1c85dc..72a36250f 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -611,6 +611,7 @@ es: created_at: Denunciado delete_and_resolve: Eliminar publicaciones forwarded: Reenviado + forwarded_replies_explanation: Este informe es de un usuario remoto y sobre contenido remoto. Se le ha enviado porque el contenido reportado es en respuesta a uno de sus usuarios. forwarded_to: Reenviado a %{domain} mark_as_resolved: Marcar como resuelto mark_as_sensitive: Marcar como sensible diff --git a/config/locales/eu.yml b/config/locales/eu.yml index dd7575c57..6aa92c2d1 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -613,6 +613,7 @@ eu: created_at: Salatua delete_and_resolve: Ezabatu bidalketak forwarded: Birbidalia + forwarded_replies_explanation: Salaketa hau beste instantzia bateko erabiltzaile baten edukiari buruzkoa da, eta zure instantziako erabiltzaile bati egidako erantzuna delako bidali dizugu mezu hau. forwarded_to: 'Hona birbidalia: %{domain}' mark_as_resolved: Markatu konpondutako gisa mark_as_sensitive: Markatu hunkigarri gisa diff --git a/config/locales/gl.yml b/config/locales/gl.yml index 0075c6592..955ec50e7 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -384,7 +384,7 @@ gl: domain_blocks: add_new: Engadir novo bloqueo de dominio confirm_suspension: - cancel: Cancelar + cancel: Desbotar confirm: Suspender permanent_action: Ao retirar a suspensión non restableces os datos ou a relación. preamble_html: Vas suspender a <strong>%{domain}</strong> e os seus subdominios. @@ -611,6 +611,7 @@ gl: created_at: Denunciado delete_and_resolve: Eliminar publicacións forwarded: Reenviado + forwarded_replies_explanation: Esta denuncia procede dunha usuaria remota e acerca de contido remoto. Enviouseche unha copia porque o contido denunciado é unha resposta a unha das túas usuarias. forwarded_to: Reenviado a %{domain} mark_as_resolved: Marcar como resolto mark_as_sensitive: Marcar como sensible diff --git a/config/locales/hu.yml b/config/locales/hu.yml index a0ff3061f..60016c935 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -611,7 +611,7 @@ hu: created_at: Jelentve delete_and_resolve: Bejegyzések törlése forwarded: Továbbítva - forwarded_replies_explanation: Ez a jelentés egy távoli felhasználótól származik, és távoli tartalomról szól. Azért lett neked továbbítva, mert a jelentett tartalom az egyik felhasználódnak küldött válasz. + forwarded_replies_explanation: Ez a jelentés egy távoli flehasználótól származik, és távoli tartalomról szól. Azért lett neked továbbítva, mert a jelentett tartalom az egyik felhasználódnak küldött válasz. forwarded_to: 'Továbbítva ide: %{domain}' mark_as_resolved: Megjelölés megoldottként mark_as_sensitive: Érzékenynek jelölés diff --git a/config/locales/ko.yml b/config/locales/ko.yml index e11081fcd..7dcb5d632 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -763,7 +763,7 @@ ko: open: 누구나 가입 할 수 있음 security: authorized_fetch: 연합된 서버들에게서 인증 필수 - authorized_fetch_hint: 연합된 서버들에게서 인증을 요구하는 것은 사용자 레벨과 서버 레벨의 차단은 좀 더 확실하게 해줍니다. 한편으로는 성능적인 페널티, 답글의 전달 범위 감소, 몇몇 연합된 서비스들과의 호환성 문제가 있을 가능성이 있습니다. 추가적으로 이 기능은 전용 액터가 공개돤 게시물이나 계정을 페치하는 것은 막지 않습니다. + authorized_fetch_hint: 연합된 서버들에게서 인증을 요구하는 것은 사용자 레벨과 서버 레벨의 차단을 좀 더 확실하게 해줍니다. 한편으로는 성능적인 페널티, 답글의 전달 범위 감소, 몇몇 연합된 서비스들과의 호환성 문제가 있을 가능성이 있습니다. 추가적으로 이 기능은 전용 액터가 공개돤 게시물이나 계정을 페치하는 것은 막지 않습니다. authorized_fetch_overridden_hint: 현재 이 값은 환경변수에 의해 설정되어 있기에 설정을 변경할 수 없습니다. federation_authentication: 연합 인증 필수 title: 서버 설정 diff --git a/config/locales/nn.yml b/config/locales/nn.yml index 09de24a67..ad2acdda8 100644 --- a/config/locales/nn.yml +++ b/config/locales/nn.yml @@ -534,7 +534,7 @@ nn: total_reported: Rapportar om dei total_storage: Medievedlegg totals_time_period_hint_html: Totalsum vist nedanfor gjeld data for alle tidsperiodar. - unknown_instance: Dette domenet er ukjent for denne serveren. + unknown_instance: Domenet er ukjent for denne tenaren. invites: deactivate_all: Slå av alle filter: @@ -611,7 +611,7 @@ nn: created_at: Rapportert delete_and_resolve: Slett innlegg forwarded: Videresendt - forwarded_replies_explanation: Denne rapporten er fra en ekstern bruker og handler om eksternt innhold. Den er videresendt til deg fordi det rapporterte innholdet svarer til en av brukerne dine. + forwarded_replies_explanation: Denne rapporten gjeld innhald på ein annan nettstad. Rapporten er vidaresend til deg fordi det rapporterte innhaldet er eit svar på noko ein av brukarane på nettstaden din har skrive. forwarded_to: Videresendt til %{domain} mark_as_resolved: Merk som løyst mark_as_sensitive: Marker som ømtolig @@ -1042,14 +1042,14 @@ nn: hint_html: Berre ein ting til! Vi må bekrefte at du er et menneske (så vi kan halde spam ute!). Løys CAPTCHA-en nedanfor og klikk "Fortsett". title: Sikkerheitssjekk confirmations: - awaiting_review: Din e-post adresse er bekreftet! %{domain} ansatte gjennomgår nå registreringen din. Du vil motta en e-post hvis de godkjenner din konto! - awaiting_review_title: Din registrering blir vurdert - clicking_this_link: klikke på denne lenken - login_link: logg inn - proceed_to_login_html: Du kan nå fortsette til %{login_link}. - redirect_to_app_html: Du burde bli omdirigert til <strong>%{app_name}</strong> -appen. Hvis det ikke skjedde, kan du prøve %{clicking_this_link} eller manuelt gå tilbake til appen. - registration_complete: Registreringen på %{domain} er nå fullført! - welcome_title: Velkommen, %{name}! + awaiting_review: Epostadressa di er stadfesta! Styrarane på %{domain} ser gjennom registreringa di. Du får ein epost frå dei om dei godkjenner brukarkontoen din. + awaiting_review_title: Me går gjennom registreringa di + clicking_this_link: klikka på denne lenka + login_link: logga inn + proceed_to_login_html: No kan du %{login_link}. + redirect_to_app_html: Du skulle vorte vidaresend til <strong>%{app_name}</strong>-appen. Viss det ikkje skjedde, kan du prøva å %{clicking_this_link} eller manuelt gå tilbake til appen. + registration_complete: Du har registrert deg som brukar på %{domain}. + welcome_title: Velkomen, %{name}! wrong_email_hint: Viss epostadressa er feil, kan du endra ho i kontoinnstillingane. delete_account: Slett konto delete_account_html: Om du vil sletta kontoen din, kan du <a href="%{path}">gå hit</a>. Du vert spurd etter stadfesting. @@ -1111,7 +1111,7 @@ nn: functional: Kontoen din er fullt operativt. pending: Søknaden din ventar på gjennomgang frå personalet vårt. Dette kan taka litt tid. Du får ein e-post om søknaden din vert godkjend. redirecting_to: Kontoen din er inaktiv fordi den for øyeblikket omdirigerer til %{acct}. - self_destruct: Siden %{domain} stenger, vil du kun ha begrenset tilgang til kontoen din. + self_destruct: Av di %{domain} er i ferd med å stenga, vil du berre få avgrensa tilgang til brukarkontoen din. view_strikes: Vis tidligere advarsler mot kontoen din too_fast: Skjemaet ble sendt inn for raskt, prøv på nytt. use_security_key: Bruk sikkerhetsnøkkel @@ -1583,8 +1583,8 @@ nn: over_total_limit: Du har overskredet grensen på %{limit} planlagte tuter too_soon: Den planlagte datoen må være i fremtiden self_destruct: - lead_html: Dessverre stenger <strong>%{domain}</strong> for alltid. Hvis du hadde en konto der vil du ikke kunne fortsette å bruke den, men du kan fremdeles be om en sikkerhetskopi av dataene dine. - title: Denne serveren stenger + lead_html: Diverre stengjer <strong>%{domain}</strong> dørene for godt. Viss du hadde ein brukarkonto der, vil du ikkje kunna halda fram å bruka han, men du kan få ut ein tryggingskopi av dataa dine. + title: Denne tenaren stengjer sessions: activity: Siste aktivitet browser: Nettlesar diff --git a/config/locales/no.yml b/config/locales/no.yml index 3cf2df3a1..42bd6193c 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -1533,7 +1533,7 @@ posting_defaults: Innleggsstandarder public_timelines: Offentlige tidslinjer privacy: - hint_html: "<strong>Tilpass hvordan du vil at din profil og dine innlegg skal bli funnet.</strong> En rekke funksjoner i Mastodon kan hjelpe deg med å nå et bredere publikum når de aktiverte. Ta deg et øyeblikk til å vurdere disse innstillingene for å forsikre deg om at de passer deg og ditt bruk." + hint_html: "<strong>Tilpass hvordan du vil at din profil og dine innlegg skal bli funnet.</strong> En rekke funksjoner i Mastodon kan hjelpe deg med å nå et bredere publikum når det er aktivert. Ta deg et øyeblikk til å vurdere disse innstillingene for å forsikre deg om at de passer deg og ditt bruk." privacy: Personvern privacy_hint_html: Kontrollér hvor mye du ønsker å dele til fordel for andre. Folk oppdager interessante profiler og kule app'er ved å bla gjennom andres følgere og ved å se hvilke app'er de bruker, men du kan velge å holde det skjult. reach: Rekkevidde diff --git a/config/locales/sr.yml b/config/locales/sr.yml index 407908517..b271b15f1 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -623,6 +623,7 @@ sr: created_at: Пријављена delete_and_resolve: Обриши објаве forwarded: Прослеђено + forwarded_replies_explanation: Овај извештај је од удаљеног корисника и о удаљеном садржају. Прослеђен вам је јер је пријављени садржај у одговору једном од ваших корисника. forwarded_to: Прослеђено ка %{domain} mark_as_resolved: Означи као решену mark_as_sensitive: Обележи као осетљиво diff --git a/config/locales/th.yml b/config/locales/th.yml index 79d668d3e..0b7498e7d 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -560,7 +560,7 @@ th: enabled: เปิดใช้งานอยู่ inbox_url: URL ของรีเลย์ pending: กำลังรอการอนุมัติของรีเลย์ - save_and_enable: บันทึกแล้วเปิดใช้งาน + save_and_enable: บันทึกและเปิดใช้งาน setup: ตั้งค่าการเชื่อมต่อรีเลย์ signatures_not_enabled: รีเลย์อาจทำงานไม่ถูกต้องขณะที่มีการเปิดใช้งานโหมดปลอดภัยหรือโหมดการติดต่อกับภายนอกแบบจำกัด status: สถานะ @@ -599,7 +599,7 @@ th: created_at: รายงานเมื่อ delete_and_resolve: ลบโพสต์ forwarded: ส่งต่อแล้ว - forwarded_replies_explanation: รายงานนี้มาจากผู้ใช้ระยะไกล และเป็นรายงานเกี่ยวกับเนื้อหาระยะไกล ซึ่งถูกส่งต่อมาหาคุณเนื่องจากเนื้อหาที่ถูกรายงานอยู่ในการตอบกลับไปยังหนึ่งในผู้ใช้ของคุณ + forwarded_replies_explanation: รายงานนี้มาจากผู้ใช้ระยะไกลและเกี่ยวกับเนื้อหาระยะไกล มีการส่งต่อรายงานไปยังคุณเนื่องจากเนื้อหาที่รายงานอยู่ในการตอบกลับหนึ่งในผู้ใช้ของคุณ forwarded_to: ส่งต่อไปยัง %{domain} แล้ว mark_as_resolved: ทำเครื่องหมายว่าแก้ปัญหาแล้ว mark_as_sensitive: ทำเครื่องหมายว่าละเอียดอ่อน diff --git a/config/locales/tr.yml b/config/locales/tr.yml index 9d4d95a83..5882eae31 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -611,6 +611,7 @@ tr: created_at: Şikayet edildi delete_and_resolve: Gönderileri sil forwarded: İletildi + forwarded_replies_explanation: Bu bildirim başka bir sunucudaki kullanıcı ve içerik ile ilgili. Bildirilen içerik kullanıcılarınızdan birine yanıt şeklinde olduğu için size yönlendirildi. forwarded_to: "%{domain}'e iletildi" mark_as_resolved: Giderildi olarak işaretle mark_as_sensitive: Hassas olarak işaretle diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index b98193065..b788b53e1 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -599,6 +599,7 @@ zh-CN: created_at: 举报时间 delete_and_resolve: 删除嘟文 forwarded: 已转发 + forwarded_replies_explanation: 该举报来自外站用户,涉及外站内容。之所以转发给您,是因为被举报的内容是对您站点一位用户的回复。 forwarded_to: 转发举报至 %{domain} mark_as_resolved: 标记为已处理 mark_as_sensitive: 标记为敏感内容 From 549e8e7baf62eb4ecb4dd039e301e6280889218d Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Fri, 17 Nov 2023 04:50:19 -0500 Subject: [PATCH 58/63] Add `email_spec` and speedup/cleanup to `spec/mailers` (#27902) --- Gemfile | 3 + Gemfile.lock | 5 + spec/mailers/admin_mailer_spec.rb | 97 +++++++------- spec/mailers/notification_mailer_spec.rb | 109 ++++++++-------- spec/mailers/user_mailer_spec.rb | 119 ++++++++++++------ spec/rails_helper.rb | 1 + .../api/v1/admin/account_actions_spec.rb | 12 +- 7 files changed, 194 insertions(+), 152 deletions(-) diff --git a/Gemfile b/Gemfile index add7b3606..74672ad06 100644 --- a/Gemfile +++ b/Gemfile @@ -109,6 +109,9 @@ group :test do # RSpec progress bar formatter gem 'fuubar', '~> 2.5' + # RSpec helpers for email specs + gem 'email_spec' + # Extra RSpec extenion methods and helpers for sidekiq gem 'rspec-sidekiq', '~> 4.0' diff --git a/Gemfile.lock b/Gemfile.lock index 20c958e2e..beec8b39c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -260,6 +260,10 @@ GEM elasticsearch-transport (7.13.3) faraday (~> 1) multi_json + email_spec (2.2.2) + htmlentities (~> 4.3.3) + launchy (~> 2.1) + mail (~> 2.7) encryptor (3.0.0) erubi (1.12.0) et-orbi (1.2.7) @@ -853,6 +857,7 @@ DEPENDENCIES doorkeeper (~> 5.6) dotenv-rails (~> 2.8) ed25519 (~> 1.3) + email_spec fabrication (~> 2.30) faker (~> 3.2) fast_blank (~> 1.0) diff --git a/spec/mailers/admin_mailer_spec.rb b/spec/mailers/admin_mailer_spec.rb index 423dce88a..9f0d89996 100644 --- a/spec/mailers/admin_mailer_spec.rb +++ b/spec/mailers/admin_mailer_spec.rb @@ -13,14 +13,13 @@ RSpec.describe AdminMailer do recipient.user.update(locale: :en) end - it 'renders the headers' do - expect(mail.subject).to eq("New report for cb6e6126.ngrok.io (##{report.id})") - 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 eq("Mike,\r\n\r\nJohn has reported Mike\r\n\r\nView: https://cb6e6126.ngrok.io/admin/reports/#{report.id}\r\n") + it 'renders the email' do + expect(mail) + .to be_present + .and(deliver_to(recipient.user_email)) + .and(deliver_from('notifications@localhost')) + .and(have_subject("New report for cb6e6126.ngrok.io (##{report.id})")) + .and(have_body_text("Mike,\r\n\r\nJohn has reported Mike\r\n\r\nView: https://cb6e6126.ngrok.io/admin/reports/#{report.id}\r\n")) end end @@ -33,14 +32,13 @@ RSpec.describe AdminMailer do recipient.user.update(locale: :en) end - it 'renders the headers' do - expect(mail.subject).to eq("#{appeal.account.username} is appealing a moderation decision on cb6e6126.ngrok.io") - 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 "#{appeal.account.username} is appealing a moderation decision by #{appeal.strike.account.username}" + it 'renders the email' do + expect(mail) + .to be_present + .and(deliver_to(recipient.user_email)) + .and(deliver_from('notifications@localhost')) + .and(have_subject("#{appeal.account.username} is appealing a moderation decision on cb6e6126.ngrok.io")) + .and(have_body_text("#{appeal.account.username} is appealing a moderation decision by #{appeal.strike.account.username}")) end end @@ -53,14 +51,13 @@ RSpec.describe AdminMailer do recipient.user.update(locale: :en) end - it 'renders the headers' do - expect(mail.subject).to eq("New account up for review on cb6e6126.ngrok.io (#{user.account.username})") - 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 details of the new account are below. You can approve or reject this application.' + it 'renders the email' do + expect(mail) + .to be_present + .and(deliver_to(recipient.user_email)) + .and(deliver_from('notifications@localhost')) + .and(have_subject("New account up for review on cb6e6126.ngrok.io (#{user.account.username})")) + .and(have_body_text('The details of the new account are below. You can approve or reject this application.')) end end @@ -75,14 +72,13 @@ RSpec.describe AdminMailer do recipient.user.update(locale: :en) end - it 'renders the headers' do - expect(mail.subject).to eq('New trends up for review on cb6e6126.ngrok.io') - 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 following items need a review before they can be displayed publicly' + it 'renders the email' do + expect(mail) + .to be_present + .and(deliver_to(recipient.user_email)) + .and(deliver_from('notifications@localhost')) + .and(have_subject('New trends up for review on cb6e6126.ngrok.io')) + .and(have_body_text('The following items need a review before they can be displayed publicly')) end end @@ -94,14 +90,13 @@ RSpec.describe AdminMailer do recipient.user.update(locale: :en) end - it 'renders the headers' do - expect(mail.subject).to eq('New Mastodon versions are available for cb6e6126.ngrok.io!') - 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 'New Mastodon versions have been released, you may want to update!' + it 'renders the email' do + expect(mail) + .to be_present + .and(deliver_to(recipient.user_email)) + .and(deliver_from('notifications@localhost')) + .and(have_subject('New Mastodon versions are available for cb6e6126.ngrok.io!')) + .and(have_body_text('New Mastodon versions have been released, you may want to update!')) end end @@ -113,18 +108,16 @@ RSpec.describe AdminMailer do recipient.user.update(locale: :en) end - it 'renders the headers', :aggregate_failures do - expect(mail.subject).to eq('Critical Mastodon updates are available for cb6e6126.ngrok.io!') - expect(mail.to).to eq [recipient.user_email] - expect(mail.from).to eq ['notifications@localhost'] - - expect(mail['Importance'].value).to eq 'high' - expect(mail['Priority'].value).to eq 'urgent' - expect(mail['X-Priority'].value).to eq '1' - end - - it 'renders the body' do - expect(mail.body.encoded).to match 'New critical versions of Mastodon have been released, you may want to update as soon as possible!' + it 'renders the email' do + expect(mail) + .to be_present + .and(deliver_to(recipient.user_email)) + .and(deliver_from('notifications@localhost')) + .and(have_subject('Critical Mastodon updates are available for cb6e6126.ngrok.io!')) + .and(have_body_text('New critical versions of Mastodon have been released, you may want to update as soon as possible!')) + .and(have_header('Importance', 'high')) + .and(have_header('Priority', 'urgent')) + .and(have_header('X-Priority', '1')) end end end diff --git a/spec/mailers/notification_mailer_spec.rb b/spec/mailers/notification_mailer_spec.rb index 78a497c06..eab196166 100644 --- a/spec/mailers/notification_mailer_spec.rb +++ b/spec/mailers/notification_mailer_spec.rb @@ -8,24 +8,27 @@ RSpec.describe NotificationMailer do let(:foreign_status) { Fabricate(:status, account: sender, text: 'The body of the foreign status') } let(:own_status) { Fabricate(:status, account: receiver.account, text: 'The body of the own status') } - shared_examples 'headers' do |type, thread| - it 'renders the to and from headers' do - expect(mail[:to].value).to eq "#{receiver.account.username} <#{receiver.email}>" - expect(mail.from).to eq ['notifications@localhost'] + shared_examples 'standard headers' do |type| + it 'renders the email' do + expect(mail) + .to be_present + .and(have_header('To', "#{receiver.account.username} <#{receiver.email}>")) + .and(have_header('List-ID', "<#{type}.alice.cb6e6126.ngrok.io>")) + .and(have_header('List-Unsubscribe', %r{<https://cb6e6126.ngrok.io/unsubscribe\?token=.+>})) + .and(have_header('List-Unsubscribe', /&type=#{type}/)) + .and(have_header('List-Unsubscribe-Post', 'List-Unsubscribe=One-Click')) + .and(deliver_to("#{receiver.account.username} <#{receiver.email}>")) + .and(deliver_from('notifications@localhost')) end + end - it 'renders the list headers' do - expect(mail['List-ID'].value).to eq "<#{type}.alice.cb6e6126.ngrok.io>" - expect(mail['List-Unsubscribe'].value).to match(%r{<https://cb6e6126.ngrok.io/unsubscribe\?token=.+>}) - expect(mail['List-Unsubscribe'].value).to match("&type=#{type}") - expect(mail['List-Unsubscribe-Post'].value).to eq 'List-Unsubscribe=One-Click' - end - - if thread - it 'renders the thread headers' do - expect(mail['In-Reply-To'].value).to match(/<conversation-\d+.\d\d\d\d-\d\d-\d\d@cb6e6126.ngrok.io>/) - expect(mail['References'].value).to match(/<conversation-\d+.\d\d\d\d-\d\d-\d\d@cb6e6126.ngrok.io>/) - end + shared_examples 'thread headers' do + it 'renders the email with conversation thread headers' do + conversation_header_regex = /<conversation-\d+.\d\d\d\d-\d\d-\d\d@cb6e6126.ngrok.io>/ + expect(mail) + .to be_present + .and(have_header('In-Reply-To', conversation_header_regex)) + .and(have_header('References', conversation_header_regex)) end end @@ -35,15 +38,15 @@ RSpec.describe NotificationMailer do let(:mail) { prepared_mailer_for(receiver.account).mention } include_examples 'localized subject', 'notification_mailer.mention.subject', name: 'bob' - include_examples 'headers', 'mention', true + include_examples 'standard headers', 'mention' + include_examples 'thread headers' - it 'renders the subject' do - expect(mail.subject).to eq('You were mentioned by bob') - end - - it 'renders the body' do - expect(mail.body.encoded).to match('You were mentioned by bob') - expect(mail.body.encoded).to include 'The body of the foreign status' + it 'renders the email' do + expect(mail) + .to be_present + .and(have_subject('You were mentioned by bob')) + .and(have_body_text('You were mentioned by bob')) + .and(have_body_text('The body of the foreign status')) end end @@ -53,14 +56,13 @@ RSpec.describe NotificationMailer do let(:mail) { prepared_mailer_for(receiver.account).follow } include_examples 'localized subject', 'notification_mailer.follow.subject', name: 'bob' - include_examples 'headers', 'follow', false + include_examples 'standard headers', 'follow' - it 'renders the subject' do - expect(mail.subject).to eq('bob is now following you') - end - - it 'renders the body' do - expect(mail.body.encoded).to match('bob is now following you') + it 'renders the email' do + expect(mail) + .to be_present + .and(have_subject('bob is now following you')) + .and(have_body_text('bob is now following you')) end end @@ -70,15 +72,15 @@ RSpec.describe NotificationMailer do let(:mail) { prepared_mailer_for(own_status.account).favourite } include_examples 'localized subject', 'notification_mailer.favourite.subject', name: 'bob' - include_examples 'headers', 'favourite', true + include_examples 'standard headers', 'favourite' + include_examples 'thread headers' - it 'renders the subject' do - expect(mail.subject).to eq('bob favorited your post') - end - - it 'renders the body' do - expect(mail.body.encoded).to match('Your post was favorited by bob') - expect(mail.body.encoded).to include 'The body of the own status' + it 'renders the email' do + expect(mail) + .to be_present + .and(have_subject('bob favorited your post')) + .and(have_body_text('Your post was favorited by bob')) + .and(have_body_text('The body of the own status')) end end @@ -88,15 +90,15 @@ RSpec.describe NotificationMailer do let(:mail) { prepared_mailer_for(own_status.account).reblog } include_examples 'localized subject', 'notification_mailer.reblog.subject', name: 'bob' - include_examples 'headers', 'reblog', true + include_examples 'standard headers', 'reblog' + include_examples 'thread headers' - it 'renders the subject' do - expect(mail.subject).to eq('bob boosted your post') - end - - it 'renders the body' do - expect(mail.body.encoded).to match('Your post was boosted by bob') - expect(mail.body.encoded).to include 'The body of the own status' + it 'renders the email' do + expect(mail) + .to be_present + .and(have_subject('bob boosted your post')) + .and(have_body_text('Your post was boosted by bob')) + .and(have_body_text('The body of the own status')) end end @@ -106,14 +108,13 @@ RSpec.describe NotificationMailer do let(:mail) { prepared_mailer_for(receiver.account).follow_request } include_examples 'localized subject', 'notification_mailer.follow_request.subject', name: 'bob' - include_examples 'headers', 'follow_request', false + include_examples 'standard headers', 'follow_request' - it 'renders the subject' do - expect(mail.subject).to eq('Pending follower: bob') - end - - it 'renders the body' do - expect(mail.body.encoded).to match('bob has requested to follow you') + it 'renders the email' do + expect(mail) + .to be_present + .and(have_subject('Pending follower: bob')) + .and(have_body_text('bob has requested to follow you')) end end diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb index c661f5bbd..4a4392824 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -10,9 +10,12 @@ describe UserMailer do it 'renders confirmation instructions' do receiver.update!(locale: nil) - expect(mail.body.encoded).to include I18n.t('devise.mailer.confirmation_instructions.title') - expect(mail.body.encoded).to include 'spec' - expect(mail.body.encoded).to include Rails.configuration.x.local_domain + + expect(mail) + .to be_present + .and(have_body_text(I18n.t('devise.mailer.confirmation_instructions.title'))) + .and(have_body_text('spec')) + .and(have_body_text(Rails.configuration.x.local_domain)) end include_examples 'localized subject', @@ -25,13 +28,17 @@ describe UserMailer do it 'renders reconfirmation instructions' do receiver.update!(email: 'new-email@example.com', locale: nil) - expect(mail.body.encoded).to include I18n.t('devise.mailer.reconfirmation_instructions.title') - expect(mail.body.encoded).to include 'spec' - expect(mail.body.encoded).to include Rails.configuration.x.local_domain - expect(mail.subject).to eq I18n.t('devise.mailer.reconfirmation_instructions.subject', - instance: Rails.configuration.x.local_domain, - locale: I18n.default_locale) + + expect(mail) + .to be_present + .and(have_body_text(I18n.t('devise.mailer.reconfirmation_instructions.title'))) + .and(have_body_text('spec')) + .and(have_body_text(Rails.configuration.x.local_domain)) end + + include_examples 'localized subject', + 'devise.mailer.confirmation_instructions.subject', + instance: Rails.configuration.x.local_domain end describe '#reset_password_instructions' do @@ -39,8 +46,11 @@ describe UserMailer do it 'renders reset password instructions' do receiver.update!(locale: nil) - expect(mail.body.encoded).to include I18n.t('devise.mailer.reset_password_instructions.title') - expect(mail.body.encoded).to include 'spec' + + expect(mail) + .to be_present + .and(have_body_text(I18n.t('devise.mailer.reset_password_instructions.title'))) + .and(have_body_text('spec')) end include_examples 'localized subject', @@ -52,7 +62,10 @@ describe UserMailer do it 'renders password change notification' do receiver.update!(locale: nil) - expect(mail.body.encoded).to include I18n.t('devise.mailer.password_change.title') + + expect(mail) + .to be_present + .and(have_body_text(I18n.t('devise.mailer.password_change.title'))) end include_examples 'localized subject', @@ -64,7 +77,10 @@ describe UserMailer do it 'renders email change notification' do receiver.update!(locale: nil) - expect(mail.body.encoded).to include I18n.t('devise.mailer.email_changed.title') + + expect(mail) + .to be_present + .and(have_body_text(I18n.t('devise.mailer.email_changed.title'))) end include_examples 'localized subject', @@ -77,8 +93,11 @@ describe UserMailer do it 'renders warning notification' do receiver.update!(locale: nil) - expect(mail.body.encoded).to include I18n.t('user_mailer.warning.title.suspend', acct: receiver.account.acct) - expect(mail.body.encoded).to include strike.text + + expect(mail) + .to be_present + .and(have_body_text(I18n.t('user_mailer.warning.title.suspend', acct: receiver.account.acct))) + .and(have_body_text(strike.text)) end end @@ -88,7 +107,10 @@ describe UserMailer do it 'renders webauthn credential deleted notification' do receiver.update!(locale: nil) - expect(mail.body.encoded).to include I18n.t('devise.mailer.webauthn_credential.deleted.title') + + expect(mail) + .to be_present + .and(have_body_text(I18n.t('devise.mailer.webauthn_credential.deleted.title'))) end include_examples 'localized subject', @@ -103,7 +125,10 @@ describe UserMailer do it 'renders suspicious sign in notification' do receiver.update!(locale: nil) - expect(mail.body.encoded).to include I18n.t('user_mailer.suspicious_sign_in.explanation') + + expect(mail) + .to be_present + .and(have_body_text(I18n.t('user_mailer.suspicious_sign_in.explanation'))) end include_examples 'localized subject', @@ -115,8 +140,10 @@ describe UserMailer do let(:mail) { described_class.appeal_approved(receiver, appeal) } it 'renders appeal_approved notification' do - expect(mail.subject).to eq I18n.t('user_mailer.appeal_approved.subject', date: I18n.l(appeal.created_at)) - expect(mail.body.encoded).to include I18n.t('user_mailer.appeal_approved.title') + expect(mail) + .to be_present + .and(have_subject(I18n.t('user_mailer.appeal_approved.subject', date: I18n.l(appeal.created_at)))) + .and(have_body_text(I18n.t('user_mailer.appeal_approved.title'))) end end @@ -125,8 +152,10 @@ describe UserMailer do let(:mail) { described_class.appeal_rejected(receiver, appeal) } it 'renders appeal_rejected notification' do - expect(mail.subject).to eq I18n.t('user_mailer.appeal_rejected.subject', date: I18n.l(appeal.created_at)) - expect(mail.body.encoded).to include I18n.t('user_mailer.appeal_rejected.title') + expect(mail) + .to be_present + .and(have_subject(I18n.t('user_mailer.appeal_rejected.subject', date: I18n.l(appeal.created_at)))) + .and(have_body_text(I18n.t('user_mailer.appeal_rejected.title'))) end end @@ -134,8 +163,10 @@ describe UserMailer do let(:mail) { described_class.two_factor_enabled(receiver) } it 'renders two_factor_enabled mail' do - expect(mail.subject).to eq I18n.t('devise.mailer.two_factor_enabled.subject') - expect(mail.body.encoded).to include I18n.t('devise.mailer.two_factor_enabled.explanation') + expect(mail) + .to be_present + .and(have_subject(I18n.t('devise.mailer.two_factor_enabled.subject'))) + .and(have_body_text(I18n.t('devise.mailer.two_factor_enabled.explanation'))) end end @@ -143,8 +174,10 @@ describe UserMailer do let(:mail) { described_class.two_factor_disabled(receiver) } it 'renders two_factor_disabled mail' do - expect(mail.subject).to eq I18n.t('devise.mailer.two_factor_disabled.subject') - expect(mail.body.encoded).to include I18n.t('devise.mailer.two_factor_disabled.explanation') + expect(mail) + .to be_present + .and(have_subject(I18n.t('devise.mailer.two_factor_disabled.subject'))) + .and(have_body_text(I18n.t('devise.mailer.two_factor_disabled.explanation'))) end end @@ -152,8 +185,10 @@ describe UserMailer do let(:mail) { described_class.webauthn_enabled(receiver) } it 'renders webauthn_enabled mail' do - expect(mail.subject).to eq I18n.t('devise.mailer.webauthn_enabled.subject') - expect(mail.body.encoded).to include I18n.t('devise.mailer.webauthn_enabled.explanation') + expect(mail) + .to be_present + .and(have_subject(I18n.t('devise.mailer.webauthn_enabled.subject'))) + .and(have_body_text(I18n.t('devise.mailer.webauthn_enabled.explanation'))) end end @@ -161,8 +196,10 @@ describe UserMailer do let(:mail) { described_class.webauthn_disabled(receiver) } it 'renders webauthn_disabled mail' do - expect(mail.subject).to eq I18n.t('devise.mailer.webauthn_disabled.subject') - expect(mail.body.encoded).to include I18n.t('devise.mailer.webauthn_disabled.explanation') + expect(mail) + .to be_present + .and(have_subject(I18n.t('devise.mailer.webauthn_disabled.subject'))) + .and(have_body_text(I18n.t('devise.mailer.webauthn_disabled.explanation'))) end end @@ -170,8 +207,10 @@ describe UserMailer do let(:mail) { described_class.two_factor_recovery_codes_changed(receiver) } it 'renders two_factor_recovery_codes_changed mail' do - expect(mail.subject).to eq I18n.t('devise.mailer.two_factor_recovery_codes_changed.subject') - expect(mail.body.encoded).to include I18n.t('devise.mailer.two_factor_recovery_codes_changed.explanation') + expect(mail) + .to be_present + .and(have_subject(I18n.t('devise.mailer.two_factor_recovery_codes_changed.subject'))) + .and(have_body_text(I18n.t('devise.mailer.two_factor_recovery_codes_changed.explanation'))) end end @@ -180,8 +219,10 @@ describe UserMailer do let(:mail) { described_class.webauthn_credential_added(receiver, credential) } it 'renders webauthn_credential_added mail' do - expect(mail.subject).to eq I18n.t('devise.mailer.webauthn_credential.added.subject') - expect(mail.body.encoded).to include I18n.t('devise.mailer.webauthn_credential.added.explanation') + expect(mail) + .to be_present + .and(have_subject(I18n.t('devise.mailer.webauthn_credential.added.subject'))) + .and(have_body_text(I18n.t('devise.mailer.webauthn_credential.added.explanation'))) end end @@ -189,8 +230,10 @@ describe UserMailer do let(:mail) { described_class.welcome(receiver) } it 'renders welcome mail' do - expect(mail.subject).to eq I18n.t('user_mailer.welcome.subject') - expect(mail.body.encoded).to include I18n.t('user_mailer.welcome.explanation') + expect(mail) + .to be_present + .and(have_subject(I18n.t('user_mailer.welcome.subject'))) + .and(have_body_text(I18n.t('user_mailer.welcome.explanation'))) end end @@ -199,8 +242,10 @@ describe UserMailer do let(:mail) { described_class.backup_ready(receiver, backup) } it 'renders backup_ready mail' do - expect(mail.subject).to eq I18n.t('user_mailer.backup_ready.subject') - expect(mail.body.encoded).to include I18n.t('user_mailer.backup_ready.explanation') + expect(mail) + .to be_present + .and(have_subject(I18n.t('user_mailer.backup_ready.subject'))) + .and(have_body_text(I18n.t('user_mailer.backup_ready.explanation'))) end end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 79f98f2e2..68023b70d 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -21,6 +21,7 @@ require 'webmock/rspec' require 'paperclip/matchers' require 'capybara/rspec' require 'chewy/rspec' +require 'email_spec/rspec' Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } diff --git a/spec/requests/api/v1/admin/account_actions_spec.rb b/spec/requests/api/v1/admin/account_actions_spec.rb index bdf1f08e4..c14e08c21 100644 --- a/spec/requests/api/v1/admin/account_actions_spec.rb +++ b/spec/requests/api/v1/admin/account_actions_spec.rb @@ -8,18 +8,12 @@ RSpec.describe 'Account actions' do let(:scopes) { 'admin:write admin:write:accounts' } let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } - let(:mailer) { instance_double(ActionMailer::MessageDelivery, deliver_later!: nil) } - - before do - allow(UserMailer).to receive(:warning).with(target_account.user, anything).and_return(mailer) - end shared_examples 'a successful notification delivery' do it 'notifies the user about the action taken' do - subject - - expect(UserMailer).to have_received(:warning).with(target_account.user, anything).once - expect(mailer).to have_received(:deliver_later!).once + expect { subject } + .to have_enqueued_job(ActionMailer::MailDeliveryJob) + .with('UserMailer', 'warning', 'deliver_now!', args: [User, AccountWarning]) end end From 0e9801443f8f91ff49b47f82151ee5984c9bd6c1 Mon Sep 17 00:00:00 2001 From: Eugen Rochko <eugen@zeonfederated.com> Date: Fri, 17 Nov 2023 11:37:04 +0100 Subject: [PATCH 59/63] Change to single opt-in during profile setup in onboarding in web UI (#27876) --- .../mastodon/features/onboarding/profile.jsx | 34 ++++++------- app/javascript/mastodon/locales/en.json | 5 +- app/javascript/styles/mastodon/forms.scss | 49 +++++++++++++++++-- 3 files changed, 64 insertions(+), 24 deletions(-) diff --git a/app/javascript/mastodon/features/onboarding/profile.jsx b/app/javascript/mastodon/features/onboarding/profile.jsx index 19ba0bcb9..09e6b2c6c 100644 --- a/app/javascript/mastodon/features/onboarding/profile.jsx +++ b/app/javascript/mastodon/features/onboarding/profile.jsx @@ -5,8 +5,8 @@ import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; import classNames from 'classnames'; import { useHistory } from 'react-router-dom'; -import { useDispatch } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { ReactComponent as AddPhotoAlternateIcon } from '@material-symbols/svg-600/outlined/add_photo_alternate.svg'; import { ReactComponent as EditIcon } from '@material-symbols/svg-600/outlined/edit.svg'; @@ -33,7 +33,6 @@ export const Profile = () => { const [avatar, setAvatar] = useState(null); const [header, setHeader] = useState(null); const [discoverable, setDiscoverable] = useState(account.get('discoverable')); - const [indexable, setIndexable] = useState(account.get('indexable')); const [isSaving, setIsSaving] = useState(false); const [errors, setErrors] = useState(); const avatarFileRef = createRef(); @@ -54,10 +53,6 @@ export const Profile = () => { setDiscoverable(e.target.checked); }, [setDiscoverable]); - const handleIndexableChange = useCallback(e => { - setIndexable(e.target.checked); - }, [setIndexable]); - const handleAvatarChange = useCallback(e => { setAvatar(e.target?.files?.[0]); }, [setAvatar]); @@ -78,12 +73,12 @@ export const Profile = () => { avatar, header, discoverable, - indexable, + indexable: discoverable, })).then(() => history.push('/start/follows')).catch(err => { setIsSaving(false); setErrors(err.response.data.details); }); - }, [dispatch, displayName, note, avatar, header, discoverable, indexable, history]); + }, [dispatch, displayName, note, avatar, header, discoverable, history]); return ( <> @@ -141,18 +136,21 @@ export const Profile = () => { <textarea id='note' value={note} onChange={handleNoteChange} maxLength={500} /> </div> </div> + + <label className='app-form__toggle'> + <div className='app-form__toggle__label'> + <strong><FormattedMessage id='onboarding.profile.discoverable' defaultMessage='Make my profile discoverable' /></strong> <span className='recommended'><FormattedMessage id='recommended' defaultMessage='Recommended' /></span> + <span className='hint'><FormattedMessage id='onboarding.profile.discoverable_hint' defaultMessage='When you opt in to discoverability on Mastodon, your posts may appear in search results and trending, and your profile may be suggested to people with similar interests to you.' /></span> + </div> + + <div className='app-form__toggle__toggle'> + <div> + <Toggle checked={discoverable} onChange={handleDiscoverableChange} /> + </div> + </div> + </label> </div> - <label className='report-dialog-modal__toggle'> - <Toggle checked={discoverable} onChange={handleDiscoverableChange} /> - <FormattedMessage id='onboarding.profile.discoverable' defaultMessage='Feature profile and posts in discovery algorithms' /> - </label> - - <label className='report-dialog-modal__toggle'> - <Toggle checked={indexable} onChange={handleIndexableChange} /> - <FormattedMessage id='onboarding.profile.indexable' defaultMessage='Include public posts in search results' /> - </label> - <div className='onboarding__footer'> <Button block onClick={handleSubmit} disabled={isSaving}>{isSaving ? <LoadingIndicator /> : <FormattedMessage id='onboarding.profile.save_and_continue' defaultMessage='Save and continue' />}</Button> </div> diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 16941e2ca..ed8bfe166 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -481,10 +481,10 @@ "onboarding.follows.empty": "Unfortunately, no results can be shown right now. You can try using search or browsing the explore page to find people to follow, or try again later.", "onboarding.follows.lead": "Your home feed is the primary way to experience Mastodon. The more people you follow, the more active and interesting it will be. To get you started, here are some suggestions:", "onboarding.follows.title": "Personalize your home feed", - "onboarding.profile.discoverable": "Feature profile and posts in discovery algorithms", + "onboarding.profile.discoverable": "Make my profile discoverable", + "onboarding.profile.discoverable_hint": "When you opt in to discoverability on Mastodon, your posts may appear in search results and trending, and your profile may be suggested to people with similar interests to you.", "onboarding.profile.display_name": "Display name", "onboarding.profile.display_name_hint": "Your full name or your fun name…", - "onboarding.profile.indexable": "Include public posts in search results", "onboarding.profile.lead": "You can always complete this later in the settings, where even more customization options are available.", "onboarding.profile.note": "Bio", "onboarding.profile.note_hint": "You can @mention other people or #hashtags…", @@ -535,6 +535,7 @@ "privacy.unlisted.short": "Unlisted", "privacy_policy.last_updated": "Last updated {date}", "privacy_policy.title": "Privacy Policy", + "recommended": "Recommended", "refresh": "Refresh", "regeneration_indicator.label": "Loading…", "regeneration_indicator.sublabel": "Your home feed is being prepared!", diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index e72a01936..555d43cc1 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -1222,10 +1222,6 @@ code { } .app-form { - & > * { - margin-bottom: 16px; - } - &__avatar-input, &__header-input { display: block; @@ -1290,4 +1286,49 @@ code { &__header-input { aspect-ratio: 580/193; } + + &__toggle { + display: flex; + align-items: center; + gap: 16px; + color: $darker-text-color; + font-size: 14px; + line-height: 20px; + + .icon { + flex: 0 0 auto; + } + + .icon { + width: 24px; + height: 24px; + } + + &__label { + flex: 1 1 auto; + + strong { + color: $primary-text-color; + font-weight: 600; + } + + .recommended { + position: absolute; + margin: 0 4px; + margin-top: -2px; + } + } + + &__toggle { + flex: 0 0 auto; + display: flex; + align-items: center; + } + + &__toggle > div { + display: flex; + border-inline-start: 1px solid lighten($ui-base-color, 8%); + padding-inline-start: 16px; + } + } } From 92577376750f560064593bff10206da0080340b2 Mon Sep 17 00:00:00 2001 From: Claire <claire.github-309c@sitedethib.com> Date: Fri, 17 Nov 2023 12:34:49 +0100 Subject: [PATCH 60/63] Rewrite `/api/v1/accounts` tests as request specs (#27888) --- spec/requests/api/v1/accounts_show_spec.rb | 53 ----- .../api/v1/accounts_spec.rb} | 189 +++++++++++++----- 2 files changed, 136 insertions(+), 106 deletions(-) delete mode 100644 spec/requests/api/v1/accounts_show_spec.rb rename spec/{controllers/api/v1/accounts_controller_spec.rb => requests/api/v1/accounts_spec.rb} (59%) diff --git a/spec/requests/api/v1/accounts_show_spec.rb b/spec/requests/api/v1/accounts_show_spec.rb deleted file mode 100644 index ee6e925aa..000000000 --- a/spec/requests/api/v1/accounts_show_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe 'GET /api/v1/accounts/{account_id}' do - it 'returns account entity as 200 OK' do - account = Fabricate(:account) - - get "/api/v1/accounts/#{account.id}" - - aggregate_failures do - expect(response).to have_http_status(200) - expect(body_as_json[:id]).to eq(account.id.to_s) - end - end - - it 'returns 404 if account not found' do - get '/api/v1/accounts/1' - - aggregate_failures do - expect(response).to have_http_status(404) - expect(body_as_json[:error]).to eq('Record not found') - end - end - - context 'when with token' do - it 'returns account entity as 200 OK if token is valid' do - account = Fabricate(:account) - user = Fabricate(:user, account: account) - token = Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts').token - - get "/api/v1/accounts/#{account.id}", headers: { Authorization: "Bearer #{token}" } - - aggregate_failures do - expect(response).to have_http_status(200) - expect(body_as_json[:id]).to eq(account.id.to_s) - end - end - - it 'returns 403 if scope of token is invalid' do - account = Fabricate(:account) - user = Fabricate(:user, account: account) - token = Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write:statuses').token - - get "/api/v1/accounts/#{account.id}", headers: { Authorization: "Bearer #{token}" } - - aggregate_failures do - expect(response).to have_http_status(403) - expect(body_as_json[:error]).to eq('This action is outside the authorized scopes') - end - end - end -end diff --git a/spec/controllers/api/v1/accounts_controller_spec.rb b/spec/requests/api/v1/accounts_spec.rb similarity index 59% rename from spec/controllers/api/v1/accounts_controller_spec.rb rename to spec/requests/api/v1/accounts_spec.rb index 9d0bb73c7..e543c4136 100644 --- a/spec/controllers/api/v1/accounts_controller_spec.rb +++ b/spec/requests/api/v1/accounts_spec.rb @@ -2,59 +2,100 @@ require 'rails_helper' -RSpec.describe Api::V1::AccountsController do - render_views +describe '/api/v1/accounts' do + let(:user) { Fabricate(:user) } + let(:scopes) { '' } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } - let(:user) { Fabricate(:user) } - let(:scopes) { '' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + describe 'GET /api/v1/accounts/:id' do + context 'when logged out' do + let(:account) { Fabricate(:account) } - before do - allow(controller).to receive(:doorkeeper_token) { token } + it 'returns account entity as 200 OK', :aggregate_failures do + get "/api/v1/accounts/#{account.id}" + + expect(response).to have_http_status(200) + expect(body_as_json[:id]).to eq(account.id.to_s) + end + end + + context 'when the account does not exist' do + it 'returns http not found' do + get '/api/v1/accounts/1' + + expect(response).to have_http_status(404) + expect(body_as_json[:error]).to eq('Record not found') + end + end + + context 'when logged in' do + subject do + get "/api/v1/accounts/#{account.id}", headers: headers + end + + let(:account) { Fabricate(:account) } + let(:scopes) { 'read:accounts' } + + it 'returns account entity as 200 OK', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(body_as_json[:id]).to eq(account.id.to_s) + end + + it_behaves_like 'forbidden for wrong scope', 'write:statuses' + end end - describe 'POST #create' do - let(:app) { Fabricate(:application) } - let(:token) { Doorkeeper::AccessToken.find_or_create_for(application: app, resource_owner: nil, scopes: 'read write', use_refresh_token: false) } - let(:agreement) { nil } - - before do - post :create, params: { username: 'test', password: '12345678', email: 'hello@world.tld', agreement: agreement } + describe 'POST /api/v1/accounts' do + subject do + post '/api/v1/accounts', headers: headers, params: { username: 'test', password: '12345678', email: 'hello@world.tld', agreement: agreement } end + let(:client_app) { Fabricate(:application) } + let(:token) { Doorkeeper::AccessToken.find_or_create_for(application: client_app, resource_owner: nil, scopes: 'read write', use_refresh_token: false) } + let(:agreement) { nil } + context 'when given truthy agreement' do let(:agreement) { 'true' } it 'creates a user', :aggregate_failures do + subject + expect(response).to have_http_status(200) expect(body_as_json[:access_token]).to_not be_blank user = User.find_by(email: 'hello@world.tld') expect(user).to_not be_nil - expect(user.created_by_application_id).to eq app.id + expect(user.created_by_application_id).to eq client_app.id end end context 'when given no agreement' do it 'returns http unprocessable entity' do + subject + expect(response).to have_http_status(422) end end end - describe 'POST #follow' do + describe 'POST /api/v1/accounts/:id/follow' do let(:scopes) { 'write:follows' } let(:other_account) { Fabricate(:account, username: 'bob', locked: locked) } context 'when posting to an other account' do - before do - post :follow, params: { id: other_account.id } + subject do + post "/api/v1/accounts/#{other_account.id}/follow", headers: headers end context 'with unlocked account' do let(:locked) { false } it 'creates a following relation between user and target user', :aggregate_failures do + subject + expect(response).to have_http_status(200) json = body_as_json @@ -72,6 +113,8 @@ RSpec.describe Api::V1::AccountsController do let(:locked) { true } it 'creates a follow request relation between user and target user', :aggregate_failures do + subject + expect(response).to have_http_status(200) json = body_as_json @@ -94,48 +137,53 @@ RSpec.describe Api::V1::AccountsController do end it 'changes reblogs option' do - post :follow, params: { id: other_account.id, reblogs: true } + post "/api/v1/accounts/#{other_account.id}/follow", headers: headers, params: { reblogs: true } - json = body_as_json - - expect(json[:following]).to be true - expect(json[:showing_reblogs]).to be true - expect(json[:notifying]).to be false + expect(body_as_json).to include({ + following: true, + showing_reblogs: true, + notifying: false, + }) end it 'changes notify option' do - post :follow, params: { id: other_account.id, notify: true } + post "/api/v1/accounts/#{other_account.id}/follow", headers: headers, params: { notify: true } - json = body_as_json - - expect(json[:following]).to be true - expect(json[:showing_reblogs]).to be false - expect(json[:notifying]).to be true + expect(body_as_json).to include({ + following: true, + showing_reblogs: false, + notifying: true, + }) end it 'changes languages option' do - post :follow, params: { id: other_account.id, languages: %w(en es) } + post "/api/v1/accounts/#{other_account.id}/follow", headers: headers, params: { languages: %w(en es) } - json = body_as_json - - expect(json[:following]).to be true - expect(json[:showing_reblogs]).to be false - expect(json[:notifying]).to be false - expect(json[:languages]).to match_array %w(en es) + expect(body_as_json).to include({ + following: true, + showing_reblogs: false, + notifying: false, + languages: match_array(%w(en es)), + }) end end end - describe 'POST #unfollow' do + describe 'POST /api/v1/accounts/:id/unfollow' do + subject do + post "/api/v1/accounts/#{other_account.id}/unfollow", headers: headers + end + let(:scopes) { 'write:follows' } let(:other_account) { Fabricate(:account, username: 'bob') } before do user.account.follow!(other_account) - post :unfollow, params: { id: other_account.id } end it 'removes the following relation between user and target user', :aggregate_failures do + subject + expect(response).to have_http_status(200) expect(user.account.following?(other_account)).to be false end @@ -143,16 +191,21 @@ RSpec.describe Api::V1::AccountsController do it_behaves_like 'forbidden for wrong scope', 'read:accounts' end - describe 'POST #remove_from_followers' do + describe 'POST /api/v1/accounts/:id/remove_from_followers' do + subject do + post "/api/v1/accounts/#{other_account.id}/remove_from_followers", headers: headers + end + let(:scopes) { 'write:follows' } let(:other_account) { Fabricate(:account, username: 'bob') } before do other_account.follow!(user.account) - post :remove_from_followers, params: { id: other_account.id } end it 'removes the followed relation between user and target user', :aggregate_failures do + subject + expect(response).to have_http_status(200) expect(user.account.followed_by?(other_account)).to be false end @@ -160,16 +213,21 @@ RSpec.describe Api::V1::AccountsController do it_behaves_like 'forbidden for wrong scope', 'read:accounts' end - describe 'POST #block' do + describe 'POST /api/v1/accounts/:id/block' do + subject do + post "/api/v1/accounts/#{other_account.id}/block", headers: headers + end + let(:scopes) { 'write:blocks' } let(:other_account) { Fabricate(:account, username: 'bob') } before do user.account.follow!(other_account) - post :block, params: { id: other_account.id } end it 'creates a blocking relation', :aggregate_failures do + subject + expect(response).to have_http_status(200) expect(user.account.following?(other_account)).to be false expect(user.account.blocking?(other_account)).to be true @@ -178,16 +236,21 @@ RSpec.describe Api::V1::AccountsController do it_behaves_like 'forbidden for wrong scope', 'read:accounts' end - describe 'POST #unblock' do + describe 'POST /api/v1/accounts/:id/unblock' do + subject do + post "/api/v1/accounts/#{other_account.id}/unblock", headers: headers + end + let(:scopes) { 'write:blocks' } let(:other_account) { Fabricate(:account, username: 'bob') } before do user.account.block!(other_account) - post :unblock, params: { id: other_account.id } end it 'removes the blocking relation between user and target user', :aggregate_failures do + subject + expect(response).to have_http_status(200) expect(user.account.blocking?(other_account)).to be false end @@ -195,16 +258,21 @@ RSpec.describe Api::V1::AccountsController do it_behaves_like 'forbidden for wrong scope', 'read:accounts' end - describe 'POST #mute' do + describe 'POST /api/v1/accounts/:id/mute' do + subject do + post "/api/v1/accounts/#{other_account.id}/mute", headers: headers + end + let(:scopes) { 'write:mutes' } let(:other_account) { Fabricate(:account, username: 'bob') } before do user.account.follow!(other_account) - post :mute, params: { id: other_account.id } end it 'mutes notifications', :aggregate_failures do + subject + expect(response).to have_http_status(200) expect(user.account.following?(other_account)).to be true expect(user.account.muting?(other_account)).to be true @@ -214,16 +282,21 @@ RSpec.describe Api::V1::AccountsController do it_behaves_like 'forbidden for wrong scope', 'read:accounts' end - describe 'POST #mute with notifications set to false' do + describe 'POST /api/v1/accounts/:id/mute with notifications set to false' do + subject do + post "/api/v1/accounts/#{other_account.id}/mute", headers: headers, params: { notifications: false } + end + let(:scopes) { 'write:mutes' } let(:other_account) { Fabricate(:account, username: 'bob') } before do user.account.follow!(other_account) - post :mute, params: { id: other_account.id, notifications: false } end it 'does not mute notifications', :aggregate_failures do + subject + expect(response).to have_http_status(200) expect(user.account.following?(other_account)).to be true expect(user.account.muting?(other_account)).to be true @@ -233,16 +306,21 @@ RSpec.describe Api::V1::AccountsController do it_behaves_like 'forbidden for wrong scope', 'read:accounts' end - describe 'POST #mute with nonzero duration set' do + describe 'POST /api/v1/accounts/:id/mute with nonzero duration set' do + subject do + post "/api/v1/accounts/#{other_account.id}/mute", headers: headers, params: { duration: 300 } + end + let(:scopes) { 'write:mutes' } let(:other_account) { Fabricate(:account, username: 'bob') } before do user.account.follow!(other_account) - post :mute, params: { id: other_account.id, duration: 300 } end it 'mutes notifications', :aggregate_failures do + subject + expect(response).to have_http_status(200) expect(user.account.following?(other_account)).to be true expect(user.account.muting?(other_account)).to be true @@ -252,16 +330,21 @@ RSpec.describe Api::V1::AccountsController do it_behaves_like 'forbidden for wrong scope', 'read:accounts' end - describe 'POST #unmute' do + describe 'POST /api/v1/accounts/:id/unmute' do + subject do + post "/api/v1/accounts/#{other_account.id}/unmute", headers: headers + end + let(:scopes) { 'write:mutes' } let(:other_account) { Fabricate(:account, username: 'bob') } before do user.account.mute!(other_account) - post :unmute, params: { id: other_account.id } end it 'removes the muting relation between user and target user', :aggregate_failures do + subject + expect(response).to have_http_status(200) expect(user.account.muting?(other_account)).to be false end From 297839c10c121505dcce421f19d41146171193c7 Mon Sep 17 00:00:00 2001 From: Claire <claire.github-309c@sitedethib.com> Date: Fri, 17 Nov 2023 12:36:04 +0100 Subject: [PATCH 61/63] Rewrite `/api/v1/statuses` tests as request specs (#27891) --- .../api/v1/statuses_spec.rb} | 132 ++++++++++-------- 1 file changed, 73 insertions(+), 59 deletions(-) rename spec/{controllers/api/v1/statuses_controller_spec.rb => requests/api/v1/statuses_spec.rb} (69%) diff --git a/spec/controllers/api/v1/statuses_controller_spec.rb b/spec/requests/api/v1/statuses_spec.rb similarity index 69% rename from spec/controllers/api/v1/statuses_controller_spec.rb rename to spec/requests/api/v1/statuses_spec.rb index 30bafe19a..1b2dd2b5d 100644 --- a/spec/controllers/api/v1/statuses_controller_spec.rb +++ b/spec/requests/api/v1/statuses_spec.rb @@ -2,24 +2,26 @@ require 'rails_helper' -RSpec.describe Api::V1::StatusesController do - render_views - - let(:user) { Fabricate(:user) } - let(:app) { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: app, scopes: scopes) } - +describe '/api/v1/statuses' do context 'with an oauth token' do - before do - allow(controller).to receive(:doorkeeper_token) { token } - end + let(:user) { Fabricate(:user) } + let(:client_app) { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: client_app, scopes: scopes) } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + + describe 'GET /api/v1/statuses/:id' do + subject do + get "/api/v1/statuses/#{status.id}", headers: headers + end - describe 'GET #show' do let(:scopes) { 'read:statuses' } let(:status) { Fabricate(:status, account: user.account) } + it_behaves_like 'forbidden for wrong scope', 'write write:statuses' + it 'returns http success' do - get :show, params: { id: status.id } + subject + expect(response).to have_http_status(200) end @@ -31,11 +33,10 @@ RSpec.describe Api::V1::StatusesController do end it 'returns filter information', :aggregate_failures do - get :show, params: { id: status.id } - json = body_as_json + subject expect(response).to have_http_status(200) - expect(json[:filtered][0]).to include({ + expect(body_as_json[:filtered][0]).to include({ filter: a_hash_including({ id: user.account.custom_filters.first.id.to_s, title: 'filter1', @@ -55,11 +56,10 @@ RSpec.describe Api::V1::StatusesController do end it 'returns filter information', :aggregate_failures do - get :show, params: { id: status.id } - json = body_as_json + subject expect(response).to have_http_status(200) - expect(json[:filtered][0]).to include({ + expect(body_as_json[:filtered][0]).to include({ filter: a_hash_including({ id: user.account.custom_filters.first.id.to_s, title: 'filter1', @@ -78,11 +78,10 @@ RSpec.describe Api::V1::StatusesController do end it 'returns filter information', :aggregate_failures do - get :show, params: { id: status.id } - json = body_as_json + subject expect(response).to have_http_status(200) - expect(json[:reblog][:filtered][0]).to include({ + expect(body_as_json[:reblog][:filtered][0]).to include({ filter: a_hash_including({ id: user.account.custom_filters.first.id.to_s, title: 'filter1', @@ -94,7 +93,7 @@ RSpec.describe Api::V1::StatusesController do end end - describe 'GET #context' do + describe 'GET /api/v1/statuses/:id/context' do let(:scopes) { 'read:statuses' } let(:status) { Fabricate(:status, account: user.account) } @@ -103,20 +102,26 @@ RSpec.describe Api::V1::StatusesController do end it 'returns http success' do - get :context, params: { id: status.id } + get "/api/v1/statuses/#{status.id}/context", headers: headers + expect(response).to have_http_status(200) end end - describe 'POST #create' do + describe 'POST /api/v1/statuses' do + subject do + post '/api/v1/statuses', headers: headers, params: params + end + let(:scopes) { 'write:statuses' } + let(:params) { { status: 'Hello world' } } + + it_behaves_like 'forbidden for wrong scope', 'read read:statuses' context 'with a basic status body' do - before do - post :create, params: { status: 'Hello world' } - end - it 'returns rate limit headers', :aggregate_failures do + subject + expect(response).to have_http_status(200) expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s expect(response.headers['X-RateLimit-Remaining']).to eq (RateLimiter::FAMILIES[:statuses][:limit] - 1).to_s @@ -127,22 +132,22 @@ RSpec.describe Api::V1::StatusesController do let!(:alice) { Fabricate(:account, username: 'alice') } let!(:bob) { Fabricate(:account, username: 'bob') } - before do - post :create, params: { status: '@alice hm, @bob is really annoying lately', allowed_mentions: [alice.id] } - end + let(:params) { { status: '@alice hm, @bob is really annoying lately', allowed_mentions: [alice.id] } } it 'returns serialized extra accounts in body', :aggregate_failures do + subject + expect(response).to have_http_status(422) expect(body_as_json[:unexpected_accounts].map { |a| a.slice(:id, :acct) }).to eq [{ id: bob.id.to_s, acct: bob.acct }] end end context 'with missing parameters' do - before do - post :create, params: {} - end + let(:params) { {} } it 'returns rate limit headers', :aggregate_failures do + subject + expect(response).to have_http_status(422) expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s end @@ -152,10 +157,11 @@ RSpec.describe Api::V1::StatusesController do before do rate_limiter = RateLimiter.new(user.account, family: :statuses) 300.times { rate_limiter.record! } - post :create, params: { status: 'Hello world' } end it 'returns rate limit headers', :aggregate_failures do + subject + expect(response).to have_http_status(429) expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s expect(response.headers['X-RateLimit-Remaining']).to eq '0' @@ -163,29 +169,37 @@ RSpec.describe Api::V1::StatusesController do end end - describe 'DELETE #destroy' do + describe 'DELETE /api/v1/statuses/:id' do + subject do + delete "/api/v1/statuses/#{status.id}", headers: headers + end + let(:scopes) { 'write:statuses' } let(:status) { Fabricate(:status, account: user.account) } - before do - post :destroy, params: { id: status.id } - end + it_behaves_like 'forbidden for wrong scope', 'read read:statuses' it 'removes the status', :aggregate_failures do + subject + expect(response).to have_http_status(200) expect(Status.find_by(id: status.id)).to be_nil end end - describe 'PUT #update' do + describe 'PUT /api/v1/statuses/:id' do + subject do + put "/api/v1/statuses/#{status.id}", headers: headers, params: { status: 'I am updated' } + end + let(:scopes) { 'write:statuses' } let(:status) { Fabricate(:status, account: user.account) } - before do - put :update, params: { id: status.id, status: 'I am updated' } - end + it_behaves_like 'forbidden for wrong scope', 'read read:statuses' it 'updates the status', :aggregate_failures do + subject + expect(response).to have_http_status(200) expect(status.reload.text).to eq 'I am updated' end @@ -193,49 +207,49 @@ RSpec.describe Api::V1::StatusesController do end context 'without an oauth token' do - before do - allow(controller).to receive(:doorkeeper_token).and_return(nil) - end - context 'with a private status' do - let(:status) { Fabricate(:status, account: user.account, visibility: :private) } + let(:status) { Fabricate(:status, visibility: :private) } - describe 'GET #show' do + describe 'GET /api/v1/statuses/:id' do it 'returns http unauthorized' do - get :show, params: { id: status.id } + get "/api/v1/statuses/#{status.id}" + expect(response).to have_http_status(404) end end - describe 'GET #context' do + describe 'GET /api/v1/statuses/:id/context' do before do - Fabricate(:status, account: user.account, thread: status) + Fabricate(:status, thread: status) end it 'returns http unauthorized' do - get :context, params: { id: status.id } + get "/api/v1/statuses/#{status.id}/context" + expect(response).to have_http_status(404) end end end context 'with a public status' do - let(:status) { Fabricate(:status, account: user.account, visibility: :public) } + let(:status) { Fabricate(:status, visibility: :public) } - describe 'GET #show' do + describe 'GET /api/v1/statuses/:id' do it 'returns http success' do - get :show, params: { id: status.id } + get "/api/v1/statuses/#{status.id}" + expect(response).to have_http_status(200) end end - describe 'GET #context' do + describe 'GET /api/v1/statuses/:id/context' do before do - Fabricate(:status, account: user.account, thread: status) + Fabricate(:status, thread: status) end it 'returns http success' do - get :context, params: { id: status.id } + get "/api/v1/statuses/#{status.id}/context" + expect(response).to have_http_status(200) end end From e892efbc4081129fbe807d3b6d8dec024e2175ed Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Fri, 17 Nov 2023 06:52:20 -0500 Subject: [PATCH 62/63] Configure elastic search integration with rspec tag (#27882) --- .github/workflows/test-ruby.yml | 4 ++-- lib/tasks/spec.rake | 10 ---------- spec/rails_helper.rb | 13 ++++++++++--- spec/support/search_data_manager.rb | 4 ++-- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml index 101de66ac..ae25648a0 100644 --- a/.github/workflows/test-ruby.yml +++ b/.github/workflows/test-ruby.yml @@ -227,7 +227,7 @@ jobs: path: tmp/screenshots/ test-search: - name: Testing search + name: Elastic Search integration testing runs-on: ubuntu-latest needs: @@ -314,7 +314,7 @@ jobs: - name: Load database schema run: './bin/rails db:create db:schema:load db:seed' - - run: bundle exec rake spec:search + - run: bin/rspec --tag search - name: Archive logs uses: actions/upload-artifact@v3 diff --git a/lib/tasks/spec.rake b/lib/tasks/spec.rake index ec4cd39bf..8f2cbeea3 100644 --- a/lib/tasks/spec.rake +++ b/lib/tasks/spec.rake @@ -9,13 +9,3 @@ if Rake::Task.task_defined?('spec:system') Rake::Task['spec:system'].enhance ['spec:enable_system_specs'] end - -if Rake::Task.task_defined?('spec:search') - namespace :spec do - task :enable_search_specs do # rubocop:disable Rails/RakeEnvironment - ENV['RUN_SEARCH_SPECS'] = 'true' - end - end - - Rake::Task['spec:search'].enhance ['spec:enable_search_specs'] -end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 68023b70d..7deab6c7f 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -4,7 +4,6 @@ ENV['RAILS_ENV'] ||= 'test' # This needs to be defined before Rails is initialized RUN_SYSTEM_SPECS = ENV.fetch('RUN_SYSTEM_SPECS', false) -RUN_SEARCH_SPECS = ENV.fetch('RUN_SEARCH_SPECS', false) if RUN_SYSTEM_SPECS STREAMING_PORT = ENV.fetch('TEST_STREAMING_PORT', '4020') @@ -55,20 +54,28 @@ RSpec.configure do |config| case type when :system !RUN_SYSTEM_SPECS - when :search - !RUN_SEARCH_SPECS end } + + # By default, skip the elastic search integration specs + config.filter_run_excluding search: true + config.fixture_path = Rails.root.join('spec', 'fixtures') config.use_transactional_fixtures = true config.order = 'random' config.infer_spec_type_from_file_location! config.filter_rails_from_backtrace! + # Set type to `cli` for all CLI specs config.define_derived_metadata(file_path: Regexp.new('spec/lib/mastodon/cli')) do |metadata| metadata[:type] = :cli end + # Set `search` metadata true for all specs in spec/search/ + config.define_derived_metadata(file_path: Regexp.new('spec/search/*')) do |metadata| + metadata[:search] = true + end + config.include Devise::Test::ControllerHelpers, type: :controller config.include Devise::Test::ControllerHelpers, type: :helper config.include Devise::Test::ControllerHelpers, type: :view diff --git a/spec/support/search_data_manager.rb b/spec/support/search_data_manager.rb index 176a674ad..3c7140b48 100644 --- a/spec/support/search_data_manager.rb +++ b/spec/support/search_data_manager.rb @@ -60,7 +60,7 @@ RSpec.configure do |config| end end - config.around :each, type: :search do |example| + config.around :each, :search do |example| search_data_manager.populate_indexes example.run search_data_manager.remove_indexes @@ -73,6 +73,6 @@ RSpec.configure do |config| end def search_examples_present? - RUN_SEARCH_SPECS + RSpec.world.filtered_examples.values.flatten.any? { |example| example.metadata[:search] == true } end end From 60f143e41f4b5d8157a54042c29af08a612249a2 Mon Sep 17 00:00:00 2001 From: Tim Campbell <timetinytim@gmail.com> Date: Sat, 18 Nov 2023 02:14:51 -0800 Subject: [PATCH 63/63] Fixed yarn not installing node packages for streaming (#27967) --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 2b23ea6e4..7e032073b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,6 +38,7 @@ RUN apt-get update && \ corepack enable COPY Gemfile* package.json yarn.lock .yarnrc.yml /opt/mastodon/ +COPY streaming/package.json /opt/mastodon/streaming/ COPY .yarn /opt/mastodon/.yarn RUN bundle install -j"$(nproc)"