From 0a6ec048a8123a26ba9e00896cecbd03c5e3a990 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 16 Nov 2023 14:43:02 +0100 Subject: [PATCH 01/14] 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 8a79eddf0ab303..9c3d9dc2c5202f 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 Date: Thu, 16 Nov 2023 09:36:59 -0500 Subject: [PATCH 02/14] 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 00000000000000..3d3d3739889aba --- /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 00000000000000..4951bb9a4da302 --- /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 00000000000000..361d69aa0a5940 --- /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 00000000000000..08de150ad960e8 --- /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 00000000000000..8cf13cb9001c85 --- /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 00000000000000..da12037edcb987 --- /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 00000000000000..54a113a2b318f8 --- /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 00000000000000..1a169513e96c44 --- /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 00000000000000..1fc84491c3bfa3 --- /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 97c73c5999fd2a..e9484879ff4190 100644 --- a/spec/workers/feed_insert_worker_spec.rb +++ b/spec/workers/feed_insert_worker_spec.rb @@ -8,6 +8,7 @@ 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 @@ 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 00000000000000..4095a5d354b9a9 --- /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 33072704bf3c94..828da5244f55f9 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 00000000000000..91668b5ada6992 --- /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 00000000000000..5071e882b6ad97 --- /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 00000000000000..2bf578357197ae --- /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 daf8a3e2855969..6a5483d1d46e0a 100644 --- a/spec/workers/webhooks/delivery_worker_spec.rb +++ b/spec/workers/webhooks/delivery_worker_spec.rb @@ -5,9 +5,21 @@ 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 Date: Thu, 16 Nov 2023 09:37:52 -0500 Subject: [PATCH 03/14] 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 5c2ba54e000f1c..5b05ac8001d144 100644 --- a/spec/presenters/account_relationships_presenter_spec.rb +++ b/spec/presenters/account_relationships_presenter_spec.rb @@ -23,12 +23,14 @@ 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 c21ffd36ecdd98..853babb84b39b6 100644 --- a/spec/presenters/familiar_followers_presenter_spec.rb +++ b/spec/presenters/familiar_followers_presenter_spec.rb @@ -22,9 +22,12 @@ 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 @@ 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 @@ 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 7746c8cd78c43e..af6a93b82ba814 100644 --- a/spec/presenters/status_relationships_presenter_spec.rb +++ b/spec/presenters/status_relationships_presenter_spec.rb @@ -22,11 +22,13 @@ 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 @@ 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 @@ 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 04/14] 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 32c2546e7c913b..b55bbde3e14fd1 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 05/14] 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 b55bbde3e14fd1..864ebfc8764239 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 Date: Fri, 17 Nov 2023 04:03:46 -0500 Subject: [PATCH 06/14] 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 b44ae9f035c72c..4ab368e12f11c1 100644 --- a/spec/workers/redownload_avatar_worker_spec.rb +++ b/spec/workers/redownload_avatar_worker_spec.rb @@ -5,9 +5,48 @@ 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 767ae7a5ab1dff..3b6f497bb80d42 100644 --- a/spec/workers/redownload_header_worker_spec.rb +++ b/spec/workers/redownload_header_worker_spec.rb @@ -5,9 +5,48 @@ 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 00000000000000..cd561d148b11f0 --- /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 07/14] New Crowdin Translations (automated) (#27914) Co-authored-by: GitHub Actions --- 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 4f5b247b64f360..59b1ca50b842cc 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 20ef4373374d16..16bf5020c50eee 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 dcd5406d05f083..11d4de44024657 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 33eda6f43667a0..bde12a7ae03186 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 5c1ccf46d91de9..10fd9b4c55cc87 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 68033636950bfc..3c99334c3b54de 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 aa8a21edb548fb..70c4efcad1c889 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 5d1aa004b35a4c..3c0d8cd570f4c4 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 88f70cffb0b74f..5bca1cfeffa5e6 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 849a7f463c717c..344e37c976e338 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 e858882f84f0ce..d85a6d428ec80e 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 a7bb4a12fe608d..14e0e13ba32129 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 7d2c6dab95113e..1f199bc45debf9 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 2e000917091d0f..b6716f796d83d9 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 96fc720a410d70..83948b178eae43 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 54123ae4c7d27d..b3c55d4f6f7079 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 284d7739c013c7..02e5991ab12ad2 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 1420be8e093d8b..de72e382c52a68 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 21aa797b47a27c..af9a3eaa0c4732 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 6f941999f6ec7a..e0fa73ef6ddcf4 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 a3402d66084b50..5e4459e438c9f8 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 fe3979f0fec73e..0dce1e666e6e9d 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 a1cc0e26e69cfc..d316143e143e1d 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 69e804878bee7c..0ad315152b72f6 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 1417bed5fc567b..c3110aa5613b92 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 b0a76b32ab8aef..c614215aad5aa9 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 6f9f37ea12a4dd..0b1a7f6ad33e28 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 2166cd2ddaee8c..c86b7c379c5168 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 8649d931039cdb..1307d7ea5a9c98 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 cc6d8994d21739..79f4637720aa3f 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 ea932bc5a76590..e7af27c5fbafec 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 4c52693cc316e9..d4f3010c9efedd 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 34d1c85dc0be3f..72a36250ff2fb1 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 dd7575c57aa111..6aa92c2d1d59e6 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 0075c6592034e8..955ec50e75763f 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 %{domain} 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 a0ff3061fc7acf..60016c93527ea1 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 e11081fcdf213f..7dcb5d632c9bfa 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 09de24a672801b..ad2acdda880925 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 %{app_name} -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 %{app_name}-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 gå hit. 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 %{domain} 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 %{domain} 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 3cf2df3a1bf6ec..42bd6193c1c15c 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: "Tilpass hvordan du vil at din profil og dine innlegg skal bli funnet. 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: "Tilpass hvordan du vil at din profil og dine innlegg skal bli funnet. 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 407908517b9c85..b271b15f1604be 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 79d668d3e47006..0b7498e7d5899d 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 9d4d95a838df0e..5882eae317a97f 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 b98193065b44dd..b788b53e1525ac 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 Date: Fri, 17 Nov 2023 04:50:19 -0500 Subject: [PATCH 08/14] 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 | 115 ++++++++--------- spec/mailers/user_mailer_spec.rb | 119 ++++++++++++------ spec/rails_helper.rb | 1 + .../api/v1/admin/account_actions_spec.rb | 12 +- 7 files changed, 197 insertions(+), 155 deletions(-) diff --git a/Gemfile b/Gemfile index add7b36066e73e..74672ad06ae913 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 20c958e2e0f2c2..beec8b39cc3630 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 423dce88ab09ee..9f0d8999631622 100644 --- a/spec/mailers/admin_mailer_spec.rb +++ b/spec/mailers/admin_mailer_spec.rb @@ -13,14 +13,13 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 78a497c06bd44f..eab196166d3219 100644 --- a/spec/mailers/notification_mailer_spec.rb +++ b/spec/mailers/notification_mailer_spec.rb @@ -8,24 +8,27 @@ 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'] - 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{}) - expect(mail['List-Unsubscribe'].value).to match("&type=#{type}") - expect(mail['List-Unsubscribe-Post'].value).to eq 'List-Unsubscribe=One-Click' + 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{})) + .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 - if thread - it 'renders the thread headers' do - expect(mail['In-Reply-To'].value).to match(//) - expect(mail['References'].value).to match(//) - end + shared_examples 'thread headers' do + it 'renders the email with conversation thread headers' do + conversation_header_regex = // + 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 @@ let(:mail) { prepared_mailer_for(receiver.account).mention } include_examples 'localized subject', 'notification_mailer.mention.subject', name: 'bob' - include_examples 'headers', 'mention', true - - 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' + include_examples 'standard headers', 'mention' + include_examples 'thread headers' + + 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 @@ let(:mail) { prepared_mailer_for(receiver.account).follow } include_examples 'localized subject', 'notification_mailer.follow.subject', name: 'bob' - include_examples 'headers', 'follow', false - - it 'renders the subject' do - expect(mail.subject).to eq('bob is now following you') - end + include_examples 'standard headers', 'follow' - 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 @@ let(:mail) { prepared_mailer_for(own_status.account).favourite } include_examples 'localized subject', 'notification_mailer.favourite.subject', name: 'bob' - include_examples 'headers', 'favourite', true - - 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' + include_examples 'standard headers', 'favourite' + include_examples 'thread headers' + + 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 @@ let(:mail) { prepared_mailer_for(own_status.account).reblog } include_examples 'localized subject', 'notification_mailer.reblog.subject', name: 'bob' - include_examples 'headers', 'reblog', true - - 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' + include_examples 'standard headers', 'reblog' + include_examples 'thread headers' + + 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 @@ 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 - - it 'renders the subject' do - expect(mail.subject).to eq('Pending follower: bob') - end + include_examples 'standard headers', 'follow_request' - 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 c661f5bbda552d..4a439282480486 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -10,9 +10,12 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 79f98f2e2241b0..68023b70df23e4 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -21,6 +21,7 @@ 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 bdf1f08e43b75c..c14e08c21c96d4 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 @@ 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 Date: Fri, 17 Nov 2023 11:37:04 +0100 Subject: [PATCH 09/14] Change to single opt-in during profile setup in onboarding in web UI (#27876) --- .../mastodon/features/onboarding/profile.jsx | 32 ++++++------ app/javascript/mastodon/locales/en.json | 5 +- app/javascript/styles/mastodon/forms.scss | 49 +++++++++++++++++-- 3 files changed, 63 insertions(+), 23 deletions(-) diff --git a/app/javascript/mastodon/features/onboarding/profile.jsx b/app/javascript/mastodon/features/onboarding/profile.jsx index 19ba0bcb956d5c..09e6b2c6c69358 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,17 +136,20 @@ export const Profile = () => {