From 8de5df225edb89ef13f5ea2697876018953a97d9 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 5 Jul 2024 10:54:45 +0200 Subject: [PATCH 001/101] Change instructions to use `bundle exec rails` instead of `rake` (#30917) --- .env.production.sample | 6 +++--- config/initializers/vapid.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.env.production.sample b/.env.production.sample index 0bf01bdc361d93..0b458a1aa96dfb 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -1,5 +1,5 @@ # This is a sample configuration file. You can generate your configuration -# with the `rake mastodon:setup` interactive setup wizard, but to customize +# with the `bundle exec rails mastodon:setup` interactive setup wizard, but to customize # your setup even further, you'll need to edit it manually. This sample does # not demonstrate all available configuration options. Please look at # https://docs.joinmastodon.org/admin/config/ for the full documentation. @@ -40,14 +40,14 @@ ES_PASS=password # Secrets # ------- -# Make sure to use `rake secret` to generate secrets +# Make sure to use `bundle exec rails secret` to generate secrets # ------- SECRET_KEY_BASE= OTP_SECRET= # Web Push # -------- -# Generate with `rake mastodon:webpush:generate_vapid_key` +# Generate with `bundle exec rails mastodon:webpush:generate_vapid_key` # -------- VAPID_PRIVATE_KEY= VAPID_PUBLIC_KEY= diff --git a/config/initializers/vapid.rb b/config/initializers/vapid.rb index 7dd870c8b7d9af..551ede34fb9004 100644 --- a/config/initializers/vapid.rb +++ b/config/initializers/vapid.rb @@ -5,7 +5,7 @@ # You should only generate this once per instance. If you later decide to change it, all push subscription will # be invalidated, requiring the users to access the website again to resubscribe. # - # Generate with `rake mastodon:webpush:generate_vapid_key` task (`docker-compose run --rm web rake mastodon:webpush:generate_vapid_key` if you use docker compose) + # Generate with `bundle exec rails mastodon:webpush:generate_vapid_key` task (`docker-compose run --rm web bundle exec rails mastodon:webpush:generate_vapid_key` if you use docker compose) # # For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html From 8c375d8a5c899b735950a7a88670a1bb2b7adcd2 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 5 Jul 2024 04:57:54 -0400 Subject: [PATCH 002/101] Use `scope module: ...` for settings/2FA routes (#30919) --- config/routes/settings.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/config/routes/settings.rb b/config/routes/settings.rb index 888fa9ecb55543..b14606656b680f 100644 --- a/config/routes/settings.rb +++ b/config/routes/settings.rb @@ -37,13 +37,13 @@ end end - resource :otp_authentication, only: [:show, :create], controller: 'two_factor_authentication/otp_authentication' + scope module: :two_factor_authentication do + resource :otp_authentication, only: [:show, :create], controller: :otp_authentication - resources :webauthn_credentials, only: [:index, :new, :create, :destroy], - path: 'security_keys', - controller: 'two_factor_authentication/webauthn_credentials' do - collection do - get :options + resources :webauthn_credentials, only: [:index, :new, :create, :destroy], path: 'security_keys' do + collection do + get :options + end end end From 81547845ac73f860df2c86594fe0bf346745ce0e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2024 11:09:40 +0200 Subject: [PATCH 003/101] New Crowdin Translations (automated) (#30925) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/ja.json | 2 ++ app/javascript/mastodon/locales/uk.json | 2 +- config/locales/gl.yml | 42 ++++++++++++------------- config/locales/simple_form.gl.yml | 2 +- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 575c68de038955..60788baff8c695 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -411,6 +411,7 @@ "limited_account_hint.action": "構わず表示する", "limited_account_hint.title": "このプロフィールは{domain}のモデレーターによって非表示にされています。", "link_preview.author": "{name}", + "link_preview.more_from_author": "{name}さんの投稿をもっと読む", "lists.account.add": "リストに追加", "lists.account.remove": "リストから外す", "lists.delete": "リストを削除", @@ -693,6 +694,7 @@ "server_banner.administered_by": "管理者", "server_banner.server_stats": "サーバーの情報", "sign_in_banner.create_account": "アカウント作成", + "sign_in_banner.follow_anyone": "連合内の誰でもフォローして投稿を時系列で見ることができます。アルゴリズム、広告、クリックベイトはありません。", "sign_in_banner.sign_in": "ログイン", "sign_in_banner.sso_redirect": "ログインまたは登録", "status.admin_account": "@{name}さんのモデレーション画面を開く", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index 150b808f894651..5d7d040fd9bfa9 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -32,7 +32,7 @@ "account.featured_tags.last_status_never": "Немає дописів", "account.featured_tags.title": "{name} виділяє хештеґи", "account.follow": "Підписатися", - "account.follow_back": "Підписатися взаємно", + "account.follow_back": "Стежити також", "account.followers": "Підписники", "account.followers.empty": "Ніхто ще не підписаний на цього користувача.", "account.followers_counter": "{count, plural, one {{counter} підписник} few {{counter} підписники} many {{counter} підписників} other {{counter} підписники}}", diff --git a/config/locales/gl.yml b/config/locales/gl.yml index ad4744e15b3e7b..c9f08dcad74d3a 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -31,18 +31,18 @@ gl: created_msg: Nota de moderación creada correctamente! destroyed_msg: Nota de moderación eliminada de xeito correcto! accounts: - add_email_domain_block: Bloquear o dominio do email + add_email_domain_block: Bloquear o dominio do enderezo approve: Aprobar approved_msg: Aprobada a solicitude de rexistro de %{username} are_you_sure: Está segura? avatar: Imaxe de perfil by_domain: Dominio change_email: - changed_msg: Email mudado de xeito correcto! - current_email: Email actual - label: Mudar email - new_email: Novo email - submit: Mudar email + changed_msg: Correo cambiado de xeito correcto! + current_email: Enderezo actual + label: Cambiar de enderezo + new_email: Novo enderezo + submit: Cambiar de enderezo title: Mudar email de %{username} change_role: changed_msg: Rol mudado correctamente! @@ -64,10 +64,10 @@ gl: display_name: Nome a amosar domain: Dominio edit: Editar - email: Email - email_status: Estado do email + email: Enderezo de correo + email_status: Estado do correo enable: Activar - enable_sign_in_token_auth: Activar autenticación cun token no email + enable_sign_in_token_auth: Activar autenticación cun token no correo enabled: Activado enabled_msg: Desbloqueada a conta de %{username} followers: Seguidoras @@ -132,7 +132,7 @@ gl: resubscribe: Resubscribir role: Rol search: Procurar - search_same_email_domain: Outras usuarias co mesmo dominio de email + search_same_email_domain: Outras usuarias co mesmo dominio de correo search_same_ip: Outras usuarias co mesmo IP security: Seguridade security_measures: @@ -154,9 +154,9 @@ gl: suspension_irreversible: Elimináronse de xeito irreversible os datos desta conta. Podes reactivar a conta para facela usable novamente pero non recuperará os datos eliminados. suspension_reversible_hint_html: Esta conta foi suspendida, e os datos serán totalmente eliminados o %{date}. Ata entón, a conta pode ser restaurada sen danos. Se desexas eliminar agora mesmo todos os datos da conta, podes facelo aquí embaixo. title: Contas - unblock_email: Desbloquear enderezo de email - unblocked_email_msg: Enderezo de email de %{username} desbloqueado - unconfirmed_email: Email non confirmado + unblock_email: Desbloquear enderezo de correo + unblocked_email_msg: Enderezo de correo de %{username} desbloqueado + unconfirmed_email: Enderezo de correo sen confirmar undo_sensitized: Desmarcar como sensible undo_silenced: Desfacer acalar undo_suspension: Desfacer suspensión @@ -173,12 +173,12 @@ gl: approve_appeal: Aprobar apelación approve_user: Aprobar Usuaria assigned_to_self_report: Asignar denuncia - change_email_user: Editar email da usuaria + change_email_user: Editar correo electrónico da usuaria change_role_user: Cambiar Rol da Usuaria confirm_user: Confirmar usuaria create_account_warning: Crear aviso create_announcement: Crear anuncio - create_canonical_email_block: Crear Bloqueo de email + create_canonical_email_block: Crear Bloqueo de correo electrónico create_custom_emoji: Crear emoticonas personalizadas create_domain_allow: Crear Dominio Permitido create_domain_block: Crear bloquedo do Dominio @@ -188,7 +188,7 @@ gl: create_user_role: Crear Rol demote_user: Degradar usuaria destroy_announcement: Eliminar anuncio - destroy_canonical_email_block: Eliminar Bloqueo de email + destroy_canonical_email_block: Eliminar Bloqueo de correo electrónico destroy_custom_emoji: Eliminar emoticona personalizada destroy_domain_allow: Eliminar Dominio permitido destroy_domain_block: Eliminar bloqueo do Dominio @@ -200,7 +200,7 @@ gl: destroy_user_role: Eliminar Rol disable_2fa_user: Desactivar 2FA disable_custom_emoji: Desactivar emoticona personalizada - disable_sign_in_token_auth_user: Desactivar Autenticación por token no email para Usuaria + disable_sign_in_token_auth_user: Desactivar Autenticación con token no correo para Usuaria disable_user: Desactivar usuaria enable_custom_emoji: Activar emoticona personalizada enable_sign_in_token_auth_user: Activar Autenticación con token no email para Usuaria @@ -211,14 +211,14 @@ gl: reject_user: Rexeitar Usuaria remove_avatar_user: Eliminar avatar reopen_report: Reabrir denuncia - resend_user: Reenviar o email de confirmación + resend_user: Reenviar o correo de confirmación reset_password_user: Restabelecer contrasinal resolve_report: Resolver denuncia sensitive_account: Marca o multimedia da túa conta como sensible silence_account: Silenciar conta suspend_account: Suspender conta unassigned_report: Desasignar denuncia - unblock_email_account: Desbloquear enderezo de email + unblock_email_account: Desbloquear enderezo de correo unsensitive_account: Retira a marca de sensible do multimedia da conta unsilence_account: Deixar de silenciar conta unsuspend_account: Retirar suspensión de conta @@ -660,7 +660,7 @@ gl: delete_data_html: Eliminar o perfil e contidos de @%{acct} para os próximos 30 días a non ser que sexa suspendida nese período preview_preamble_html: "@%{acct} vai recibir un aviso co seguinte contido:" record_strike_html: Anotar un aviso contra @%{acct} para axudarche a xestionar futuros problemas con esta conta - send_email_html: Enviar un email de aviso a @%{acct} + send_email_html: Enviar un correo de aviso a @%{acct} warning_placeholder: Razóns adicionais optativas para a acción de moderación. target_origin: Orixe da conta denunciada title: Denuncias @@ -1060,7 +1060,7 @@ gl: redirect_to_app_html: Ímoste redirixir á app %{app_name}. Se iso non acontece, proba %{clicking_this_link} ou volve ti manualmente á app. registration_complete: Completouse a creación da conta en %{domain}! welcome_title: Benvida, %{name}! - wrong_email_hint: Se o enderezo de email non é correcto, podes cambialo nos axustes da conta. + wrong_email_hint: Se o enderezo de correo non é correcto, podes cambialo nos axustes da conta. delete_account: Eliminar conta delete_account_html: Se queres eliminar a túa conta, podes facelo aquí. Deberás confirmar a acción. description: diff --git a/config/locales/simple_form.gl.yml b/config/locales/simple_form.gl.yml index 0411c45bc169f4..e46ccb8737fd09 100644 --- a/config/locales/simple_form.gl.yml +++ b/config/locales/simple_form.gl.yml @@ -255,7 +255,7 @@ gl: require_invite_text: Pedir unha razón para unirse show_domain_blocks: Amosar dominios bloqueados show_domain_blocks_rationale: Explicar porque están bloqueados os dominios - site_contact_email: Email de contacto + site_contact_email: Correo de contacto site_contact_username: Nome do contacto site_extended_description: Descrición ampla site_short_description: Descrición do servidor From a16c2c99b576b6815c9fefede9acc0be0d60d54d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2024 11:51:55 +0200 Subject: [PATCH 004/101] fix(deps): update dependency cssnano to v7.0.4 (#30927) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/yarn.lock b/yarn.lock index d61ef41632453b..4c1a7922086e40 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6742,16 +6742,16 @@ __metadata: languageName: node linkType: hard -"cssnano-preset-default@npm:^7.0.3": - version: 7.0.3 - resolution: "cssnano-preset-default@npm:7.0.3" +"cssnano-preset-default@npm:^7.0.4": + version: 7.0.4 + resolution: "cssnano-preset-default@npm:7.0.4" dependencies: browserslist: "npm:^4.23.1" css-declaration-sorter: "npm:^7.2.0" cssnano-utils: "npm:^5.0.0" postcss-calc: "npm:^10.0.0" postcss-colormin: "npm:^7.0.1" - postcss-convert-values: "npm:^7.0.1" + postcss-convert-values: "npm:^7.0.2" postcss-discard-comments: "npm:^7.0.1" postcss-discard-duplicates: "npm:^7.0.0" postcss-discard-empty: "npm:^7.0.0" @@ -6778,7 +6778,7 @@ __metadata: postcss-unique-selectors: "npm:^7.0.1" peerDependencies: postcss: ^8.4.31 - checksum: 10c0/ab3e51003efed6542a12d43c10ca693ab26138a1d035697b9be8f07e084e37a78617cbb8028b0a7e7841302ec151f4ecf35cbd763efe291846b62c35ea4c0bb4 + checksum: 10c0/0083821e778bdf7b8aa9589408a01a717be730f73584e7b81756a6fcf87af05b8f17342025e666572a8d573cc30783f2d817b0f7ad63670398bc3135b017ccad languageName: node linkType: hard @@ -6792,14 +6792,14 @@ __metadata: linkType: hard "cssnano@npm:^7.0.0": - version: 7.0.3 - resolution: "cssnano@npm:7.0.3" + version: 7.0.4 + resolution: "cssnano@npm:7.0.4" dependencies: - cssnano-preset-default: "npm:^7.0.3" + cssnano-preset-default: "npm:^7.0.4" lilconfig: "npm:^3.1.2" peerDependencies: postcss: ^8.4.31 - checksum: 10c0/4cbcd1e0ebe0bd83196cc5b16b3a60d3ebc98326c79b2f71df597bb73c8e3ee1f42b89159d7a038acc398251184d648d9dd516f4194e46746f3af6fa74b4aec7 + checksum: 10c0/3939a0b37b11cb4bae92f7916517c7ba21257551f92517b49a640d5df32e855fb7e73321f4be44d2c2de578309c05d711cdcb1976e95607b1b7f92bd4cbd1350 languageName: node linkType: hard @@ -13339,15 +13339,15 @@ __metadata: languageName: node linkType: hard -"postcss-convert-values@npm:^7.0.1": - version: 7.0.1 - resolution: "postcss-convert-values@npm:7.0.1" +"postcss-convert-values@npm:^7.0.2": + version: 7.0.2 + resolution: "postcss-convert-values@npm:7.0.2" dependencies: browserslist: "npm:^4.23.1" postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.4.31 - checksum: 10c0/612f025f179f0f2ad7365db8c0b423614dcb8e1e4061875a4691a39dede0bca758d1a8f9f5c8b08e12af053e9e884f65ca5626ccc723d5b3f420650d67fe3046 + checksum: 10c0/beb59faf6aae97e6d3c233c5e6ed06cc60d65c49eec576036e3d0da1a831a1e827e3d41f5e81d016440b4f0bdf1406268ae069c4d5b38a6667b310c3da079d22 languageName: node linkType: hard From 05f0d510052fb56478986c474192abc7cea3e775 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2024 13:32:29 +0200 Subject: [PATCH 005/101] chore(deps): update dependency sidekiq-scheduler to v5.0.5 (#30918) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index eb6720e4542b77..c0fa8a60399867 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -793,10 +793,10 @@ GEM redis (>= 4.5.0, < 5) sidekiq-bulk (0.2.0) sidekiq - sidekiq-scheduler (5.0.3) + sidekiq-scheduler (5.0.5) rufus-scheduler (~> 3.2) sidekiq (>= 6, < 8) - tilt (>= 1.4.0) + tilt (>= 1.4.0, < 3) sidekiq-unique-jobs (7.1.33) brpoplpush-redis_script (> 0.1.1, <= 2.0.0) concurrent-ruby (~> 1.0, >= 1.0.5) From 016c1e4e788890f0c81a47640f76de136a0a8f32 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Fri, 5 Jul 2024 13:54:38 +0200 Subject: [PATCH 006/101] Improve handling of encoding problems when creating link previews (#30929) --- app/lib/link_details_extractor.rb | 22 +++++++++++---- app/services/fetch_link_card_service.rb | 2 +- .../requests/latin1_posing_as_utf8_broken.txt | 17 +++++++++++ .../latin1_posing_as_utf8_recoverable.txt | 17 +++++++++++ spec/services/fetch_link_card_service_spec.rb | 28 +++++++++++++++++-- 5 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 spec/fixtures/requests/latin1_posing_as_utf8_broken.txt create mode 100644 spec/fixtures/requests/latin1_posing_as_utf8_recoverable.txt diff --git a/app/lib/link_details_extractor.rb b/app/lib/link_details_extractor.rb index dbfdd33fccaf3a..9436d20b58a14b 100644 --- a/app/lib/link_details_extractor.rb +++ b/app/lib/link_details_extractor.rb @@ -156,11 +156,11 @@ def height end def title - html_entities.decode(structured_data&.headline || opengraph_tag('og:title') || document.xpath('//title').map(&:content).first).strip + html_entities_decode(structured_data&.headline || opengraph_tag('og:title') || document.xpath('//title').map(&:content).first).strip end def description - html_entities.decode(structured_data&.description || opengraph_tag('og:description') || meta_tag('description')) + html_entities_decode(structured_data&.description || opengraph_tag('og:description') || meta_tag('description')) end def published_at @@ -180,7 +180,7 @@ def canonical_url end def provider_name - html_entities.decode(structured_data&.publisher_name || opengraph_tag('og:site_name')) + html_entities_decode(structured_data&.publisher_name || opengraph_tag('og:site_name')) end def provider_url @@ -188,7 +188,7 @@ def provider_url end def author_name - html_entities.decode(structured_data&.author_name || opengraph_tag('og:author') || opengraph_tag('og:author:username')) + html_entities_decode(structured_data&.author_name || opengraph_tag('og:author') || opengraph_tag('og:author:username')) end def author_url @@ -257,7 +257,7 @@ def structured_data next if json_ld.blank? - structured_data = StructuredData.new(html_entities.decode(json_ld)) + structured_data = StructuredData.new(html_entities_decode(json_ld)) next unless structured_data.valid? @@ -273,10 +273,11 @@ def document end def detect_encoding_and_parse_document - [detect_encoding, nil, @html_charset, 'UTF-8'].uniq.each do |encoding| + [detect_encoding, nil, @html_charset].uniq.each do |encoding| document = Nokogiri::HTML(@html, nil, encoding) return document if document.to_s.valid_encoding? end + Nokogiri::HTML(@html, nil, 'UTF-8') end def detect_encoding @@ -290,6 +291,15 @@ def detector end end + def html_entities_decode(string) + return if string.nil? + + unicode_string = string.encode('UTF-8') + raise EncodingError, 'cannot convert string to valid UTF-8' unless unicode_string.valid_encoding? + + html_entities.decode(unicode_string) + end + def html_entities @html_entities ||= HTMLEntities.new(:expanded) end diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb index 8bc9f912c55da0..436e024c99bc3d 100644 --- a/app/services/fetch_link_card_service.rb +++ b/app/services/fetch_link_card_service.rb @@ -32,7 +32,7 @@ def call(status) end attach_card if @card&.persisted? - rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, Encoding::UndefinedConversionError => e + rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, EncodingError => e Rails.logger.debug { "Error fetching link #{@original_url}: #{e}" } nil end diff --git a/spec/fixtures/requests/latin1_posing_as_utf8_broken.txt b/spec/fixtures/requests/latin1_posing_as_utf8_broken.txt new file mode 100644 index 00000000000000..ed8a4716a3eba6 --- /dev/null +++ b/spec/fixtures/requests/latin1_posing_as_utf8_broken.txt @@ -0,0 +1,17 @@ +HTTP/1.1 200 OK +server: nginx +date: Thu, 13 Jun 2024 14:33:13 GMT +content-type: text/html; charset=utf-8 +content-length: 158 +accept-ranges: bytes + + + + + + Tofu l'orange + + +

Tofu l'orange

+ + diff --git a/spec/fixtures/requests/latin1_posing_as_utf8_recoverable.txt b/spec/fixtures/requests/latin1_posing_as_utf8_recoverable.txt new file mode 100644 index 00000000000000..a24985832ca551 --- /dev/null +++ b/spec/fixtures/requests/latin1_posing_as_utf8_recoverable.txt @@ -0,0 +1,17 @@ +HTTP/1.1 200 OK +server: nginx +date: Thu, 13 Jun 2024 14:33:13 GMT +content-type: text/html; charset=utf-8 +content-length: 158 +accept-ranges: bytes + + + + + + Tofu with orange sauce + + +

Tofu l'orange

+ + diff --git a/spec/services/fetch_link_card_service_spec.rb b/spec/services/fetch_link_card_service_spec.rb index d83a5275149962..547737b6143b2d 100644 --- a/spec/services/fetch_link_card_service_spec.rb +++ b/spec/services/fetch_link_card_service_spec.rb @@ -27,6 +27,8 @@ stub_request(:get, 'http://example.com/koi8-r').to_return(request_fixture('koi8-r.txt')) stub_request(:get, 'http://example.com/windows-1251').to_return(request_fixture('windows-1251.txt')) stub_request(:get, 'http://example.com/low_confidence_latin1').to_return(request_fixture('low_confidence_latin1.txt')) + stub_request(:get, 'http://example.com/latin1_posing_as_utf8_broken').to_return(request_fixture('latin1_posing_as_utf8_broken.txt')) + stub_request(:get, 'http://example.com/latin1_posing_as_utf8_recoverable').to_return(request_fixture('latin1_posing_as_utf8_recoverable.txt')) stub_request(:get, 'http://example.com/aergerliche-umlaute').to_return(request_fixture('redirect_with_utf8_url.txt')) Rails.cache.write('oembed_endpoint:example.com', oembed_cache) if oembed_cache @@ -159,10 +161,30 @@ end context 'with a URL of a page in ISO-8859-1 encoding, that charlock_holmes cannot detect' do - let(:status) { Fabricate(:status, text: 'Check out http://example.com/low_confidence_latin1') } + context 'when encoding in http header is correct' do + let(:status) { Fabricate(:status, text: 'Check out http://example.com/low_confidence_latin1') } - it 'decodes the HTML' do - expect(status.preview_card.title).to eq("Tofu á l'orange") + it 'decodes the HTML' do + expect(status.preview_card.title).to eq("Tofu á l'orange") + end + end + + context 'when encoding in http header is incorrect' do + context 'when encoding problems appear in unrelated tags' do + let(:status) { Fabricate(:status, text: 'Check out http://example.com/latin1_posing_as_utf8_recoverable') } + + it 'decodes the HTML' do + expect(status.preview_card.title).to eq('Tofu with orange sauce') + end + end + + context 'when encoding problems appear in title tag' do + let(:status) { Fabricate(:status, text: 'Check out http://example.com/latin1_posing_as_utf8_broken') } + + it 'does not create a preview card' do + expect(status.preview_card).to be_nil + end + end end end From 97eddb5906640a79a1cba20823ce10e986f7f317 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Fri, 5 Jul 2024 15:28:52 +0200 Subject: [PATCH 007/101] Fix details extraction when no title exists. (#30933) --- app/lib/link_details_extractor.rb | 2 +- spec/fixtures/requests/page_without_title.txt | 17 +++++++++++++++++ spec/services/fetch_link_card_service_spec.rb | 9 +++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 spec/fixtures/requests/page_without_title.txt diff --git a/app/lib/link_details_extractor.rb b/app/lib/link_details_extractor.rb index 9436d20b58a14b..a62ede2bb1f356 100644 --- a/app/lib/link_details_extractor.rb +++ b/app/lib/link_details_extractor.rb @@ -156,7 +156,7 @@ def height end def title - html_entities_decode(structured_data&.headline || opengraph_tag('og:title') || document.xpath('//title').map(&:content).first).strip + html_entities_decode(structured_data&.headline || opengraph_tag('og:title') || document.xpath('//title').map(&:content).first)&.strip end def description diff --git a/spec/fixtures/requests/page_without_title.txt b/spec/fixtures/requests/page_without_title.txt new file mode 100644 index 00000000000000..0054aa3b7e0db0 --- /dev/null +++ b/spec/fixtures/requests/page_without_title.txt @@ -0,0 +1,17 @@ +HTTP/1.1 200 OK +server: nginx +date: Thu, 13 Jun 2024 14:33:13 GMT +content-type: text/html; charset=utf-8 +content-length: 171 +accept-ranges: bytes + + + + + + + +

I am not a valid page

+

Thankfully, browsers do not care

+ + diff --git a/spec/services/fetch_link_card_service_spec.rb b/spec/services/fetch_link_card_service_spec.rb index 547737b6143b2d..b2cd99cea6d25d 100644 --- a/spec/services/fetch_link_card_service_spec.rb +++ b/spec/services/fetch_link_card_service_spec.rb @@ -30,6 +30,7 @@ stub_request(:get, 'http://example.com/latin1_posing_as_utf8_broken').to_return(request_fixture('latin1_posing_as_utf8_broken.txt')) stub_request(:get, 'http://example.com/latin1_posing_as_utf8_recoverable').to_return(request_fixture('latin1_posing_as_utf8_recoverable.txt')) stub_request(:get, 'http://example.com/aergerliche-umlaute').to_return(request_fixture('redirect_with_utf8_url.txt')) + stub_request(:get, 'http://example.com/page_without_title').to_return(request_fixture('page_without_title.txt')) Rails.cache.write('oembed_endpoint:example.com', oembed_cache) if oembed_cache @@ -112,6 +113,14 @@ end end + context 'with a page that has no title' do + let(:status) { Fabricate(:status, text: 'http://example.com/page_without_title') } + + it 'does not create a preview card' do + expect(status.preview_card).to be_nil + end + end + context 'with a 404 URL' do let(:status) { Fabricate(:status, text: 'http://example.com/not-found') } From 8f5694d79e531b94784697d92bed24d003d77353 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 5 Jul 2024 15:40:53 +0200 Subject: [PATCH 008/101] Fix right-to-left text in preview cards (#30930) --- app/javascript/mastodon/features/status/components/card.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/status/components/card.jsx b/app/javascript/mastodon/features/status/components/card.jsx index f0ae40cbc412f5..ee1fbe0f8fe826 100644 --- a/app/javascript/mastodon/features/status/components/card.jsx +++ b/app/javascript/mastodon/features/status/components/card.jsx @@ -141,7 +141,7 @@ export default class Card extends PureComponent { const showAuthor = !!card.getIn(['authors', 0, 'accountId']); const description = ( -
+
{provider} {card.get('published_at') && <> · } From 63ba69810eca80fc2d10114a79f2988c1b75892f Mon Sep 17 00:00:00 2001 From: Claire Date: Sat, 6 Jul 2024 09:22:24 +0200 Subject: [PATCH 009/101] Fix overflow behavior on profile fields in hover cards (#30928) --- app/javascript/styles/mastodon/components.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 12eac79b984dfa..c114885d171043 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -10453,7 +10453,7 @@ noscript { gap: 4px; dt { - flex: 0 0 auto; + flex: 0 1 auto; color: $dark-text-color; min-width: 0; overflow: hidden; From aca1decf541ed7ac60176e2f0cf33ee4a481685f Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 5 Jul 2024 15:40:53 +0200 Subject: [PATCH 010/101] [Glitch] Fix right-to-left text in preview cards Port 8f5694d79e531b94784697d92bed24d003d77353 to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/features/status/components/card.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/features/status/components/card.jsx b/app/javascript/flavours/glitch/features/status/components/card.jsx index 0d315cbbf10ad9..933f2b1045b559 100644 --- a/app/javascript/flavours/glitch/features/status/components/card.jsx +++ b/app/javascript/flavours/glitch/features/status/components/card.jsx @@ -130,7 +130,7 @@ export default class Card extends PureComponent { const showAuthor = !!card.getIn(['authors', 0, 'accountId']); const description = ( -
+
{provider} {card.get('published_at') && <> · } From a99ae90d76d91285c0336411742fa88618af8775 Mon Sep 17 00:00:00 2001 From: Claire Date: Sat, 6 Jul 2024 09:22:24 +0200 Subject: [PATCH 011/101] [Glitch] Fix overflow behavior on profile fields in hover cards Port 63ba69810eca80fc2d10114a79f2988c1b75892f to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/styles/components.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/styles/components.scss b/app/javascript/flavours/glitch/styles/components.scss index 5c2a130cb976a4..0f78aa45dc766f 100644 --- a/app/javascript/flavours/glitch/styles/components.scss +++ b/app/javascript/flavours/glitch/styles/components.scss @@ -11007,7 +11007,7 @@ noscript { gap: 4px; dt { - flex: 0 0 auto; + flex: 0 1 auto; color: $dark-text-color; min-width: 0; overflow: hidden; From 1d35626dc7f8422e7b2da9a5e3560fa8f6aa0a25 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Sun, 7 Jul 2024 05:39:31 -0400 Subject: [PATCH 012/101] Limit CI push triggers to `main` and `stable-*` branches (#30934) --- .github/workflows/bundler-audit.yml | 6 ++++-- .github/workflows/check-i18n.yml | 8 ++++++-- .github/workflows/codeql.yml | 10 +++++++--- .github/workflows/crowdin-upload.yml | 4 +++- .github/workflows/format-check.yml | 4 ++++ .github/workflows/lint-css.yml | 7 ++++--- .github/workflows/lint-haml.yml | 7 ++++--- .github/workflows/lint-js.yml | 7 ++++--- .github/workflows/lint-ruby.yml | 7 ++++--- .github/workflows/test-js.yml | 7 ++++--- .github/workflows/test-migrations.yml | 7 ++++--- .github/workflows/test-ruby.yml | 7 ++++--- 12 files changed, 52 insertions(+), 29 deletions(-) diff --git a/.github/workflows/bundler-audit.yml b/.github/workflows/bundler-audit.yml index e3e2da0c787a54..2341d6e67f6241 100644 --- a/.github/workflows/bundler-audit.yml +++ b/.github/workflows/bundler-audit.yml @@ -1,8 +1,10 @@ name: Bundler Audit on: + merge_group: push: - branches-ignore: - - 'dependabot/**' + branches: + - 'main' + - 'stable-*' paths: - 'Gemfile*' - '.ruby-version' diff --git a/.github/workflows/check-i18n.yml b/.github/workflows/check-i18n.yml index ceb385933b29bb..5a1c0519665873 100644 --- a/.github/workflows/check-i18n.yml +++ b/.github/workflows/check-i18n.yml @@ -2,9 +2,13 @@ name: Check i18n on: push: - branches: [main] + branches: + - 'main' + - 'stable-*' pull_request: - branches: [main] + branches: + - 'main' + - 'stable-*' env: RAILS_ENV: test diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 6fb93b7fef45a6..8690e9ed6d1639 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,11 +1,15 @@ name: 'CodeQL' on: + merge_group: push: - branches: ['main'] + branches: + - 'main' + - 'stable-*' pull_request: - # The branches below must be a subset of the branches above - branches: ['main'] + branches: + - 'main' + - 'stable-*' schedule: - cron: '22 6 * * 1' diff --git a/.github/workflows/crowdin-upload.yml b/.github/workflows/crowdin-upload.yml index 705af12c025044..4c9d7b096b8e63 100644 --- a/.github/workflows/crowdin-upload.yml +++ b/.github/workflows/crowdin-upload.yml @@ -1,9 +1,11 @@ name: Crowdin / Upload translations on: + merge_group: push: branches: - - main + - 'main' + - 'stable-*' paths: - crowdin.yml - app/javascript/mastodon/locales/en.json diff --git a/.github/workflows/format-check.yml b/.github/workflows/format-check.yml index 2d483b50229626..c10f350a02ef28 100644 --- a/.github/workflows/format-check.yml +++ b/.github/workflows/format-check.yml @@ -1,6 +1,10 @@ name: Check formatting on: + merge_group: push: + branches: + - 'main' + - 'stable-*' pull_request: jobs: diff --git a/.github/workflows/lint-css.yml b/.github/workflows/lint-css.yml index d3b8035cd86521..95fcd569420fdf 100644 --- a/.github/workflows/lint-css.yml +++ b/.github/workflows/lint-css.yml @@ -1,9 +1,10 @@ name: CSS Linting on: + merge_group: push: - branches-ignore: - - 'dependabot/**' - - 'renovate/**' + branches: + - 'main' + - 'stable-*' paths: - 'package.json' - 'yarn.lock' diff --git a/.github/workflows/lint-haml.yml b/.github/workflows/lint-haml.yml index ca4b0c80bfdc3e..a1a9e99c902bfb 100644 --- a/.github/workflows/lint-haml.yml +++ b/.github/workflows/lint-haml.yml @@ -1,9 +1,10 @@ name: Haml Linting on: + merge_group: push: - branches-ignore: - - 'dependabot/**' - - 'renovate/**' + branches: + - 'main' + - 'stable-*' paths: - '.github/workflows/haml-lint-problem-matcher.json' - '.github/workflows/lint-haml.yml' diff --git a/.github/workflows/lint-js.yml b/.github/workflows/lint-js.yml index 1c1ecc2b22049b..7d31a5e20e7804 100644 --- a/.github/workflows/lint-js.yml +++ b/.github/workflows/lint-js.yml @@ -1,9 +1,10 @@ name: JavaScript Linting on: + merge_group: push: - branches-ignore: - - 'dependabot/**' - - 'renovate/**' + branches: + - 'main' + - 'stable-*' paths: - 'package.json' - 'yarn.lock' diff --git a/.github/workflows/lint-ruby.yml b/.github/workflows/lint-ruby.yml index b3a89c3caf1cbd..277e456146fdcf 100644 --- a/.github/workflows/lint-ruby.yml +++ b/.github/workflows/lint-ruby.yml @@ -1,9 +1,10 @@ name: Ruby Linting on: + merge_group: push: - branches-ignore: - - 'dependabot/**' - - 'renovate/**' + branches: + - 'main' + - 'stable-*' paths: - 'Gemfile*' - '.rubocop*.yml' diff --git a/.github/workflows/test-js.yml b/.github/workflows/test-js.yml index 481afdba30366d..e9e43ac9e80184 100644 --- a/.github/workflows/test-js.yml +++ b/.github/workflows/test-js.yml @@ -1,9 +1,10 @@ name: JavaScript Testing on: + merge_group: push: - branches-ignore: - - 'dependabot/**' - - 'renovate/**' + branches: + - 'main' + - 'stable-*' paths: - 'package.json' - 'yarn.lock' diff --git a/.github/workflows/test-migrations.yml b/.github/workflows/test-migrations.yml index 3eaf2c2d7402b8..43de631545c6ed 100644 --- a/.github/workflows/test-migrations.yml +++ b/.github/workflows/test-migrations.yml @@ -1,10 +1,11 @@ name: Historical data migration test on: + merge_group: push: - branches-ignore: - - 'dependabot/**' - - 'renovate/**' + branches: + - 'main' + - 'stable-*' pull_request: jobs: diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml index ef898968d0128c..0e75a0b764a853 100644 --- a/.github/workflows/test-ruby.yml +++ b/.github/workflows/test-ruby.yml @@ -1,10 +1,11 @@ name: Ruby Testing on: + merge_group: push: - branches-ignore: - - 'dependabot/**' - - 'renovate/**' + branches: + - 'main' + - 'stable-*' pull_request: env: From 0899c91d4be0e0846caf53a4b065c4904f34d23a Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Sun, 7 Jul 2024 18:13:10 +0200 Subject: [PATCH 013/101] Add the account hover card for account search results (#30949) --- app/javascript/mastodon/components/account.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/components/account.jsx b/app/javascript/mastodon/components/account.jsx index 18a31cba244867..2a748e67ff139b 100644 --- a/app/javascript/mastodon/components/account.jsx +++ b/app/javascript/mastodon/components/account.jsx @@ -131,7 +131,7 @@ const Account = ({ size = 46, account, onFollow, onBlock, onMute, onMuteNotifica return (
- +
From 981395e4d68a1c0cfccbdb8924bc11ac912d1e05 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Sun, 7 Jul 2024 18:14:15 +0200 Subject: [PATCH 014/101] Remove the `title` attribute when hovering a card anchor (#30948) --- .../mastodon/components/hover_card_controller.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/components/hover_card_controller.tsx b/app/javascript/mastodon/components/hover_card_controller.tsx index 5ca55ebde91049..057ef1aaed4a71 100644 --- a/app/javascript/mastodon/components/hover_card_controller.tsx +++ b/app/javascript/mastodon/components/hover_card_controller.tsx @@ -43,6 +43,7 @@ export const HoverCardController: React.FC = () => { useEffect(() => { let isScrolling = false; let currentAnchor: HTMLElement | null = null; + let currentTitle: string | null = null; const open = (target: HTMLElement) => { target.setAttribute('aria-describedby', 'hover-card'); @@ -75,6 +76,9 @@ export const HoverCardController: React.FC = () => { currentAnchor?.removeAttribute('aria-describedby'); currentAnchor = target; + currentTitle = target.getAttribute('title'); + target.removeAttribute('title'); + setEnterTimeout(() => { open(target); }, enterDelay); @@ -90,11 +94,20 @@ export const HoverCardController: React.FC = () => { }; const handleMouseLeave = (e: MouseEvent) => { + const { target } = e; + if (!currentAnchor) { return; } - if (e.target === currentAnchor || e.target === cardRef.current) { + if ( + currentTitle && + target instanceof HTMLElement && + target === currentAnchor + ) + target.setAttribute('title', currentTitle); + + if (target === currentAnchor || target === cardRef.current) { cancelEnterTimeout(); setLeaveTimeout(() => { From c5883e099937971e30eb17f6da3e5e8800523cfa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 09:11:15 +0200 Subject: [PATCH 015/101] chore(deps): update dependency @testing-library/dom to v10.3.1 (#30954) 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 4c1a7922086e40..f3f166eff5c6fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3357,8 +3357,8 @@ __metadata: linkType: hard "@testing-library/dom@npm:^10.2.0": - version: 10.2.0 - resolution: "@testing-library/dom@npm:10.2.0" + version: 10.3.1 + resolution: "@testing-library/dom@npm:10.3.1" dependencies: "@babel/code-frame": "npm:^7.10.4" "@babel/runtime": "npm:^7.12.5" @@ -3368,7 +3368,7 @@ __metadata: dom-accessibility-api: "npm:^0.5.9" lz-string: "npm:^1.5.0" pretty-format: "npm:^27.0.2" - checksum: 10c0/de582dfbeb632436547a0ca5851b5a714a4a17f8e96ab3dc4fb4e454eef52c912b648b7cb6e9fdf477f3eeef97e698f3250f0ce50846f39d04677a44169209f2 + checksum: 10c0/e898475cd4932225c2962bf9f94353d7d88462c8912881af8e9866cee714c967b21badb0895ec8626123759cddc6663bc40300b1d1bf789957a603086eda6329 languageName: node linkType: hard From 5c3ec727ecff6fe74b12ae533388dbc28b5061fe Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 09:11:29 +0200 Subject: [PATCH 016/101] chore(deps): update dependency typescript to v5.5.3 (#30953) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index f3f166eff5c6fb..a9cc4416655916 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17275,22 +17275,22 @@ __metadata: linkType: hard "typescript@npm:5, typescript@npm:^5.0.4": - version: 5.5.2 - resolution: "typescript@npm:5.5.2" + version: 5.5.3 + resolution: "typescript@npm:5.5.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/8ca39b27b5f9bd7f32db795045933ab5247897660627251e8254180b792a395bf061ea7231947d5d7ffa5cb4cc771970fd4ef543275f9b559f08c9325cccfce3 + checksum: 10c0/f52c71ccbc7080b034b9d3b72051d563601a4815bf3e39ded188e6ce60813f75dbedf11ad15dd4d32a12996a9ed8c7155b46c93a9b9c9bad1049766fe614bbdd languageName: node linkType: hard "typescript@patch:typescript@npm%3A5#optional!builtin, typescript@patch:typescript@npm%3A^5.0.4#optional!builtin": - version: 5.5.2 - resolution: "typescript@patch:typescript@npm%3A5.5.2#optional!builtin::version=5.5.2&hash=379a07" + version: 5.5.3 + resolution: "typescript@patch:typescript@npm%3A5.5.3#optional!builtin::version=5.5.3&hash=379a07" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/a7b7ede75dc7fc32a76d0d0af6b91f5fbd8620890d84c906f663d8783bf3de6d7bd50f0430b8bb55eac88a38934af847ff709e7156e5138b95ae94cbd5f73e5b + checksum: 10c0/911c7811d61f57f07df79c4a35f56a0f426a65426a020e5fcd792f66559f399017205f5f10255329ab5a3d8c2d1f1d19530aeceffda70758a521fae1d469432e languageName: node linkType: hard From bd8481784870b04cd4fda20fa18fdeeb7a07430d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 07:15:48 +0000 Subject: [PATCH 017/101] chore(deps): update dependency irb to v1.14.0 (#30947) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index c0fa8a60399867..1c253719e4ebae 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -346,7 +346,7 @@ GEM activesupport (>= 3.0) nokogiri (>= 1.6) io-console (0.7.2) - irb (1.13.2) + irb (1.14.0) rdoc (>= 4.0.0) reline (>= 0.4.2) jmespath (1.6.2) From bd285cdd02a079b6c1412be04cd28cf759fd0d9d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 09:16:47 +0200 Subject: [PATCH 018/101] fix(deps): update dependency glob to v10.4.3 (#30941) 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 a9cc4416655916..88ab99b2f4bd0b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8995,8 +8995,8 @@ __metadata: linkType: hard "glob@npm:^10.2.2, glob@npm:^10.2.6, glob@npm:^10.3.10": - version: 10.4.2 - resolution: "glob@npm:10.4.2" + version: 10.4.3 + resolution: "glob@npm:10.4.3" dependencies: foreground-child: "npm:^3.1.0" jackspeak: "npm:^3.1.2" @@ -9006,7 +9006,7 @@ __metadata: path-scurry: "npm:^1.11.1" bin: glob: dist/esm/bin.mjs - checksum: 10c0/2c7296695fa75a935f3ad17dc62e4e170a8bb8752cf64d328be8992dd6ad40777939003754e10e9741ff8fbe43aa52fba32d6930d0ffa0e3b74bc3fb5eebaa2f + checksum: 10c0/bea148e5dae96c17e2764f4764c72376a6ab7072b27a21e861ae4af6f97f3e810d79d67f64de52f63ce1d7fdb73b7306f61c65b48d0f61ca7c8647ce8acaf9a7 languageName: node linkType: hard From c29fdadefdaae5a68f6bea85d6ae3b529b6ab7e7 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 8 Jul 2024 03:21:49 -0400 Subject: [PATCH 019/101] Use `scope module: ...` for more API routes (#30935) --- config/routes/api.rb | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/config/routes/api.rb b/config/routes/api.rb index 3eb4bb4b4d58c4..9011916697452d 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -203,9 +203,11 @@ post :unmute end - resource :pin, only: :create, controller: 'accounts/pins' - post :unpin, to: 'accounts/pins#destroy' - resource :note, only: :create, controller: 'accounts/notes' + scope module: :accounts do + resource :pin, only: :create + post :unpin, to: 'pins#destroy' + resource :note, only: :create + end end resources :tags, only: [:show] do @@ -218,7 +220,7 @@ resources :followed_tags, only: [:index] resources :lists, only: [:index, :create, :show, :update, :destroy] do - resource :accounts, only: [:show, :create, :destroy], controller: 'lists/accounts' + resource :accounts, only: [:show, :create, :destroy], module: :lists end namespace :featured_tags do @@ -228,7 +230,7 @@ resources :featured_tags, only: [:index, :create, :destroy] resources :polls, only: [:create, :show] do - resources :votes, only: :create, controller: 'polls/votes' + resources :votes, only: :create, module: :polls end namespace :push do @@ -314,8 +316,10 @@ resources :suggestions, only: [:index] resource :instance, only: [:show] resources :filters, only: [:index, :create, :show, :update, :destroy] do - resources :keywords, only: [:index, :create], controller: 'filters/keywords' - resources :statuses, only: [:index, :create], controller: 'filters/statuses' + scope module: :filters do + resources :keywords, only: [:index, :create] + resources :statuses, only: [:index, :create] + end end namespace :filters do From fd3bfc0710999923c722222579395e9a3d9e3c73 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 07:22:28 +0000 Subject: [PATCH 020/101] fix(deps): update dependency postcss-preset-env to v9.6.0 (#30944) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 453 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 234 insertions(+), 219 deletions(-) diff --git a/yarn.lock b/yarn.lock index 88ab99b2f4bd0b..87586a916c66ff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1558,13 +1558,13 @@ __metadata: languageName: node linkType: hard -"@csstools/cascade-layer-name-parser@npm:^1.0.12": - version: 1.0.12 - resolution: "@csstools/cascade-layer-name-parser@npm:1.0.12" +"@csstools/cascade-layer-name-parser@npm:^1.0.13": + version: 1.0.13 + resolution: "@csstools/cascade-layer-name-parser@npm:1.0.13" peerDependencies: - "@csstools/css-parser-algorithms": ^2.7.0 - "@csstools/css-tokenizer": ^2.3.2 - checksum: 10c0/5f92aefcbb3f4b660cf7b0db54f6a4ba21a32fa1b64ea4f050a6370233152d4f561ecf5c8e98ca231e73c16e0d9f75b20b0a65153e18b14957658c81e0f68213 + "@csstools/css-parser-algorithms": ^2.7.1 + "@csstools/css-tokenizer": ^2.4.1 + checksum: 10c0/a6412fc8601af1baadc8195934aa668d3476e799891c9d0883390f31ec8678e9b565ac14d919bec633bbc086657ac12aa4cd852c718851a2d34517ee6856ff8e languageName: node linkType: hard @@ -1575,52 +1575,52 @@ __metadata: languageName: node linkType: hard -"@csstools/css-calc@npm:^1.2.3": - version: 1.2.3 - resolution: "@csstools/css-calc@npm:1.2.3" +"@csstools/css-calc@npm:^1.2.4": + version: 1.2.4 + resolution: "@csstools/css-calc@npm:1.2.4" peerDependencies: - "@csstools/css-parser-algorithms": ^2.7.0 - "@csstools/css-tokenizer": ^2.3.2 - checksum: 10c0/fb34767ea9638b837167bcecaf945bcc0c5e8f0d811067c4e8c7a57bc8f0955f61107b1ed5e017b95c54acacc8088473e5497a9986bee95b37ec92999e792871 + "@csstools/css-parser-algorithms": ^2.7.1 + "@csstools/css-tokenizer": ^2.4.1 + checksum: 10c0/6233746eb642797b7fbc2cf6e7651e95700b294e78e3c29e8730c3236bb92cf62903efb6e54639e8f877683c40646e137c95e615c4450809b21b61a6192888ca languageName: node linkType: hard -"@csstools/css-color-parser@npm:^2.0.3": - version: 2.0.3 - resolution: "@csstools/css-color-parser@npm:2.0.3" +"@csstools/css-color-parser@npm:^2.0.4": + version: 2.0.4 + resolution: "@csstools/css-color-parser@npm:2.0.4" dependencies: "@csstools/color-helpers": "npm:^4.2.1" - "@csstools/css-calc": "npm:^1.2.3" + "@csstools/css-calc": "npm:^1.2.4" peerDependencies: - "@csstools/css-parser-algorithms": ^2.7.0 - "@csstools/css-tokenizer": ^2.3.2 - checksum: 10c0/d8860e6b9c65de4f90d4c21e4d66471fd858434cf63af80f812a900371343b753b86a256627e8bd024cb8903a6a0181d2d9c0c65ab5d78cf29d084a761e2adba + "@csstools/css-parser-algorithms": ^2.7.1 + "@csstools/css-tokenizer": ^2.4.1 + checksum: 10c0/e009a2f34b6c328daad54262deb86b71c6a7a9a1da1db8ad39cf719641e1a728ed8ddbd23613d519d26deee33b89ba12cb15a6928718c1b3cc5e24ac1ed02f47 languageName: node linkType: hard -"@csstools/css-parser-algorithms@npm:^2.6.3, @csstools/css-parser-algorithms@npm:^2.7.0": - version: 2.7.0 - resolution: "@csstools/css-parser-algorithms@npm:2.7.0" +"@csstools/css-parser-algorithms@npm:^2.6.3, @csstools/css-parser-algorithms@npm:^2.7.1": + version: 2.7.1 + resolution: "@csstools/css-parser-algorithms@npm:2.7.1" peerDependencies: - "@csstools/css-tokenizer": ^2.3.2 - checksum: 10c0/fb84fefdf37c41d170f81b687bf1ee1847a970e51cc1fe3a320e3eaf225383ae9a3c4eb6208b83357dfe18c5114353d780e0c65f05d86d6435e5a9ad9334c834 + "@csstools/css-tokenizer": ^2.4.1 + checksum: 10c0/7d29bef6f5790ddb67d922ad232253bf910e4fa5293f5e4a5ed8b920ae9bd4e8171942df7d8943af23b42fd4e9fb460181394d20c97da9562e6ce98a875e8c47 languageName: node linkType: hard -"@csstools/css-tokenizer@npm:^2.3.1, @csstools/css-tokenizer@npm:^2.3.2": - version: 2.3.2 - resolution: "@csstools/css-tokenizer@npm:2.3.2" - checksum: 10c0/f7d0d8b3e9e2dcdc6547a387253a09dbbacaaffb5c8718bcd7f15dddeefdd441b73fc5f9fad3f03fabef3b37ec4b62be7ff79caab366427fa90eaf54cd8fc452 +"@csstools/css-tokenizer@npm:^2.3.1, @csstools/css-tokenizer@npm:^2.4.1": + version: 2.4.1 + resolution: "@csstools/css-tokenizer@npm:2.4.1" + checksum: 10c0/fe71cee85ec7372da07083d088b6a704f43e5d3d2d8071c4b8a86fae60408b559a218a43f8625bf2f0be5c7f90c8f3ad20a1aae1921119a1c02b51c310cc2b6b languageName: node linkType: hard -"@csstools/media-query-list-parser@npm:^2.1.11, @csstools/media-query-list-parser@npm:^2.1.12": - version: 2.1.12 - resolution: "@csstools/media-query-list-parser@npm:2.1.12" +"@csstools/media-query-list-parser@npm:^2.1.11, @csstools/media-query-list-parser@npm:^2.1.13": + version: 2.1.13 + resolution: "@csstools/media-query-list-parser@npm:2.1.13" peerDependencies: - "@csstools/css-parser-algorithms": ^2.7.0 - "@csstools/css-tokenizer": ^2.3.2 - checksum: 10c0/7395cc710d8f54670c1e7a418a88dcf1ae726316272294ec645f6d79a8e931f5d390ba7ed5d0141d29ad7280cd447b8773143dc7676659413de79228130e1a65 + "@csstools/css-parser-algorithms": ^2.7.1 + "@csstools/css-tokenizer": ^2.4.1 + checksum: 10c0/8bf72342c15581b8f658633436d83c26a214056f6b960ff121b940271f4b1b5b07e9cc3990a73e684fb72319592f0c392408b4f0e08bbe242b2065aa456e2733 languageName: node linkType: hard @@ -1636,46 +1636,60 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-color-function@npm:^3.0.17": - version: 3.0.17 - resolution: "@csstools/postcss-color-function@npm:3.0.17" +"@csstools/postcss-color-function@npm:^3.0.19": + version: 3.0.19 + resolution: "@csstools/postcss-color-function@npm:3.0.19" dependencies: - "@csstools/css-color-parser": "npm:^2.0.3" - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" - "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" + "@csstools/css-color-parser": "npm:^2.0.4" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" + "@csstools/postcss-progressive-custom-properties": "npm:^3.3.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/6d347fc9fe65cb897c275c129103576e551b74a7c47a1a4dc8160da2fad7752bf51e3cfbff339f86b39c723efac33643168d2dfaac4d3624d072875d18a65a4b + checksum: 10c0/067e33d7dfc32b56fe63d4f97464a3eaf27dde720961e44feab6076bd2c172dd4c1bad16aa37a922dcbba470756bd6a13e728d9e71eab6937d48d83873cd1879 languageName: node linkType: hard -"@csstools/postcss-color-mix-function@npm:^2.0.17": - version: 2.0.17 - resolution: "@csstools/postcss-color-mix-function@npm:2.0.17" +"@csstools/postcss-color-mix-function@npm:^2.0.19": + version: 2.0.19 + resolution: "@csstools/postcss-color-mix-function@npm:2.0.19" dependencies: - "@csstools/css-color-parser": "npm:^2.0.3" - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" - "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" + "@csstools/css-color-parser": "npm:^2.0.4" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" + "@csstools/postcss-progressive-custom-properties": "npm:^3.3.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/ba9a406ebe4caba6709878ee26debb06780be5cbf4e6ab7e902d79ca6e21ec6a8409b9dc0a5ef36fc4bf54bf2bd8f9642b72da8d7939145f99dc40fedd2be9d2 + checksum: 10c0/e967d93672a065806dc78da0153f8b4f5087f7c3ddfe361eba4942780760d47b317124913c9b0dda7f9bfff1253f77d1b6debd8a6a2aa3a6c80e263101da5e8c languageName: node linkType: hard -"@csstools/postcss-exponential-functions@npm:^1.0.8": - version: 1.0.8 - resolution: "@csstools/postcss-exponential-functions@npm:1.0.8" +"@csstools/postcss-content-alt-text@npm:^1.0.0": + version: 1.0.0 + resolution: "@csstools/postcss-content-alt-text@npm:1.0.0" + dependencies: + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" + "@csstools/postcss-progressive-custom-properties": "npm:^3.3.0" + "@csstools/utilities": "npm:^1.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10c0/0c2c64857ac652989d00c3d2ba49d0cd1cc245193cba6724d2f5841aa990ee6a07267cfebc6fabde6a6246616df60373006d17c5ea9b904129fbfd826dc10a8d + languageName: node + linkType: hard + +"@csstools/postcss-exponential-functions@npm:^1.0.9": + version: 1.0.9 + resolution: "@csstools/postcss-exponential-functions@npm:1.0.9" dependencies: - "@csstools/css-calc": "npm:^1.2.3" - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" + "@csstools/css-calc": "npm:^1.2.4" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" peerDependencies: postcss: ^8.4 - checksum: 10c0/6b049801fc1275b34f43ffbb915f447a54cbff7bf48ab0705c3ad1ffde055cb876c4dc24e7a9162cd65e219457328e298a673f6176446493db17cf7af6f90dc0 + checksum: 10c0/eaec29ef6ec201786c606176235dced4af1922d5ac56c6b0993ad2e7d87464a32702d9b28cae9a76e8527f741b50cbc31d4c646f45d02dc69d520f241b3e7878 languageName: node linkType: hard @@ -1691,59 +1705,59 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-gamut-mapping@npm:^1.0.10": - version: 1.0.10 - resolution: "@csstools/postcss-gamut-mapping@npm:1.0.10" +"@csstools/postcss-gamut-mapping@npm:^1.0.11": + version: 1.0.11 + resolution: "@csstools/postcss-gamut-mapping@npm:1.0.11" dependencies: - "@csstools/css-color-parser": "npm:^2.0.3" - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" + "@csstools/css-color-parser": "npm:^2.0.4" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" peerDependencies: postcss: ^8.4 - checksum: 10c0/6c2dab6a84f81904bed89cb584bd9bc6a904b49a4fa315b17be65c7d68baefe592561ee439660d5602b7481bac3be9a93189dc45404764524495400f34c6b6e6 + checksum: 10c0/29e755013f1d1de34eb62a931ed410d2830ca3dfc81476cb3c72d9d3260b85a9adedc51aa548550c6e308f3f9640c489e6953db40e9cac9835d0421d5b14ef1f languageName: node linkType: hard -"@csstools/postcss-gradients-interpolation-method@npm:^4.0.18": - version: 4.0.18 - resolution: "@csstools/postcss-gradients-interpolation-method@npm:4.0.18" +"@csstools/postcss-gradients-interpolation-method@npm:^4.0.20": + version: 4.0.20 + resolution: "@csstools/postcss-gradients-interpolation-method@npm:4.0.20" dependencies: - "@csstools/css-color-parser": "npm:^2.0.3" - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" - "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" + "@csstools/css-color-parser": "npm:^2.0.4" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" + "@csstools/postcss-progressive-custom-properties": "npm:^3.3.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/23c431068ac205392b4953dbce411e208e79e221ba8030c5e23c0b82e8fd53bc3bc4f2cdc47050f5d91a4ac69cb80f4f1853b213aa8072fa60a6cb6ff0621e04 + checksum: 10c0/6588825a72a1471e2d6036c8cf7dbad2bf05f369d96dbdd68ff5ce7ff91803b8ee1146f5f1bf6f3ab6299944549da872914664c3f9e8ae5a31847f76f0085c74 languageName: node linkType: hard -"@csstools/postcss-hwb-function@npm:^3.0.16": - version: 3.0.16 - resolution: "@csstools/postcss-hwb-function@npm:3.0.16" +"@csstools/postcss-hwb-function@npm:^3.0.18": + version: 3.0.18 + resolution: "@csstools/postcss-hwb-function@npm:3.0.18" dependencies: - "@csstools/css-color-parser": "npm:^2.0.3" - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" - "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" + "@csstools/css-color-parser": "npm:^2.0.4" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" + "@csstools/postcss-progressive-custom-properties": "npm:^3.3.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/4deca8831a69038aff719a77df92c53578bb28e23cc61dc4ea7b1d912b1b685683a9c6232396c2616948ac2e8488ad1e2009c9c8ed30c493d97ba8ad37b6418d + checksum: 10c0/e9d76b0b2f9c54920124ca1804b49e3f5b26e003729418b5ef4b340ff1baa4779da1c02be618888fdbcc2d0747182352efbbd3ffe128e2417928c35c25443789 languageName: node linkType: hard -"@csstools/postcss-ic-unit@npm:^3.0.6": - version: 3.0.6 - resolution: "@csstools/postcss-ic-unit@npm:3.0.6" +"@csstools/postcss-ic-unit@npm:^3.0.7": + version: 3.0.7 + resolution: "@csstools/postcss-ic-unit@npm:3.0.7" dependencies: - "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" + "@csstools/postcss-progressive-custom-properties": "npm:^3.3.0" "@csstools/utilities": "npm:^1.0.0" postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/a4b962327d433419fdcfdcf620ce6a5cf09aa3c93029ad08b035df1e2bc35caae31de49f1d14218de0656fced35c0d2e07e5ff7b8099c29dbfb40395fc283234 + checksum: 10c0/2add905b75860c64d7174886fecfc76d86e3818f42f003f4bbfc0604cc7f0f31c6dbd1651e6b9512fea876190d80033578ae49e813b64b17c8cf3b1f03d8e146 languageName: node linkType: hard @@ -1768,17 +1782,17 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-light-dark-function@npm:^1.0.6": - version: 1.0.6 - resolution: "@csstools/postcss-light-dark-function@npm:1.0.6" +"@csstools/postcss-light-dark-function@npm:^1.0.8": + version: 1.0.8 + resolution: "@csstools/postcss-light-dark-function@npm:1.0.8" dependencies: - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" - "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" + "@csstools/postcss-progressive-custom-properties": "npm:^3.3.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/6b2c64860d789cd3e3ce875c01259333911f6e32a751a7475604de8022c13abcb578e5cb941b51bd3a2022bee883df3f6b64800c6e3559b06da283d968aeb615 + checksum: 10c0/78fa6d799d38f14af1b32b534eedbec9478033e1fbc5a4e820f2421e865673d010b69b391546686ceb408ead64d79bb4eba2a4fb1fc9f0de70ff21e3ff8477c6 languageName: node linkType: hard @@ -1820,42 +1834,42 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-logical-viewport-units@npm:^2.0.10": - version: 2.0.10 - resolution: "@csstools/postcss-logical-viewport-units@npm:2.0.10" +"@csstools/postcss-logical-viewport-units@npm:^2.0.11": + version: 2.0.11 + resolution: "@csstools/postcss-logical-viewport-units@npm:2.0.11" dependencies: - "@csstools/css-tokenizer": "npm:^2.3.2" + "@csstools/css-tokenizer": "npm:^2.4.1" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/fe142b11e0e8ccab4667cc5db90b45e67b7d11eaf5c038e91d867e1b18a315ef0859114185aeb48fdc1ce05986be8b644d6157fe9e19da7281f7023c99eb8877 + checksum: 10c0/20207e9b7fc3ab52df5fcd06fde71fca4fd22bd6bd451cfc2ec6ea69994708b7fc5381e203dc4367293a8de00b1eca7a3ebe89cfa9b933d2f2cb8e3ac4d5aa86 languageName: node linkType: hard -"@csstools/postcss-media-minmax@npm:^1.1.7": - version: 1.1.7 - resolution: "@csstools/postcss-media-minmax@npm:1.1.7" +"@csstools/postcss-media-minmax@npm:^1.1.8": + version: 1.1.8 + resolution: "@csstools/postcss-media-minmax@npm:1.1.8" dependencies: - "@csstools/css-calc": "npm:^1.2.3" - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" - "@csstools/media-query-list-parser": "npm:^2.1.12" + "@csstools/css-calc": "npm:^1.2.4" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" + "@csstools/media-query-list-parser": "npm:^2.1.13" peerDependencies: postcss: ^8.4 - checksum: 10c0/a02943a17b540cbd909b55bbb1f8c9331badc51b613279bbdb7127c9921a5d0675bb41675a3b4d0f15e9586120e5a96d9b9786b63b2c594fbb3a238e860c6ad8 + checksum: 10c0/7d666905282c7a89387dbce84f3429bad04870e0de264c5b1ce3e6f042b8eb72d585a18b2d7ac5e1a8c7f6785892da3cc7f6ea0b48069b06e9d383bdbc149b4a languageName: node linkType: hard -"@csstools/postcss-media-queries-aspect-ratio-number-values@npm:^2.0.10": - version: 2.0.10 - resolution: "@csstools/postcss-media-queries-aspect-ratio-number-values@npm:2.0.10" +"@csstools/postcss-media-queries-aspect-ratio-number-values@npm:^2.0.11": + version: 2.0.11 + resolution: "@csstools/postcss-media-queries-aspect-ratio-number-values@npm:2.0.11" dependencies: - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" - "@csstools/media-query-list-parser": "npm:^2.1.12" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" + "@csstools/media-query-list-parser": "npm:^2.1.13" peerDependencies: postcss: ^8.4 - checksum: 10c0/d7879e72df98d9fe2e5d85a64837e7a73c2df1aea8659d65516f0acb070317edd353531882f0bdfd81510703d1da30d6da861052a0bda85fde1f9eab94b1e467 + checksum: 10c0/b4023a1951b7661196332852ce714a4e2fb4f1a67164ec0944e28a009b389e59c67e9de790920fcd082b122276414dd39c12ae12a4566e59e1bbcc794560a870 languageName: node linkType: hard @@ -1882,44 +1896,44 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-oklab-function@npm:^3.0.17": - version: 3.0.17 - resolution: "@csstools/postcss-oklab-function@npm:3.0.17" +"@csstools/postcss-oklab-function@npm:^3.0.19": + version: 3.0.19 + resolution: "@csstools/postcss-oklab-function@npm:3.0.19" dependencies: - "@csstools/css-color-parser": "npm:^2.0.3" - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" - "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" + "@csstools/css-color-parser": "npm:^2.0.4" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" + "@csstools/postcss-progressive-custom-properties": "npm:^3.3.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/ff27a4b6fd8490439aa0f3c91ffa2a42a8cf539d7306d9329cef7ca59f28317cee40253f402d19a18c196471fd39a05842d2974d92f1b131dc748074d91ac4ee + checksum: 10c0/2909f76ba408c9f60b61c479994c96200b0e1d3dbf524d5ae6dc5ca1e21d38caf974595e0d071c3900dbe3568376928085dd811aa24ea3e715bcd9de26fb0fa9 languageName: node linkType: hard -"@csstools/postcss-progressive-custom-properties@npm:^3.2.0": - version: 3.2.0 - resolution: "@csstools/postcss-progressive-custom-properties@npm:3.2.0" +"@csstools/postcss-progressive-custom-properties@npm:^3.3.0": + version: 3.3.0 + resolution: "@csstools/postcss-progressive-custom-properties@npm:3.3.0" dependencies: postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/829880844fbbeef1c67e0b380057e574659b4caed38c8414c17d7eb4a0cc727afa1cd74a889bc7ca79c819ecae757810356706901cf6bb677a36ca123915cbb7 + checksum: 10c0/6c9987d65049a70b5090dcfe42fde9ab2b3cb88911a81bb6651ed81c8baf99502ff2cbec0cb3e022426fa994b558b4bf33fd791ccdcdf683dde75b4865d34f39 languageName: node linkType: hard -"@csstools/postcss-relative-color-syntax@npm:^2.0.17": - version: 2.0.17 - resolution: "@csstools/postcss-relative-color-syntax@npm:2.0.17" +"@csstools/postcss-relative-color-syntax@npm:^2.0.19": + version: 2.0.19 + resolution: "@csstools/postcss-relative-color-syntax@npm:2.0.19" dependencies: - "@csstools/css-color-parser": "npm:^2.0.3" - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" - "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" + "@csstools/css-color-parser": "npm:^2.0.4" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" + "@csstools/postcss-progressive-custom-properties": "npm:^3.3.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/46226351b3825323e3496dcee44ff354cd3ccc9241d837659e1311f428f0b4dc878d9bb762cbb8f63243b7af346728ab7a46c311f9dc38bb609147523c698eab + checksum: 10c0/f0aff764f4889ff664b6fa94ddfa5a22daf39354aa2d2ac0eab893eb3ed841b7d2a72131393334d6a5379445fc80f92ab5bd63d4dc3b43746bc7c9055da46591 languageName: node linkType: hard @@ -1934,16 +1948,16 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-stepped-value-functions@npm:^3.0.9": - version: 3.0.9 - resolution: "@csstools/postcss-stepped-value-functions@npm:3.0.9" +"@csstools/postcss-stepped-value-functions@npm:^3.0.10": + version: 3.0.10 + resolution: "@csstools/postcss-stepped-value-functions@npm:3.0.10" dependencies: - "@csstools/css-calc": "npm:^1.2.3" - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" + "@csstools/css-calc": "npm:^1.2.4" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" peerDependencies: postcss: ^8.4 - checksum: 10c0/bafe80947abc8613903f1f3f1939ece9780696774f15960aef229733e40e483dc2830145426d49c4f6d0b1dabb35f812c8a2dda0d0dcddc930321e36b5c6ca0b + checksum: 10c0/f9ebe50fb884d002aa40070196a827816f635b891fd2147ae5ddf1ad6df5bddbb50783d6786897bb3dffa33052565e38289392040cf4454aaa179ab00353117d languageName: node linkType: hard @@ -1959,16 +1973,16 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-trigonometric-functions@npm:^3.0.9": - version: 3.0.9 - resolution: "@csstools/postcss-trigonometric-functions@npm:3.0.9" +"@csstools/postcss-trigonometric-functions@npm:^3.0.10": + version: 3.0.10 + resolution: "@csstools/postcss-trigonometric-functions@npm:3.0.10" dependencies: - "@csstools/css-calc": "npm:^1.2.3" - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" + "@csstools/css-calc": "npm:^1.2.4" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" peerDependencies: postcss: ^8.4 - checksum: 10c0/7a439d31a63d35986dab634d9e415f7ce7c32a2d3d382052b5b730a259a12e44c5f1b14e318d79086253e3d5d4f7d942d0e7317d92eb3421dd08824eebec45fb + checksum: 10c0/31adcc66510d9788ccb0669d2761517a6135b13692007d8e4334bc0e8d3515dfecfbdcd04e060d0c09a0f5fc2f12db92221b9d53e92b65b044c89cde9a3424cb languageName: node linkType: hard @@ -6726,10 +6740,10 @@ __metadata: languageName: node linkType: hard -"cssdb@npm:^8.0.0": - version: 8.0.0 - resolution: "cssdb@npm:8.0.0" - checksum: 10c0/d9a31b760214624352000b16a8f7194c357f66b6c445e663ab58dd03b6f0f53efaaca6d6f96200d666e205894d2d1c346664ad993d9522ff9fc1c331804a8d62 +"cssdb@npm:^8.1.0": + version: 8.1.0 + resolution: "cssdb@npm:8.1.0" + checksum: 10c0/1fa1f1566c7e9964f5c71e443583eaba16a90933a3ef6803815c4281d084b75da948c415bade33d7085894fe0929c082fcb3135bf4400048cfff40d227ebd5dd languageName: node linkType: hard @@ -13286,18 +13300,18 @@ __metadata: languageName: node linkType: hard -"postcss-color-functional-notation@npm:^6.0.12": - version: 6.0.12 - resolution: "postcss-color-functional-notation@npm:6.0.12" +"postcss-color-functional-notation@npm:^6.0.14": + version: 6.0.14 + resolution: "postcss-color-functional-notation@npm:6.0.14" dependencies: - "@csstools/css-color-parser": "npm:^2.0.3" - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" - "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" + "@csstools/css-color-parser": "npm:^2.0.4" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" + "@csstools/postcss-progressive-custom-properties": "npm:^3.3.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/2e8faecd2609e1b4eb8c1cab21ecca5e746916795df20e6997d66eb61c29fbb01d3e75fef3e0b3e1c181918a2186570441b81779b1fc429d6d8823fbfa164231 + checksum: 10c0/fdc5188e19c3923da32fe08d50e55d0b3ca1cedf99f46331baa0a4bbd73a1fc6b4447b0346ab16049032b56ab84b98b4758a0ede7c237637e35a4cc60caac141 languageName: node linkType: hard @@ -13351,46 +13365,46 @@ __metadata: languageName: node linkType: hard -"postcss-custom-media@npm:^10.0.7": - version: 10.0.7 - resolution: "postcss-custom-media@npm:10.0.7" +"postcss-custom-media@npm:^10.0.8": + version: 10.0.8 + resolution: "postcss-custom-media@npm:10.0.8" dependencies: - "@csstools/cascade-layer-name-parser": "npm:^1.0.12" - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" - "@csstools/media-query-list-parser": "npm:^2.1.12" + "@csstools/cascade-layer-name-parser": "npm:^1.0.13" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" + "@csstools/media-query-list-parser": "npm:^2.1.13" peerDependencies: postcss: ^8.4 - checksum: 10c0/4171385ab9370806861dcf7597e53fd6aa1862e77b475c9c565c95bfcc2b950f920f8da26a6dbec42e257388bca97c274635662b5e81fe3905b5e37babe06569 + checksum: 10c0/673ca0058a2f2357a83b33ce00bbeee7cda92621c08472fa55d7ac7ae56f5f8f979132528d537f2dedf715d35a8f9b14b2f0ab6b45423d49e2554c19aab3c827 languageName: node linkType: hard -"postcss-custom-properties@npm:^13.3.11": - version: 13.3.11 - resolution: "postcss-custom-properties@npm:13.3.11" +"postcss-custom-properties@npm:^13.3.12": + version: 13.3.12 + resolution: "postcss-custom-properties@npm:13.3.12" dependencies: - "@csstools/cascade-layer-name-parser": "npm:^1.0.12" - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" + "@csstools/cascade-layer-name-parser": "npm:^1.0.13" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" "@csstools/utilities": "npm:^1.0.0" postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/4aa95628aa5d5b6df4dfeedbc3891b9666db88d75930cadc14d2fbba0a1b72f4e3cc3d83b5a0c0b8ce44f85b4fda6ebd7fe7792a1abc0a14d7d63b9f170d299c + checksum: 10c0/6af9f6ac94a6ac887749cd38d4586349f6aca29269ebfdb837019a3ba0130032f0ff4899b431b5c348f4ac79a7b16fb7300a256514a6a68e32a63489c18a70e7 languageName: node linkType: hard -"postcss-custom-selectors@npm:^7.1.11": - version: 7.1.11 - resolution: "postcss-custom-selectors@npm:7.1.11" +"postcss-custom-selectors@npm:^7.1.12": + version: 7.1.12 + resolution: "postcss-custom-selectors@npm:7.1.12" dependencies: - "@csstools/cascade-layer-name-parser": "npm:^1.0.12" - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" + "@csstools/cascade-layer-name-parser": "npm:^1.0.13" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" postcss-selector-parser: "npm:^6.1.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/f37d2e34239e868b35b7970ec97a7a8f657a9f92ed2b221af44f19949f7c3aedcecd0abb5fa1acb120c5ceffdf7a20869338956a37d7bfc37a83d8088f5d3dd2 + checksum: 10c0/78a7930e4f97c42b544f00c06272264432d47f9df777684b57673bb971b7ab49d5d6fb9289a5a869125e7e50dcd0cad65cf8846501253084b73a42ffab41b2c5 languageName: node linkType: hard @@ -13443,16 +13457,16 @@ __metadata: languageName: node linkType: hard -"postcss-double-position-gradients@npm:^5.0.6": - version: 5.0.6 - resolution: "postcss-double-position-gradients@npm:5.0.6" +"postcss-double-position-gradients@npm:^5.0.7": + version: 5.0.7 + resolution: "postcss-double-position-gradients@npm:5.0.7" dependencies: - "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" + "@csstools/postcss-progressive-custom-properties": "npm:^3.3.0" "@csstools/utilities": "npm:^1.0.0" postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/9b24b13043fe506c0ddd94e707fe4f21f4f9a6c05ca49a4f45e23412951fd6a4cfa0095002d10b322ca8be60df0badae3715a27eefdeb7bf8da4fdd1ecd5d7a2 + checksum: 10c0/52d96a34aa3e2e251edeaa2d4c2dd106c687f7910ec18266693656c0edd003384b927c855cecac07f52b5c7bdccd140abdc7e27082ce4c3755e3a966206a2cb9 languageName: node linkType: hard @@ -13508,18 +13522,18 @@ __metadata: languageName: node linkType: hard -"postcss-lab-function@npm:^6.0.17": - version: 6.0.17 - resolution: "postcss-lab-function@npm:6.0.17" +"postcss-lab-function@npm:^6.0.19": + version: 6.0.19 + resolution: "postcss-lab-function@npm:6.0.19" dependencies: - "@csstools/css-color-parser": "npm:^2.0.3" - "@csstools/css-parser-algorithms": "npm:^2.7.0" - "@csstools/css-tokenizer": "npm:^2.3.2" - "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" + "@csstools/css-color-parser": "npm:^2.0.4" + "@csstools/css-parser-algorithms": "npm:^2.7.1" + "@csstools/css-tokenizer": "npm:^2.4.1" + "@csstools/postcss-progressive-custom-properties": "npm:^3.3.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/a331f188b02cc8beb315150232b6b58bc5793e8d61585973d352a9b4d370b908ff354ccf9ea1ba20a956fd37ea4ada918ea975c8d4f69e850d26edf0106436e8 + checksum: 10c0/d9a91fb57dcbe967260df86e22ca335a5444f1f34d128fa7b5dbf2522772f2138ad708f1f20f0a59035d66ed736e82972ca7f1b669a157534a17ee8898af1921 languageName: node linkType: hard @@ -13840,60 +13854,61 @@ __metadata: linkType: hard "postcss-preset-env@npm:^9.5.2": - version: 9.5.15 - resolution: "postcss-preset-env@npm:9.5.15" + version: 9.6.0 + resolution: "postcss-preset-env@npm:9.6.0" dependencies: "@csstools/postcss-cascade-layers": "npm:^4.0.6" - "@csstools/postcss-color-function": "npm:^3.0.17" - "@csstools/postcss-color-mix-function": "npm:^2.0.17" - "@csstools/postcss-exponential-functions": "npm:^1.0.8" + "@csstools/postcss-color-function": "npm:^3.0.19" + "@csstools/postcss-color-mix-function": "npm:^2.0.19" + "@csstools/postcss-content-alt-text": "npm:^1.0.0" + "@csstools/postcss-exponential-functions": "npm:^1.0.9" "@csstools/postcss-font-format-keywords": "npm:^3.0.2" - "@csstools/postcss-gamut-mapping": "npm:^1.0.10" - "@csstools/postcss-gradients-interpolation-method": "npm:^4.0.18" - "@csstools/postcss-hwb-function": "npm:^3.0.16" - "@csstools/postcss-ic-unit": "npm:^3.0.6" + "@csstools/postcss-gamut-mapping": "npm:^1.0.11" + "@csstools/postcss-gradients-interpolation-method": "npm:^4.0.20" + "@csstools/postcss-hwb-function": "npm:^3.0.18" + "@csstools/postcss-ic-unit": "npm:^3.0.7" "@csstools/postcss-initial": "npm:^1.0.1" "@csstools/postcss-is-pseudo-class": "npm:^4.0.8" - "@csstools/postcss-light-dark-function": "npm:^1.0.6" + "@csstools/postcss-light-dark-function": "npm:^1.0.8" "@csstools/postcss-logical-float-and-clear": "npm:^2.0.1" "@csstools/postcss-logical-overflow": "npm:^1.0.1" "@csstools/postcss-logical-overscroll-behavior": "npm:^1.0.1" "@csstools/postcss-logical-resize": "npm:^2.0.1" - "@csstools/postcss-logical-viewport-units": "npm:^2.0.10" - "@csstools/postcss-media-minmax": "npm:^1.1.7" - "@csstools/postcss-media-queries-aspect-ratio-number-values": "npm:^2.0.10" + "@csstools/postcss-logical-viewport-units": "npm:^2.0.11" + "@csstools/postcss-media-minmax": "npm:^1.1.8" + "@csstools/postcss-media-queries-aspect-ratio-number-values": "npm:^2.0.11" "@csstools/postcss-nested-calc": "npm:^3.0.2" "@csstools/postcss-normalize-display-values": "npm:^3.0.2" - "@csstools/postcss-oklab-function": "npm:^3.0.17" - "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" - "@csstools/postcss-relative-color-syntax": "npm:^2.0.17" + "@csstools/postcss-oklab-function": "npm:^3.0.19" + "@csstools/postcss-progressive-custom-properties": "npm:^3.3.0" + "@csstools/postcss-relative-color-syntax": "npm:^2.0.19" "@csstools/postcss-scope-pseudo-class": "npm:^3.0.1" - "@csstools/postcss-stepped-value-functions": "npm:^3.0.9" + "@csstools/postcss-stepped-value-functions": "npm:^3.0.10" "@csstools/postcss-text-decoration-shorthand": "npm:^3.0.7" - "@csstools/postcss-trigonometric-functions": "npm:^3.0.9" + "@csstools/postcss-trigonometric-functions": "npm:^3.0.10" "@csstools/postcss-unset-value": "npm:^3.0.1" autoprefixer: "npm:^10.4.19" browserslist: "npm:^4.23.1" css-blank-pseudo: "npm:^6.0.2" css-has-pseudo: "npm:^6.0.5" css-prefers-color-scheme: "npm:^9.0.1" - cssdb: "npm:^8.0.0" + cssdb: "npm:^8.1.0" postcss-attribute-case-insensitive: "npm:^6.0.3" postcss-clamp: "npm:^4.1.0" - postcss-color-functional-notation: "npm:^6.0.12" + postcss-color-functional-notation: "npm:^6.0.14" postcss-color-hex-alpha: "npm:^9.0.4" postcss-color-rebeccapurple: "npm:^9.0.3" - postcss-custom-media: "npm:^10.0.7" - postcss-custom-properties: "npm:^13.3.11" - postcss-custom-selectors: "npm:^7.1.11" + postcss-custom-media: "npm:^10.0.8" + postcss-custom-properties: "npm:^13.3.12" + postcss-custom-selectors: "npm:^7.1.12" postcss-dir-pseudo-class: "npm:^8.0.1" - postcss-double-position-gradients: "npm:^5.0.6" + postcss-double-position-gradients: "npm:^5.0.7" postcss-focus-visible: "npm:^9.0.1" postcss-focus-within: "npm:^8.0.1" postcss-font-variant: "npm:^5.0.0" postcss-gap-properties: "npm:^5.0.1" postcss-image-set-function: "npm:^6.0.3" - postcss-lab-function: "npm:^6.0.17" + postcss-lab-function: "npm:^6.0.19" postcss-logical: "npm:^7.0.1" postcss-nesting: "npm:^12.1.5" postcss-opacity-percentage: "npm:^2.0.0" @@ -13905,7 +13920,7 @@ __metadata: postcss-selector-not: "npm:^7.0.2" peerDependencies: postcss: ^8.4 - checksum: 10c0/e2ee0b5d7dbaddb82ff6d51b5882120862d6be184973ae3d55642923183ab441d421d5f9810fe02e680a70dbc85b20b1c2eb02c68f167dcaf3ef80a71dd40e78 + checksum: 10c0/caa91ba4d3b897d43ab2669b3edf40b24ef32c88e23b113be8956412e64b28deed6ba229c331848fcbc0d143bfde155173fb1e1ada9ccae5037b2ee8f7e554b7 languageName: node linkType: hard From d41b43ed4fff8ccbee5eac62064c5def14f88973 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 8 Jul 2024 09:41:50 +0200 Subject: [PATCH 021/101] Limit attachments to `MEDIA_ATTACHMENTS_LIMIT` when returning posts through the API (#30932) --- app/models/status.rb | 2 +- app/models/status_edit.rb | 14 ++++++++------ spec/models/status_spec.rb | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/app/models/status.rb b/app/models/status.rb index baa6578005532e..73f0052673569c 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -288,7 +288,7 @@ def ordered_media_attachments else map = media_attachments.index_by(&:id) ordered_media_attachment_ids.filter_map { |media_attachment_id| map[media_attachment_id] } - end + end.take(MEDIA_ATTACHMENTS_LIMIT) end def replies_count diff --git a/app/models/status_edit.rb b/app/models/status_edit.rb index 089c42fb99493d..6e25a6f3bbdd6b 100644 --- a/app/models/status_edit.rb +++ b/app/models/status_edit.rb @@ -53,12 +53,14 @@ def emojis def ordered_media_attachments return @ordered_media_attachments if defined?(@ordered_media_attachments) - @ordered_media_attachments = if ordered_media_attachment_ids.nil? - [] - else - map = status.media_attachments.index_by(&:id) - ordered_media_attachment_ids.map.with_index { |media_attachment_id, index| PreservedMediaAttachment.new(media_attachment: map[media_attachment_id], description: media_descriptions[index]) } - end + @ordered_media_attachments = begin + if ordered_media_attachment_ids.nil? + [] + else + map = status.media_attachments.index_by(&:id) + ordered_media_attachment_ids.map.with_index { |media_attachment_id, index| PreservedMediaAttachment.new(media_attachment: map[media_attachment_id], description: media_descriptions[index]) } + end + end.take(Status::MEDIA_ATTACHMENTS_LIMIT) end def proper diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb index 271cf8690ecd7b..df67c365eb34a0 100644 --- a/spec/models/status_spec.rb +++ b/spec/models/status_spec.rb @@ -247,6 +247,41 @@ end end + describe '#ordered_media_attachments' do + let(:status) { Fabricate(:status) } + + let(:first_attachment) { Fabricate(:media_attachment) } + let(:second_attachment) { Fabricate(:media_attachment) } + let(:last_attachment) { Fabricate(:media_attachment) } + let(:extra_attachment) { Fabricate(:media_attachment) } + + before do + stub_const('Status::MEDIA_ATTACHMENTS_LIMIT', 3) + + # Add attachments out of order + status.media_attachments << second_attachment + status.media_attachments << last_attachment + status.media_attachments << extra_attachment + status.media_attachments << first_attachment + end + + context 'when ordered_media_attachment_ids is not set' do + it 'returns up to MEDIA_ATTACHMENTS_LIMIT attachments' do + expect(status.ordered_media_attachments.size).to eq Status::MEDIA_ATTACHMENTS_LIMIT + end + end + + context 'when ordered_media_attachment_ids is set' do + before do + status.update!(ordered_media_attachment_ids: [first_attachment.id, second_attachment.id, last_attachment.id, extra_attachment.id]) + end + + it 'returns up to MEDIA_ATTACHMENTS_LIMIT attachments in the expected order' do + expect(status.ordered_media_attachments).to eq [first_attachment, second_attachment, last_attachment] + end + end + end + describe '.mutes_map' do subject { described_class.mutes_map([status.conversation.id], account) } From 1a37862a7606058026b1e55b767f38a6f82da504 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 10:03:44 +0200 Subject: [PATCH 022/101] New Crowdin Translations (automated) (#30940) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/bg.json | 3 ++ app/javascript/mastodon/locales/ja.json | 2 + app/javascript/mastodon/locales/kab.json | 52 +++++++++++++++++++++++- app/javascript/mastodon/locales/lv.json | 5 ++- app/javascript/mastodon/locales/uk.json | 14 +++---- config/locales/activerecord.kab.yml | 28 +++++++++++++ config/locales/devise.kab.yml | 21 ++++++++++ config/locales/doorkeeper.kab.yml | 24 +++++++++++ config/locales/doorkeeper.ko.yml | 2 +- config/locales/ko.yml | 2 +- config/locales/ro.yml | 2 + config/locales/simple_form.kab.yml | 27 ++++++++++++ config/locales/simple_form.ro.yml | 18 ++++++++ 13 files changed, 187 insertions(+), 13 deletions(-) diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index 323890ba2efe8a..98e84c45d75351 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -35,7 +35,9 @@ "account.follow_back": "Последване взаимно", "account.followers": "Последователи", "account.followers.empty": "Още никой не следва потребителя.", + "account.followers_counter": "{count, plural, one {{counter} последовател} other {{counter} последователи}}", "account.following": "Последвано", + "account.following_counter": "{count, plural, one {{counter} последван} other {{counter} последвани}}", "account.follows.empty": "Потребителят още никого не следва.", "account.go_to_profile": "Към профила", "account.hide_reblogs": "Скриване на подсилвания от @{name}", @@ -61,6 +63,7 @@ "account.requested_follow": "{name} поиска да ви последва", "account.share": "Споделяне на профила на @{name}", "account.show_reblogs": "Показване на подсилвания от @{name}", + "account.statuses_counter": "{count, plural, one {{counter} публикация} other {{counter} публикации}}", "account.unblock": "Отблокиране на @{name}", "account.unblock_domain": "Отблокиране на домейн {domain}", "account.unblock_short": "Отблокиране", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 60788baff8c695..f3ba028eabde00 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -692,9 +692,11 @@ "server_banner.about_active_users": "過去30日間にこのサーバーを使用している人 (月間アクティブユーザー)", "server_banner.active_users": "人のアクティブユーザー", "server_banner.administered_by": "管理者", + "server_banner.is_one_of_many": "{domain} は、数々の独立したMastodonサーバーのうちのひとつです。サーバーに登録してFediverseのコミュニティに加わってみませんか。", "server_banner.server_stats": "サーバーの情報", "sign_in_banner.create_account": "アカウント作成", "sign_in_banner.follow_anyone": "連合内の誰でもフォローして投稿を時系列で見ることができます。アルゴリズム、広告、クリックベイトはありません。", + "sign_in_banner.mastodon_is": "Mastodonに参加して、世界で起きていることを見つけよう。", "sign_in_banner.sign_in": "ログイン", "sign_in_banner.sso_redirect": "ログインまたは登録", "status.admin_account": "@{name}さんのモデレーション画面を開く", diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json index de866cc1bc4c33..fcaa8f143ed445 100644 --- a/app/javascript/mastodon/locales/kab.json +++ b/app/javascript/mastodon/locales/kab.json @@ -1,6 +1,10 @@ { + "about.blocks": "Ulac agbur", "about.contact": "Anermis:", "about.disclaimer": "Mastodon d aseɣẓan ilelli, d aseɣẓan n uɣbalu yeldin, d tnezzut n Mastodon gGmbH.", + "about.domain_blocks.preamble": "Maṣṭudun s umata yeḍmen-ak ad teẓreḍ agbur, ad tesdemreḍ akked yimseqdacen-nniḍen seg yal aqeddac deg fedivers. Ha-tent-an ɣur-k tsuraf i yellan deg uqeddac-agi.", + "about.domain_blocks.silenced.title": "Ɣur-s talast", + "about.domain_blocks.suspended.title": "Yeḥbes", "about.not_available": "Talɣut-a ur tettwabder ara deg uqeddac-a.", "about.powered_by": "Azeṭṭa inmetti yettwasɣelsen sɣur {mastodon}", "about.rules": "Ilugan n uqeddac", @@ -166,6 +170,7 @@ "dismissable_banner.explore_tags": "D wiyi i d ihacṭagen i d-yettawin tamyigawt deg web anmetti ass-a. Ihacṭagen i sseqdacen ugar n medden, εlayit d imezwura.", "domain_block_modal.block": "Sewḥel aqeddac", "domain_block_modal.they_cant_follow": "Yiwen ur yezmir ad k·m-id-yeḍfer seg uqeddac-a.", + "domain_block_modal.title": "Sewḥel taɣult?", "domain_pill.activitypub_like_language": "ActivityPub am tutlayt yettmeslay Mastodon d izeḍwan inmettiyen nniḍen.", "domain_pill.server": "Aqeddac", "domain_pill.username": "Isem n useqdac", @@ -214,6 +219,7 @@ "filter_modal.added.review_and_configure_title": "Iɣewwaṛen n imzizdig", "filter_modal.added.settings_link": "asebter n yiɣewwaṛen", "filter_modal.added.short_explanation": "Tasuffeɣt-a tettwarna ɣer taggayt-a n yimsizdegen: {title}.", + "filter_modal.added.title": "Yettwarna umsizdeg!", "filter_modal.select_filter.expired": "yemmut", "filter_modal.select_filter.prompt_new": "Taggayt tamaynutt : {name}", "filter_modal.select_filter.search": "Nadi neɣ snulfu-d", @@ -224,9 +230,9 @@ "firehose.remote": "Iqeddacen nniḍen", "follow_request.authorize": "Ssireg", "follow_request.reject": "Agi", - "follow_suggestions.dismiss": "Ur ttɛawad ara ad t-id-sekneṭ", + "follow_suggestions.dismiss": "Dayen ur t-id-skan ara", "follow_suggestions.view_all": "Wali-ten akk", - "follow_suggestions.who_to_follow": "Menhu ara ḍefṛeḍ", + "follow_suggestions.who_to_follow": "Ad tḍefreḍ?", "followed_tags": "Ihacṭagen yettwaḍfaren", "footer.about": "Ɣef", "footer.directory": "Akaram n imeɣna", @@ -235,6 +241,7 @@ "footer.keyboard_shortcuts": "Inegzumen n unasiw", "footer.privacy_policy": "Tasertit tabaḍnit", "footer.source_code": "Wali tangalt taɣbalut", + "footer.status": "N tsuffeɣt", "generic.saved": "Yettwasekles", "getting_started.heading": "Bdu", "hashtag.column_header.tag_mode.all": "d {additional}", @@ -313,11 +320,14 @@ "lightbox.previous": "Ɣer deffir", "limited_account_hint.action": "Wali amaɣnu akken yebɣu yili", "link_preview.author": "S-ɣur {name}", + "link_preview.more_from_author": "Ugar sɣur {name}", + "link_preview.shares": "{count, plural, one {{counter} post} other {{counter} posts}}", "lists.account.add": "Rnu ɣer tebdart", "lists.account.remove": "Kkes seg tebdart", "lists.delete": "Kkes tabdart", "lists.edit": "Ẓreg tabdart", "lists.edit.submit": "Beddel azwel", + "lists.exclusive": "Ffer tisuffaɣ-a seg ugejdan", "lists.new.create": "Rnu tabdart", "lists.new.title_placeholder": "Azwel amaynut n tebdart", "lists.replies_policy.followed": "Kra n useqdac i yettwaḍefren", @@ -338,6 +348,7 @@ "navigation_bar.bookmarks": "Ticraḍ", "navigation_bar.community_timeline": "Tasuddemt tadigant", "navigation_bar.compose": "Aru tajewwiqt tamaynut", + "navigation_bar.direct": "Tibdarin tusligin", "navigation_bar.discover": "Ẓer", "navigation_bar.domain_blocks": "Tiɣula yeffren", "navigation_bar.explore": "Snirem", @@ -357,9 +368,14 @@ "navigation_bar.search": "Nadi", "navigation_bar.security": "Taɣellist", "not_signed_in_indicator.not_signed_in": "You need to sign in to access this resource.", + "notification.admin.report": "Yemla-t-id {name} {target}", + "notification.admin.sign_up": "Ijerred {name}", + "notification.favourite": "{name} yesmenyaf addad-ik·im", "notification.follow": "iṭṭafar-ik·em-id {name}", "notification.follow_request": "{name} yessuter-d ad k·m-yeḍfeṛ", "notification.mention": "{name} yebder-ik-id", + "notification.moderation-warning.learn_more": "Issin ugar", + "notification.moderation_warning.action_suspend": "Yettwaseḥbes umiḍan-ik.", "notification.own_poll": "Tafrant-ik·im tfuk", "notification.poll": "Tfukk tefrant ideg tettekkaḍ", "notification.reblog": "{name} yebḍa tajewwiqt-ik i tikelt-nniḍen", @@ -370,6 +386,7 @@ "notification_requests.notifications_from": "Ilɣa sɣur {name}", "notifications.clear": "Sfeḍ tilɣa", "notifications.clear_confirmation": "Tebɣiḍ s tidet ad tekkseḍ akk tilɣa-inek·em i lebda?", + "notifications.column_settings.admin.report": "Ineqqisen imaynuten:", "notifications.column_settings.alert": "Tilɣa n tnarit", "notifications.column_settings.favourite": "Imenyafen:", "notifications.column_settings.filter_bar.advanced": "Sken-d akk taggayin", @@ -384,6 +401,7 @@ "notifications.column_settings.sound": "Rmed imesli", "notifications.column_settings.status": "Tisuffaɣ timaynutin :", "notifications.column_settings.unread_notifications.category": "Ilɣa ur nettwaɣra", + "notifications.column_settings.update": "Iẓreg:", "notifications.filter.all": "Akk", "notifications.filter.boosts": "Seǧhed", "notifications.filter.favourites": "Imenyafen", @@ -413,6 +431,7 @@ "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": "Ttwassnen deg Mastodon", "onboarding.profile.display_name": "Isem ara d-yettwaskanen", + "onboarding.profile.display_name_hint": "Isem-ik·im ummid neɣ isem-ik·im n uqeṣṣer…", "onboarding.profile.note": "Tameddurt", "onboarding.profile.note_hint": "Tzemreḍ ad d-@tbedreḍ imdanen niḍen neɣ #ihacṭagen …", "onboarding.profile.save_and_continue": "Sekles, tkemmleḍ", @@ -441,6 +460,7 @@ "poll.total_votes": "{count, plural, one {# n udɣaṛ} other {# n yedɣaṛen}}", "poll.vote": "Dɣeṛ", "poll.voted": "Tdeɣṛeḍ ɣef tririt-ayi", + "poll.votes": "{votes, plural, one {# n udɣaṛ} other {# n yedɣaṛen}}", "poll_button.add_poll": "Rnu asenqed", "poll_button.remove_poll": "Kkes asenqed", "privacy.change": "Seggem tabaḍnit n yizen", @@ -465,9 +485,12 @@ "relative_time.seconds": "{number}tas", "relative_time.today": "assa", "reply_indicator.cancel": "Sefsex", + "reply_indicator.poll": "Afmiḍi", "report.block": "Sewḥel", + "report.categories.legal": "Azerfan", "report.categories.other": "Tiyyaḍ", "report.categories.spam": "Aspam", + "report.category.subtitle": "Fren amṣada akk ufrin", "report.category.title_account": "ameɣnu", "report.category.title_status": "tasuffeɣt", "report.close": "Immed", @@ -476,13 +499,25 @@ "report.next": "Uḍfiṛ", "report.placeholder": "Iwenniten-nniḍen", "report.reasons.dislike": "Ur t-ḥemmleɣ ara", + "report.reasons.dislike_description": "D ayen akk ur bɣiɣ ara ad waliɣ", "report.reasons.other": "D ayen nniḍen", + "report.reasons.other_description": "Ugur ur yemṣada ara akk d taggayin-nniḍen", "report.reasons.spam": "D aspam", + "report.reasons.spam_description": "Yir iseɣwan, yir agman d tririyin i d-yettuɣalen", + "report.reasons.violation": "Truẓi n yilugan n uqeddac", + "report.reasons.violation_description": "Teẓriḍ y·tettruẓu kra n yilugan", + "report.rules.subtitle": "Fren ayen akk yemṣadan", + "report.rules.title": "Acu n yilugan i yettwarẓan?", + "report.statuses.subtitle": "Fren ayen akk yemṣadan", + "report.statuses.title": "Llant tsuffaɣ ara isdemren aneqqis-a?", "report.submit": "Azen", "report.target": "Mmel {target}", + "report.thanks.take_action_actionable": "Ideg nekkni nessenqad tuttra-inek•inem, tzemreḍ ad tḥadreḍ mgal @{name}:", "report.thanks.title": "Ur tebɣiḍ ara ad twaliḍ aya?", + "report.thanks.title_actionable": "Tanemmirt ɣef uneqqis, ad nwali deg waya.", "report.unfollow": "Seḥbes aḍfar n @{name}", "report_notification.attached_statuses": "{count, plural, one {# post} other {# posts}} attached", + "report_notification.categories.legal": "Azerfan", "report_notification.categories.other": "Ayen nniḍen", "report_notification.categories.spam": "Aspam", "report_notification.open": "Ldi aneqqis", @@ -497,6 +532,7 @@ "search_popout.full_text_search_disabled_message": "Ur yelli ara deg {domain}.", "search_popout.language_code": "Tangalt ISO n tutlayt", "search_popout.options": "Iwellihen n unadi", + "search_popout.quick_actions": "Tigawin tiruradin", "search_popout.recent": "Inadiyen ineggura", "search_popout.user": "amseqdac", "search_results.accounts": "Imeɣna", @@ -505,7 +541,9 @@ "search_results.see_all": "Wali-ten akk", "search_results.statuses": "Tisuffaɣ", "search_results.title": "Anadi ɣef {q}", + "server_banner.active_users": "iseqdacen urmiden", "server_banner.administered_by": "Yettwadbel sɣur :", + "server_banner.server_stats": "Tidaddanin n uqeddac:", "sign_in_banner.create_account": "Snulfu-d amiḍan", "sign_in_banner.sign_in": "Qqen", "sign_in_banner.sso_redirect": "Qqen neɣ jerred", @@ -516,13 +554,20 @@ "status.cannot_reblog": "Tasuffeɣt-a ur tezmir ara ad tettwabḍu tikelt-nniḍen", "status.copy": "Nɣel assaɣ ɣer tasuffeɣt", "status.delete": "Kkes", + "status.direct": "Bder-d @{name} weḥd-s", + "status.direct_indicator": "Abdar uslig", "status.edit": "Ẓreg", "status.edited_x_times": "Tettwaẓreg {count, plural, one {{count} n tikkelt} other {{count} n tikkal}}", "status.embed": "Seddu", + "status.favourite": "Amenyaf", "status.filter": "Sizdeg tassufeɣt-a", "status.filtered": "Yettwasizdeg", "status.hide": "Ffer tasuffeɣt", + "status.history.created": "Yerna-t {name} {date}", + "status.history.edited": "Ibeddel-it {name} {date}", "status.load_more": "Sali ugar", + "status.media.open": "Sit i ulday", + "status.media.show": "Sit i uskan", "status.media_hidden": "Amidya yettwaffer", "status.mention": "Bder-d @{name}", "status.more": "Ugar", @@ -548,6 +593,7 @@ "status.show_less_all": "Semẓi akk tisuffɣin", "status.show_more": "Ssken-d ugar", "status.show_more_all": "Ẓerr ugar lebda", + "status.show_original": "Sken aɣbalu", "status.title.with_attachments": "{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}", "status.translate": "Suqel", "status.translated_from_with": "Yettwasuqel seg {lang} s {provider}", @@ -582,6 +628,7 @@ "upload_form.video_description": "Glem-d i yemdanen i yesɛan ugur deg tmesliwt neɣ deg yiẓri", "upload_modal.analyzing_picture": "Tasleḍt n tugna tetteddu…", "upload_modal.apply": "Snes", + "upload_modal.applying": "Asnas…", "upload_modal.choose_image": "Fren tugna", "upload_modal.description_placeholder": "Aberraɣ arurad ineggez nnig n uqjun amuṭṭis", "upload_modal.detect_text": "Sefru-d aḍris seg tugna", @@ -589,6 +636,7 @@ "upload_modal.preparing_ocr": "Aheyyi n OCR…", "upload_modal.preview_label": "Taskant ({ratio})", "upload_progress.label": "Asali iteddu...", + "upload_progress.processing": "Asesfer…", "username.taken": "Yettwaṭṭef yisem-a n useqdac. Ɛreḍ wayeḍ", "video.close": "Mdel tabidyutt", "video.download": "Sidered afaylu", diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json index 041072c6adecef..701569fa05ce1c 100644 --- a/app/javascript/mastodon/locales/lv.json +++ b/app/javascript/mastodon/locales/lv.json @@ -35,6 +35,7 @@ "account.follow_back": "Sekot atpakaļ", "account.followers": "Sekotāji", "account.followers.empty": "Šim lietotājam vēl nav sekotāju.", + "account.followers_counter": "{count, plural, zero {{count} sekotāju} one {{count} sekotājs} other {{count} sekotāji}}", "account.following": "Seko", "account.follows.empty": "Šis lietotājs pagaidām nevienam neseko.", "account.go_to_profile": "Doties uz profilu", @@ -312,9 +313,9 @@ "home.column_settings.show_reblogs": "Rādīt pastiprinātos ierakstus", "home.column_settings.show_replies": "Rādīt atbildes", "home.hide_announcements": "Slēpt paziņojumus", - "home.pending_critical_update.body": "Lūdzu, pēc iespējas ātrāk atjaunini savu Mastodon serveri!", + "home.pending_critical_update.body": "Lūgums pēc iespējas drīzāk atjaunināt savu Mastodon serveri.", "home.pending_critical_update.link": "Skatīt jauninājumus", - "home.pending_critical_update.title": "Pieejams kritisks drošības jauninājums!", + "home.pending_critical_update.title": "Ir pieejams būtisks drošības atjauninājums.", "home.show_announcements": "Rādīt paziņojumus", "interaction_modal.description.favourite": "Ar Mastodon kontu tu vari pievienot šo ziņu izlasei, lai informētu autoru, ka to novērtē, un saglabātu to vēlākai lasīšanai.", "interaction_modal.description.follow": "Ar Mastodon kontu Tu vari sekot {name}, lai saņemtu lietotāja ierakstus savā mājas plūsmā.", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index 5d7d040fd9bfa9..67ebb031aefcd0 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -217,18 +217,18 @@ "domain_block_modal.title": "Заблокувати домен?", "domain_block_modal.you_will_lose_followers": "Усіх ваших підписників з цього сервера буде вилучено.", "domain_block_modal.you_wont_see_posts": "Ви не бачитимете дописів і сповіщень від користувачів на цьому сервері.", - "domain_pill.activitypub_lets_connect": "Це дозволяє вам спілкуватися та взаємодіяти з людьми не лише на Mastodon, але й у різних соціальних додатках.", - "domain_pill.activitypub_like_language": "ActivityPub - це як мова, якою Мастодонт розмовляє з іншими соціальними мережами.", + "domain_pill.activitypub_lets_connect": "Це дозволяє вам спілкуватися та взаємодіяти з людьми не лише на Mastodon, але й у різних соціальних застосунках.", + "domain_pill.activitypub_like_language": "ActivityPub - це як мова, якою Mastodon розмовляє з іншими соціальними мережами.", "domain_pill.server": "Сервер", "domain_pill.their_handle": "Їхня адреса:", - "domain_pill.their_server": "Їхній цифровий дім, де живуть усі їхні пости.", + "domain_pill.their_server": "Їхній цифровий дім, де живуть усі їхні дописи.", "domain_pill.their_username": "Їхній унікальний ідентифікатор на їхньому сервері. Ви можете знайти користувачів з однаковими іменами на різних серверах.", "domain_pill.username": "Ім'я користувача", "domain_pill.whats_in_a_handle": "Що є в адресі?", "domain_pill.who_they_are": "Оскільки дескриптори вказують, хто це і де він знаходиться, ви можете взаємодіяти з людьми через соціальну мережу платформ на основі .", "domain_pill.who_you_are": "Оскільки ваш нікнейм вказує, хто ви та де ви, люди можуть взаємодіяти з вами через соціальну мережу платформ на основі .", "domain_pill.your_handle": "Ваша адреса:", - "domain_pill.your_server": "Ваш цифровий дім, де живуть усі ваші публікації. Не подобається цей? Перенесіть сервери в будь-який час і залучайте своїх підписників.", + "domain_pill.your_server": "Ваш цифровий дім, де живуть усі ваші дописи. Не подобається цей? Перенесіть сервери в будь-який час і залучайте своїх підписників.", "domain_pill.your_username": "Ваш унікальний ідентифікатор на цьому сервері. Ви можете знайти користувачів з однаковими іменами на різних серверах.", "embed.instructions": "Вбудуйте цей допис до вашого вебсайту, скопіювавши код нижче.", "embed.preview": "Ось який вигляд це матиме:", @@ -489,9 +489,9 @@ "notification.reblog": "{name} поширює ваш допис", "notification.relationships_severance_event": "Втрачено з'єднання з {name}", "notification.relationships_severance_event.account_suspension": "Адміністратор з {from} призупинив {target}, що означає, що ви більше не можете отримувати оновлення від них або взаємодіяти з ними.", - "notification.relationships_severance_event.domain_block": "Адміністратор з {from} заблокував {target}, включаючи {followersCount} ваших підписників і {{followingCount, plural, one {# account} other {# accounts}}, на які ви підписані.", - "notification.relationships_severance_event.learn_more": "Дізнатися більше", - "notification.relationships_severance_event.user_domain_block": "Ви заблокували {target}, видаливши {followersCount} ваших підписників і {followingCount, plural, one {# account} other {# accounts}}, за якими ви стежите.", + "notification.relationships_severance_event.domain_block": "Адміністратор з {from} заблокував {target}, включаючи {followersCount} ваших підписників і {followingCount , plural, one {# обліковий запис} few {# облікові записи} many {# облікових записів} other {# обліковий запис}}, на які ви підписані.", + "notification.relationships_severance_event.learn_more": "Докладніше", + "notification.relationships_severance_event.user_domain_block": "Ви заблокували {target}, видаливши {followersCount} ваших підписників і {followingCount, plural, one {# обліковий запис} few {# облікові записи} many {# облікових записів} other {# обліковий запис}}, за якими ви стежите.", "notification.status": "{name} щойно дописує", "notification.update": "{name} змінює допис", "notification_requests.accept": "Прийняти", diff --git a/config/locales/activerecord.kab.yml b/config/locales/activerecord.kab.yml index b3ca90069b50ed..8cdc6501cbb494 100644 --- a/config/locales/activerecord.kab.yml +++ b/config/locales/activerecord.kab.yml @@ -21,6 +21,18 @@ kab: username: invalid: ilaq ad ilin isekkilen, uṭṭunen d yijerriden n wadda kan reserved: yettwaṭṭef + admin/webhook: + attributes: + url: + invalid: mačči d URL ameɣtu + doorkeeper/application: + attributes: + website: + invalid: mačči d URL ameɣtu + import: + attributes: + data: + malformed: yir amsal status: attributes: reblog: @@ -28,4 +40,20 @@ kab: user: attributes: email: + blocked: isseqdac asaǧǧaw n yimayl ur yettusirgen ara unreachable: ur d-ttban ara d akken yella + role_id: + elevated: ur yezmir ara ad iεeddi tamlilt-ik tamirant + user_role: + attributes: + permissions_as_keys: + dangerous: deg-s tisirag tiriɣelsanin i temlilt tazadurt + elevated: ur yezmir ara ad yesεu tirirag ur nelli ara deg temlilit-ik tamirant + own_role: ur yezmir ara ad yettwabeddel s temlilt-ik tamirant + position: + elevated: ur yezmir ara ad iεeddi tamlilt-ik tamirant + own_role: ur yezmir ara ad yettwabeddel s temlilt-ik tamirant + webhook: + attributes: + events: + invalid_permissions: ur yezmir ara ad yesεu tidyanin iwumi ur tesεiḍ ara tisirag diff --git a/config/locales/devise.kab.yml b/config/locales/devise.kab.yml index 438c1df2b9a46f..3fbc58c1f57fcb 100644 --- a/config/locales/devise.kab.yml +++ b/config/locales/devise.kab.yml @@ -12,6 +12,7 @@ kab: last_attempt: Γur-k yiwen n uɛraḍ-nniḍen kan send ad yettucekkel umiḍan-ik. locked: Amiḍan-ik yettwargel. not_found_in_database: Tella tuccḍa deg %{authentication_keys} neγ deg wawal uffir. + omniauth_user_creation_failure: Tuccḍa lawan n tmerna n umiḍan i timagit-a. pending: Amiḍan-inek mazal-it deg ɛiwed n tmuγli. timeout: Tiɣimit n tuqqna tezri. Ma ulac aɣilif ɛiwed tuqqna akken ad tkemmleḍ. unauthenticated: Ilaq ad teqqneḍ neɣ ad tjerrḍeḍ akken ad tkemmelḍ. @@ -47,21 +48,41 @@ kab: subject: 'Mastodon: Iwellihen n uwennez n wawal uffir' title: Aɛiwed n wawal uffir two_factor_disabled: + explanation: Tuqqna tella tura s useqdec n tansa n yimayl tasuft d wawal n uεeddi. subject: 'Mastodon: Asesteb s snat n tarrayin yensa' + subtitle: Asesteb s snat tarrayin i umiḍan-ik yensan. title: Asesteb s snat n tarrayin insa two_factor_enabled: + explanation: Ajuṭu yettusirwen s usnas TOTP yeqqnen ilaq i wakken ad teqqneḍ. subject: 'Mastodon: Asesteb s snat n tarrayin yermed' + subtitle: Asesteb s snat tarrayin yettwarmed i umiḍan-ik. title: Asesteb s snat n tarrayin irmed two_factor_recovery_codes_changed: explanation: Tangalt n tuɣalin tettwaḥbes sakin nesnulfa-d yiwet d tamaynut. subject: 'Mastodon: Tingalin n tuɣalin n snat n tarayin ttwarnanat i tikkelt-nniḍen' + subtitle: Tangalt n tuɣalin tettwaḥbes sakin nesnulfa-d yiwet d tamaynut. title: Tangalt n tuɣalin 2FA tettwabeddel unlock_instructions: subject: 'Mastodon: iwelihhen n userreḥ' webauthn_credential: added: + explanation: Tasarut-a n tɣellist tettwarna ɣer umiḍan-ik·im subject: 'Maṣṭudun : Tasarutt tamaynutt n tɣellist' title: Tasarut tamaynutt n tɣellist tamaynut tettwarna + deleted: + explanation: Tasarut-a n tɣellist tettwakkes seg umiḍan-ik·im + subject: 'Mastodon: Tasarut n tɣellsit tettwakkes' + title: Yiwet seg tsura-k·m n tɣellist tettwakkes + webauthn_disabled: + explanation: Yensa usesteb s tsura n tɣellist i umiḍan-ik. + extra: Tzemreḍ ad tkecmeḍ tura s useqdec asuf n ujuṭu yettwasran s usnas TOPTP yeqqnen. + subject: 'Mastodon: Asesteb s tsura n tɣellist yensa' + title: Tisura n tɣellist nsant + webauthn_enabled: + explanation: Asesteb n tsarut n tɣellist tettwarmed i umiḍan-ik. + extra: Tasarut-ik n tɣellist tezmer tura ad tettuseqdec i unekcum. + subject: 'Mastodon: Asesteb n tsarut n tɣellist yermed' + title: Tisura n tɣellist remdent omniauth_callbacks: failure: Ur nezmir ara ad ak·akem-nsesṭeb seg %{kind} acku "%{reason}". success: Asesṭeb idda akken iwata seg umiḍan %{kind}. diff --git a/config/locales/doorkeeper.kab.yml b/config/locales/doorkeeper.kab.yml index 1b1a7df957645f..33aecd8c6f03d7 100644 --- a/config/locales/doorkeeper.kab.yml +++ b/config/locales/doorkeeper.kab.yml @@ -5,6 +5,7 @@ kab: doorkeeper/application: name: Isem n usnas redirect_uri: URI n uwelleh + scopes: Tinerfadin website: Asmel web n usnas errors: models: @@ -39,6 +40,7 @@ kab: empty: Ulac ɣur-k·m isnasen. name: Isem new: Asnas amaynut + scopes: Tinerfadin show: Ẓer title: Isnasen-ik·im new: @@ -47,6 +49,8 @@ kab: actions: Tigawin application_id: ID n usnas callback_urls: URL n tririt n wawal + scopes: Tinerfadin + secret: Tuffirt n umsaɣ title: 'Asnas: %{name}' authorizations: buttons: @@ -55,6 +59,7 @@ kab: error: title: Tella-d tuccḍa new: + review_permissions: Asenqed n tsirag title: Tlaq tsiregt show: title: Nɣel tangalt n wurag sakkin senteḍ-itt deg usnas. @@ -64,8 +69,12 @@ kab: confirmations: revoke: Tetḥeqqeḍ? index: + authorized_at: Yettwasireg ɣef %{date} description_html: Ha-t-an yisnasen i izemren ad kecmen ɣer umiḍan-ik·im, s useqdec n API. Ma llan yisnasen ur teεqileḍ ara da, neɣ kra n wesnas ur iteddu ara akken ilaq, tzemreḍ ad tekkseḍ anekcum-is. last_used_at: Yettwaseqdec i tikkelt taneggarut ass n %{date} + never_used: Urǧin yettwaseqdac + scopes: Tisirag + superapp: Adigan title: Isnasen-ik·im yettusirgen errors: messages: @@ -82,13 +91,28 @@ kab: destroy: notice: Yettwaḥwi wesnas. grouped_scopes: + access: + read: Anekcum i tɣuri kan + read/write: Anekcum i tɣuri d tira + write: Anekcum i tira kan title: accounts: Imiḍanen admin/accounts: Tadbelt n imiḍan + admin/all: Akk timahilin tinebdalin + admin/reports: Tadbelt n yineqqisen + blocks: Yewḥel + bookmarks: Ticraḍ + conversations: Idiwenniyen crypto: Awgelhen seg yixef ɣer yixef + favourites: Imenyafen filters: Imzizdigen + follow: Aḍfar, asgugem akked usewḥel + follows: Aḍfar lists: Tibdarin + media: Imeddayen n umidya + mutes: Yeggugem notifications: Tilɣa + profile: Amaɣnu-k Mastodon push: Tilɣa yettudemmren reports: Ineqqisen search: Nadi diff --git a/config/locales/doorkeeper.ko.yml b/config/locales/doorkeeper.ko.yml index 3ab0698d51c4ef..7ec357bf9fdec4 100644 --- a/config/locales/doorkeeper.ko.yml +++ b/config/locales/doorkeeper.ko.yml @@ -31,7 +31,7 @@ ko: form: error: 이런! 오류를 확인하세요 help: - native_redirect_uri: "%{native_redirect_uri}에서 로컬 테스트를 할 수 있습니다." + native_redirect_uri: "%{native_redirect_uri}를 이용해 로컬 테스트를 할 수 있습니다" redirect_uri: 한 줄에 하나의 URI를 작성하세요 scopes: 스페이스로 범위를 구분하세요. 빈 칸으로 놔두면 기본 범위를 사용합니다. index: diff --git a/config/locales/ko.yml b/config/locales/ko.yml index a8e2ade61df8e5..f7feb73ba0b03d 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -1741,7 +1741,7 @@ ko: contrast: 마스토돈 (고대비) default: 마스토돈 (어두움) mastodon-light: 마스토돈 (밝음) - system: 자동 선택 (시스템 테마 이용) + system: 자동 (시스템 테마 사용) time: formats: default: "%Y-%m-%d %H:%M" diff --git a/config/locales/ro.yml b/config/locales/ro.yml index cd54d51051d838..79bc2a275f4045 100644 --- a/config/locales/ro.yml +++ b/config/locales/ro.yml @@ -116,6 +116,8 @@ ro: redownloaded_msg: S-a reîmprospătat cu succes profilul %{username} de la origine reject: Respinge rejected_msg: S-a respins cu succes cererea de înregistrare a utilizatorului %{username} + remote_suspension_irreversible: Datele acestui cont au fost șterse în mod ireversibil. + remote_suspension_reversible_hint_html: Contul a fost suspendat pe server-ul respectiv, iar datele vor fi șterse complet pe %{date}. Până atunci, server-ul remote poate restabili acest cont fără consecințe negative. Dacă dorești să elimini toate datele contului numaidecât, poți face acest lucru mai jos. remove_avatar: Elimină avatar remove_header: Elimină antet removed_avatar_msg: S-a îndepărtat cu succes poza de profil a utilizatorului %{username} diff --git a/config/locales/simple_form.kab.yml b/config/locales/simple_form.kab.yml index 9461f16cd54577..63e2b9aacf1bf6 100644 --- a/config/locales/simple_form.kab.yml +++ b/config/locales/simple_form.kab.yml @@ -5,6 +5,7 @@ kab: account: display_name: Isem-ik·im ummid neɣ isem-ik·im n uqeṣṣer. fields: Asebter-ik·im agejdan, imqimen, leεmer, ayen tebɣiḍ. + note: 'Tzemreḍ ad d-@tbedreḍ imdanen niḍen neɣ #ihacṭagen.' account_alias: acct: Sekcem isem n umseqdac@domain n umiḍan s wansa itebγiḍ ad gujjeḍ account_migration: @@ -27,6 +28,8 @@ kab: name: 'Ha-t-an kra seg ihacṭagen i tesseqdaceḍ ussan-a ineggura maḍi :' imports: data: Afaylu CSV id yusan seg uqeddac-nniḍen n Maṣṭudun + invite_request: + text: Aya ad aɣ-iɛiwen ad nessenqed tuttra-k•m ip_block: comment: D afrayan. Cfu ɣef wayɣer i terniḍ alugen-a. severities: @@ -36,6 +39,8 @@ kab: fields: name: Tabzimt value: Agbur + account_alias: + acct: Tansa n umiḍan aqbur account_migration: acct: Tansa n umiḍan amaynut account_warning_preset: @@ -51,6 +56,7 @@ kab: suspend: Ḥbes di leεḍil announcement: ends_at: Tagara n tedyant + starts_at: Tazwara n tedyant text: Alɣu defaults: autofollow: Ɛreḍ-it-id ad yeḍfer amiḍan-ik·im @@ -59,18 +65,25 @@ kab: chosen_languages: Sizdeg tutlayin confirm_new_password: Sentem awal uffir amaynut confirm_password: Sentem awal uffir + context: Isatalen n umsizdeg current_password: Awal uffir n tura data: Isefka display_name: Isem ara d-yettwaskanen email: Tansa imayl expires_in: Ad yemmet + fields: Urtiyen niḍen header: Ixef + honeypot: "%{label} (ur tettaččar ara)" + inbox_url: URL n tbewwaḍt n urmas yettwacudden + irreversible: Kkes deg wadeg n tuffra locale: Tutlayt n wegrudem max_uses: Amḍan afellay n iseqdacen new_password: Awal uffir amaynut note: Tameddurt otp_attempt: Tangalt n snat n tarayin password: Awal uffir + phrase: Awal n tsarut neɣ tafyirt + setting_advanced_layout: Rmed agrudem n web leqqayen setting_default_language: Tutlayt n tira setting_default_privacy: Tabaḍnit n tira setting_display_media_default: Akk-a kan @@ -88,8 +101,15 @@ kab: featured_tag: name: Ahacṭag form_admin_settings: + custom_css: CSS udmawan + profile_directory: Rmed akaram n imaγnuten + site_contact_email: Imayl n unermas + site_short_description: Aglam n uqeddac site_terms: Tasertit tabaḍnit site_title: Isem n uqeddac + status_page_url: URL n uusebter n waddaden + theme: Asentel amezwer + thumbnail: Tanfult n uqeddac interactions: must_be_follower: Ssewḥel ilɣa sɣur wid akked tid ur yellin ara d imeḍfaren-ik·im must_be_following: Ssewḥel ilɣa sɣur wid akked tid ur tettḍafareḍ ara @@ -109,18 +129,25 @@ kab: follow: Yeḍfer-ik·im-id walbɛaḍ follow_request: Ma yella win i d-yessutren ad k·em-yeḍfer mention: Yuder-ik·em-id walbɛaḍ + pending_account: Amiḍan amaynut yesran asenqed reblog: Yella win yesselhan adda-dik·im + report: Aneqis amaynut yettwazen rule: hint: Isallen-nniḍen text: Alugen tag: name: Ahacṭag user: + role: Tamlilt time_zone: Tamnaḍt tasragant user_role: name: Isem permissions_as_keys: Tisirag + webhook: + events: Tidyanin turmidin 'no': Ala + not_recommended: Ur yettuwelleh ara + overridden: Yeččur recommended: Yettuwelleh required: mark: "*" diff --git a/config/locales/simple_form.ro.yml b/config/locales/simple_form.ro.yml index dfb44c77453e56..5df411b9512d3e 100644 --- a/config/locales/simple_form.ro.yml +++ b/config/locales/simple_form.ro.yml @@ -2,6 +2,12 @@ ro: simple_form: hints: + account: + discoverable: Este posibil ca postările și profilul tău să fie recomandate în diferite zone ale Mastodon, iar profilul tău ar poate fi sugerat altor utilizatori. + fields: Pagina ta principală, pronumele tale, vârsta, sau orice îți dorești. + indexable: Postările tale publice pot apărea în rezultatele căutărilor pe Mastodon. Persoanele care au interacționat cu postările tale vor putea să le caute oricând. + note: 'Poți @menționa alte persoane sau #hashtag-uri.' + unlocked: Alte persoane vă vor putea urmări fără a solicita aprobare. Debifați dacă doriți să revizuiți cererile și să alegeți dacă doriți să acceptați sau să respingeți noii urmăritori. account_alias: acct: Specificați numele de utilizator@domeniu al contului de la care doriți să treceți account_migration: @@ -23,12 +29,14 @@ ro: text: Poți folosi sintaxa de postare. Te rugăm să fii atent la spațiul pe care anunțul îl va ocupa pe ecranul utilizatorului defaults: autofollow: Persoanele care se înregistrează datorită invitației tale te vor urmări automat + avatar: WEBP, PNG, GIF sau JPG. Cel mult %{size}. Va fi redimensionată la %{dimensions}px bot: Acest cont performează în cea mai mare parte acțiuni automate și nu poate fi monitorizat context: Contextele în care filtrul trebuie aplicat current_password: În scopuri de securitate, vă rugăm să introduceţi parola contului curent current_username: Pentru a confirma, vă rugăm să introduceţi numele de utilizator al contului curent digest: Este trimis doar după o lungă perioadă de inactivitate și numai dacă primești mesaje personale în perioada de absență email: Vei primi un e-mail de confirmare + header: WEBP, PNG, GIF sau JPG. Cel mult %{size}. Va fi redimensionată la %{dimensions}px inbox_url: Copiază adresa URL de pe prima pagină a reului pe care vrei să îl utilizezi irreversible: Postările sortate vor dispărea ireversibil, chiar dacă filtrul este ulterior șters locale: Limba interfaței de utilizator, e-mailurile si notificările push @@ -36,17 +44,27 @@ ro: phrase: Vor fi potrivite indiferent de textul din casetă sau advertismentul unei postări scopes: La care API-uri aplicația are nevoie de acces. Dacă selectezi un scop principal nu mai e nevoie să selectezi fiecare sub-scop al acestuia. setting_aggregate_reblogs: Nu afișa impulsurile noi pentru postările care au fost deja recent impulsionate (afectează doar noile impulsuri primite) + setting_always_send_emails: În mod normal, notificările prin e-mail nu vor fi trimise când utilizați în mod activ Mastodon setting_default_sensitive: Fișierele media sensibile sunt ascunse implicit și pot fi dezvăluite cu un clic setting_display_media_default: Ascunde conținutul media marcat ca sensibil (NSFW) setting_display_media_hide_all: Întotdeauna ascunde tot conținutul media setting_display_media_show_all: Întotdeauna afișează conținutul media marcat ca sensibil setting_use_blurhash: Gradienții sunt bazați pe culorile vizualelor ascunse, dar ofuscă orice detalii setting_use_pending_items: Ascunde actualizările cronologice din spatele unui click în loc de a derula automat fluxul + username: Poți folosi litere, numere sau liniuțe de subliniere whole_word: Când fraza sau cuvântul este doar alfanumeric, acesta se aplică doar dacă există o potrivire completă domain_allow: domain: Acest domeniu va putea prelua date de pe acest server și datele primite de la el vor fi procesate și stocate email_domain_block: + domain: Acesta poate fi numele de domeniu care apare în adresa de e-mail sau în înregistrarea MX pe care o utilizează. Acestea vor fi verificate la înscriere. with_dns_records: Se va face o încercare de a rezolva înregistrările DNS ale domeniului dat și rezultatele vor fi de asemenea afișate pe lista neagră + featured_tag: + name: 'Iată câteva dintre hashtag-urile pe care le-ai folosit cel mai recent:' + filters: + action: Alege ce acţiune va fi efectuată atunci când o postare corespunde filtrului + actions: + hide: Ascunde complet conținutul filtrat, ca și cum nu ar exista + warn: Ascunde conținutul filtrat în spatele unui avertisment care menționează titlul filtrului form_challenge: current_password: Ați intrat într-o zonă securizată imports: From 36d819bef34279bb36cdfa3ed89711405ad79591 Mon Sep 17 00:00:00 2001 From: Tianwei Dong Date: Mon, 8 Jul 2024 09:10:57 +0100 Subject: [PATCH 023/101] Change to use an unified constant for max media attachments per status (#29073) Co-authored-by: Renaud Chaput --- app/javascript/mastodon/actions/compose.js | 4 ++-- app/javascript/mastodon/components/media_gallery.jsx | 4 ++-- .../features/compose/containers/upload_button_container.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index b07dff182a8bb4..0bcba7d57fe78b 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -279,7 +279,7 @@ export function submitComposeFail(error) { export function uploadCompose(files) { return function (dispatch, getState) { - const uploadLimit = 4; + const uploadLimit = getState().getIn(['server', 'server', 'configuration', 'statuses', 'max_media_attachments']); const media = getState().getIn(['compose', 'media_attachments']); const pending = getState().getIn(['compose', 'pending_media_attachments']); const progress = new Array(files.length).fill(0); @@ -299,7 +299,7 @@ export function uploadCompose(files) { dispatch(uploadComposeRequest()); for (const [i, file] of Array.from(files).entries()) { - if (media.size + i > 3) break; + if (media.size + i > (uploadLimit - 1)) break; const data = new FormData(); data.append('file', file); diff --git a/app/javascript/mastodon/components/media_gallery.jsx b/app/javascript/mastodon/components/media_gallery.jsx index 91459a1285c0d1..ed4805b05d50f5 100644 --- a/app/javascript/mastodon/components/media_gallery.jsx +++ b/app/javascript/mastodon/components/media_gallery.jsx @@ -305,13 +305,13 @@ class MediaGallery extends PureComponent { style.aspectRatio = '3 / 2'; } - const size = media.take(4).size; + const size = media.size; const uncached = media.every(attachment => attachment.get('type') === 'unknown'); if (this.isFullSizeEligible()) { children = ; } else { - children = media.take(4).map((attachment, i) => ); + children = media.map((attachment, i) => ); } if (uncached) { diff --git a/app/javascript/mastodon/features/compose/containers/upload_button_container.js b/app/javascript/mastodon/features/compose/containers/upload_button_container.js index 066e1853468680..7cdc12663d70ca 100644 --- a/app/javascript/mastodon/features/compose/containers/upload_button_container.js +++ b/app/javascript/mastodon/features/compose/containers/upload_button_container.js @@ -9,7 +9,7 @@ const mapStateToProps = state => { const readyAttachmentsSize = state.getIn(['compose', 'media_attachments']).size ?? 0; const pendingAttachmentsSize = state.getIn(['compose', 'pending_media_attachments']).size ?? 0; const attachmentsSize = readyAttachmentsSize + pendingAttachmentsSize; - const isOverLimit = attachmentsSize > 3; + const isOverLimit = attachmentsSize > state.getIn(['server', 'server', 'configuration', 'statuses', 'max_media_attachments'])-1; const hasVideoOrAudio = state.getIn(['compose', 'media_attachments']).some(m => ['video', 'audio'].includes(m.get('type'))); return { From 79b0e192d9fb06dbf203e05e6f621dc3ef4d8c73 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 8 Jul 2024 04:16:00 -0400 Subject: [PATCH 024/101] Move test migrations paths check to workflow level (#30702) --- .github/workflows/test-migrations.yml | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test-migrations.yml b/.github/workflows/test-migrations.yml index 43de631545c6ed..6a0e67c58ee500 100644 --- a/.github/workflows/test-migrations.yml +++ b/.github/workflows/test-migrations.yml @@ -6,25 +6,24 @@ on: branches: - 'main' - 'stable-*' + paths: + - 'Gemfile*' + - '.ruby-version' + - '**/*.rb' + - '.github/workflows/test-migrations.yml' + - 'lib/tasks/tests.rake' + pull_request: + paths: + - 'Gemfile*' + - '.ruby-version' + - '**/*.rb' + - '.github/workflows/test-migrations.yml' + - 'lib/tasks/tests.rake' jobs: - pre_job: - runs-on: ubuntu-latest - - outputs: - should_skip: ${{ steps.skip_check.outputs.should_skip }} - - steps: - - id: skip_check - uses: fkirc/skip-duplicate-actions@v5 - with: - paths: '["Gemfile*", ".ruby-version", "**/*.rb", ".github/workflows/test-migrations.yml", "lib/tasks/tests.rake"]' - test: runs-on: ubuntu-latest - needs: pre_job - if: needs.pre_job.outputs.should_skip != 'true' strategy: fail-fast: false From f1300ad284d8c7877a7406e253cf7558596d9837 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 8 Jul 2024 12:01:08 -0400 Subject: [PATCH 025/101] Rename jobs/attachments rspec tag names (#29762) --- .github/workflows/test-ruby.yml | 2 +- .../controllers/admin/disputes/appeals_controller_spec.rb | 4 ++-- spec/controllers/admin/domain_blocks_controller_spec.rb | 2 +- spec/controllers/admin/resets_controller_spec.rb | 2 +- spec/controllers/auth/sessions_controller_spec.rb | 4 ++-- spec/controllers/concerns/user_tracking_concern_spec.rb | 2 +- spec/controllers/disputes/appeals_controller_spec.rb | 4 ++-- spec/controllers/settings/deletes_controller_spec.rb | 2 +- spec/lib/activitypub/activity/delete_spec.rb | 2 +- spec/lib/activitypub/activity/move_spec.rb | 2 +- spec/models/admin/account_action_spec.rb | 2 +- spec/models/custom_emoji_spec.rb | 2 +- spec/models/media_attachment_spec.rb | 2 +- spec/models/user_spec.rb | 4 ++-- spec/rails_helper.rb | 4 ++-- spec/requests/api/v1/admin/account_actions_spec.rb | 2 +- spec/requests/api/v1/conversations_spec.rb | 2 +- spec/requests/api/v1/featured_tags_spec.rb | 2 +- spec/requests/api/v1/media_spec.rb | 6 +++--- spec/requests/api/v1/notifications/policies_spec.rb | 2 +- spec/requests/api/v1/notifications/requests_spec.rb | 2 +- spec/requests/api/v1/notifications_spec.rb | 2 +- spec/requests/api/v1/reports_spec.rb | 2 +- spec/requests/api/v1/statuses/favourites_spec.rb | 2 +- spec/requests/api/v1/statuses/reblogs_spec.rb | 2 +- spec/requests/api/v1/timelines/home_spec.rb | 2 +- spec/requests/api/v2/media_spec.rb | 2 +- spec/requests/api/v2_alpha/notifications_spec.rb | 2 +- .../models/concerns/account/statuses_search_spec.rb | 2 +- .../activitypub/fetch_remote_status_service_spec.rb | 2 +- spec/services/activitypub/process_account_service_spec.rb | 2 +- spec/services/appeal_service_spec.rb | 2 +- spec/services/authorize_follow_service_spec.rb | 2 +- spec/services/batched_remove_status_service_spec.rb | 2 +- spec/services/block_domain_service_spec.rb | 2 +- spec/services/block_service_spec.rb | 2 +- spec/services/delete_account_service_spec.rb | 4 ++-- spec/services/fan_out_on_write_service_spec.rb | 8 ++++---- spec/services/favourite_service_spec.rb | 2 +- spec/services/follow_service_spec.rb | 2 +- spec/services/import_service_spec.rb | 2 +- spec/services/mute_service_spec.rb | 2 +- spec/services/notify_service_spec.rb | 2 +- spec/services/reject_follow_service_spec.rb | 2 +- spec/services/remove_from_followers_service_spec.rb | 2 +- spec/services/remove_status_service_spec.rb | 2 +- spec/services/report_service_spec.rb | 2 +- spec/services/resolve_account_service_spec.rb | 2 +- spec/services/suspend_account_service_spec.rb | 2 +- spec/services/unallow_domain_service_spec.rb | 2 +- spec/services/unblock_service_spec.rb | 2 +- spec/services/unfollow_service_spec.rb | 4 ++-- spec/services/unsuspend_account_service_spec.rb | 2 +- spec/services/update_account_service_spec.rb | 2 +- spec/support/examples/models/concerns/account_avatar.rb | 4 ++-- spec/support/examples/models/concerns/account_header.rb | 2 +- spec/system/admin/accounts_spec.rb | 2 +- spec/system/new_statuses_spec.rb | 2 +- spec/system/ocr_spec.rb | 2 +- spec/system/report_interface_spec.rb | 2 +- spec/workers/backup_worker_spec.rb | 2 +- spec/workers/move_worker_spec.rb | 2 +- spec/workers/post_process_media_worker_spec.rb | 2 +- 63 files changed, 76 insertions(+), 76 deletions(-) diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml index 0e75a0b764a853..fcfeed5fbad7b3 100644 --- a/.github/workflows/test-ruby.yml +++ b/.github/workflows/test-ruby.yml @@ -224,7 +224,7 @@ jobs: - name: Load database schema run: './bin/rails db:create db:schema:load db:seed' - - run: bin/rspec --tag paperclip_processing + - run: bin/rspec --tag attachment_processing - name: Upload coverage reports to Codecov if: matrix.ruby-version == '.ruby-version' diff --git a/spec/controllers/admin/disputes/appeals_controller_spec.rb b/spec/controllers/admin/disputes/appeals_controller_spec.rb index bf7f9bd70485c0..678ceee1158eb1 100644 --- a/spec/controllers/admin/disputes/appeals_controller_spec.rb +++ b/spec/controllers/admin/disputes/appeals_controller_spec.rb @@ -34,7 +34,7 @@ let(:current_user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) } - it 'redirects back to the strike page and notifies target account about approved appeal', :sidekiq_inline do + it 'redirects back to the strike page and notifies target account about approved appeal', :inline_jobs do emails = capture_emails { subject } expect(response) @@ -58,7 +58,7 @@ let(:current_user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) } - it 'redirects back to the strike page and notifies target account about rejected appeal', :sidekiq_inline do + it 'redirects back to the strike page and notifies target account about rejected appeal', :inline_jobs do emails = capture_emails { subject } expect(response) diff --git a/spec/controllers/admin/domain_blocks_controller_spec.rb b/spec/controllers/admin/domain_blocks_controller_spec.rb index 87b08323da917c..eb2c6265d12eec 100644 --- a/spec/controllers/admin/domain_blocks_controller_spec.rb +++ b/spec/controllers/admin/domain_blocks_controller_spec.rb @@ -176,7 +176,7 @@ end end - describe 'PUT #update', :sidekiq_inline do + describe 'PUT #update', :inline_jobs do subject do post :update, params: { :id => domain_block.id, :domain_block => { domain: 'example.com', severity: new_severity }, 'confirm' => '' } end diff --git a/spec/controllers/admin/resets_controller_spec.rb b/spec/controllers/admin/resets_controller_spec.rb index 10ed2cf969730c..0cbc3b60ab6f88 100644 --- a/spec/controllers/admin/resets_controller_spec.rb +++ b/spec/controllers/admin/resets_controller_spec.rb @@ -13,7 +13,7 @@ sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin')), scope: :user end - describe 'POST #create', :sidekiq_inline do + describe 'POST #create', :inline_jobs do it 'redirects to admin accounts page' do emails = capture_emails { subject } diff --git a/spec/controllers/auth/sessions_controller_spec.rb b/spec/controllers/auth/sessions_controller_spec.rb index e78554ec7dc3e8..9a94e5e1a1a464 100644 --- a/spec/controllers/auth/sessions_controller_spec.rb +++ b/spec/controllers/auth/sessions_controller_spec.rb @@ -123,7 +123,7 @@ user.update(current_sign_in_at: 1.month.ago) end - it 'logs the user in and sends suspicious email and redirects home', :sidekiq_inline do + it 'logs the user in and sends suspicious email and redirects home', :inline_jobs do emails = capture_emails { subject } expect(response) @@ -263,7 +263,7 @@ travel_to '2023-12-20T10:00:00Z' end - it 'does not log the user in, sets a flash message, and sends a suspicious sign in email', :sidekiq_inline do + it 'does not log the user in, sets a flash message, and sends a suspicious sign in email', :inline_jobs do emails = capture_emails do Auth::SessionsController::MAX_2FA_ATTEMPTS_PER_HOUR.times do post :create, params: { user: { otp_attempt: '1234' } }, session: { attempt_user_id: user.id, attempt_user_updated_at: user.updated_at.to_s } diff --git a/spec/controllers/concerns/user_tracking_concern_spec.rb b/spec/controllers/concerns/user_tracking_concern_spec.rb index b1de3cf4e2e2a0..f23d482f5f9303 100644 --- a/spec/controllers/concerns/user_tracking_concern_spec.rb +++ b/spec/controllers/concerns/user_tracking_concern_spec.rb @@ -75,7 +75,7 @@ def show expect(redis.ttl("account:#{user.account_id}:regeneration")).to be >= 0 end - it 'regenerates feed when sign in is older than two weeks', :sidekiq_inline do + it 'regenerates feed when sign in is older than two weeks', :inline_jobs do get :show expect_updated_sign_in_at(user) diff --git a/spec/controllers/disputes/appeals_controller_spec.rb b/spec/controllers/disputes/appeals_controller_spec.rb index 99d5a8b17f0252..3e874bbdcc0cab 100644 --- a/spec/controllers/disputes/appeals_controller_spec.rb +++ b/spec/controllers/disputes/appeals_controller_spec.rb @@ -17,7 +17,7 @@ let(:strike) { Fabricate(:account_warning, target_account: current_user.account) } let(:params) { { strike_id: strike.id, appeal: { text: 'Foo' } } } - it 'notifies staff about new appeal and redirects back to strike page', :sidekiq_inline do + it 'notifies staff about new appeal and redirects back to strike page', :inline_jobs do emails = capture_emails { subject } expect(emails.size) @@ -36,7 +36,7 @@ let(:strike) { Fabricate(:account_warning, target_account: current_user.account) } let(:params) { { strike_id: strike.id, appeal: { text: '' } } } - it 'does not send email and renders strike show page', :sidekiq_inline do + it 'does not send email and renders strike show page', :inline_jobs do emails = capture_emails { subject } expect(emails).to be_empty diff --git a/spec/controllers/settings/deletes_controller_spec.rb b/spec/controllers/settings/deletes_controller_spec.rb index ccca4564e733a1..3342599bc1172e 100644 --- a/spec/controllers/settings/deletes_controller_spec.rb +++ b/spec/controllers/settings/deletes_controller_spec.rb @@ -50,7 +50,7 @@ delete :destroy, params: { form_delete_confirmation: { password: 'petsmoldoggos' } } end - it 'removes user record and redirects', :aggregate_failures, :sidekiq_inline do + it 'removes user record and redirects', :aggregate_failures, :inline_jobs do expect(response).to redirect_to '/auth/sign_in' expect(User.find_by(id: user.id)).to be_nil expect(user.account.reload).to be_suspended diff --git a/spec/lib/activitypub/activity/delete_spec.rb b/spec/lib/activitypub/activity/delete_spec.rb index 48421a116294b5..71977a96a2be81 100644 --- a/spec/lib/activitypub/activity/delete_spec.rb +++ b/spec/lib/activitypub/activity/delete_spec.rb @@ -47,7 +47,7 @@ expect(Status.find_by(id: status.id)).to be_nil end - it 'sends delete activity to followers of rebloggers', :sidekiq_inline do + it 'sends delete activity to followers of rebloggers', :inline_jobs do expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once end diff --git a/spec/lib/activitypub/activity/move_spec.rb b/spec/lib/activitypub/activity/move_spec.rb index 4dda014a068565..d69ef215161210 100644 --- a/spec/lib/activitypub/activity/move_spec.rb +++ b/spec/lib/activitypub/activity/move_spec.rb @@ -38,7 +38,7 @@ subject.perform end - context 'when all conditions are met', :sidekiq_inline do + context 'when all conditions are met', :inline_jobs do it 'sets moved account on old account' do expect(old_account.reload.moved_to_account_id).to eq new_account.id end diff --git a/spec/models/admin/account_action_spec.rb b/spec/models/admin/account_action_spec.rb index e55db2f814a19e..49bc2b4a91c46c 100644 --- a/spec/models/admin/account_action_spec.rb +++ b/spec/models/admin/account_action_spec.rb @@ -69,7 +69,7 @@ end end - it 'sends email to target account user', :sidekiq_inline do + it 'sends email to target account user', :inline_jobs do emails = capture_emails { subject } expect(emails).to contain_exactly( diff --git a/spec/models/custom_emoji_spec.rb b/spec/models/custom_emoji_spec.rb index 038d1d0c6cda8f..cb8cb5c11bd67e 100644 --- a/spec/models/custom_emoji_spec.rb +++ b/spec/models/custom_emoji_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -RSpec.describe CustomEmoji, :paperclip_processing do +RSpec.describe CustomEmoji, :attachment_processing do describe '#search' do subject { described_class.search(search_term) } diff --git a/spec/models/media_attachment_spec.rb b/spec/models/media_attachment_spec.rb index a8f1ce7745ee6e..24e8ca39c11f0e 100644 --- a/spec/models/media_attachment_spec.rb +++ b/spec/models/media_attachment_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -RSpec.describe MediaAttachment, :paperclip_processing do +RSpec.describe MediaAttachment, :attachment_processing do describe 'local?' do subject { media_attachment.local? } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index fa0a0503a6561d..4755500fc4e356 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -101,7 +101,7 @@ end end - describe 'scopes', :sidekiq_inline do + describe 'scopes', :inline_jobs do describe 'recent' do it 'returns an array of recent users ordered by id' do first_user = Fabricate(:user) @@ -507,7 +507,7 @@ def within_duration_window_days context 'when user is new' do let(:confirmed_at) { nil } - it 'confirms user and delivers welcome email', :sidekiq_inline do + it 'confirms user and delivers welcome email', :inline_jobs do emails = capture_emails { subject } expect(user.confirmed_at).to be_present diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 38aa711089e325..79031f1a9430f7 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -120,7 +120,7 @@ def sign_in(resource, _deprecated = nil, scope: nil) end config.around do |example| - if example.metadata[:sidekiq_inline] == true + if example.metadata[:inline_jobs] == true Sidekiq::Testing.inline! else Sidekiq::Testing.fake! @@ -137,7 +137,7 @@ def sign_in(resource, _deprecated = nil, scope: nil) end config.before do |example| - unless example.metadata[:paperclip_processing] + unless example.metadata[:attachment_processing] allow_any_instance_of(Paperclip::Attachment).to receive(:post_process).and_return(true) # rubocop:disable RSpec/AnyInstance end end diff --git a/spec/requests/api/v1/admin/account_actions_spec.rb b/spec/requests/api/v1/admin/account_actions_spec.rb index 778658508ec100..5bcf809401da4c 100644 --- a/spec/requests/api/v1/admin/account_actions_spec.rb +++ b/spec/requests/api/v1/admin/account_actions_spec.rb @@ -10,7 +10,7 @@ let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } shared_examples 'a successful notification delivery' do - it 'notifies the user about the action taken', :sidekiq_inline do + it 'notifies the user about the action taken', :inline_jobs do emails = capture_emails { subject } expect(emails.size) diff --git a/spec/requests/api/v1/conversations_spec.rb b/spec/requests/api/v1/conversations_spec.rb index caa0f5c52c0975..f4776f18d97453 100644 --- a/spec/requests/api/v1/conversations_spec.rb +++ b/spec/requests/api/v1/conversations_spec.rb @@ -10,7 +10,7 @@ let(:other) { Fabricate(:user) } - describe 'GET /api/v1/conversations', :sidekiq_inline do + describe 'GET /api/v1/conversations', :inline_jobs do before do user.account.follow!(other.account) PostStatusService.new.call(other.account, text: 'Hey @alice', visibility: 'direct') diff --git a/spec/requests/api/v1/featured_tags_spec.rb b/spec/requests/api/v1/featured_tags_spec.rb index f499dd1d09a4d5..4b9698870411aa 100644 --- a/spec/requests/api/v1/featured_tags_spec.rb +++ b/spec/requests/api/v1/featured_tags_spec.rb @@ -147,7 +147,7 @@ expect(body).to be_empty end - it 'deletes the featured tag', :sidekiq_inline do + it 'deletes the featured tag', :inline_jobs do delete "/api/v1/featured_tags/#{id}", headers: headers featured_tag = FeaturedTag.find_by(id: id) diff --git a/spec/requests/api/v1/media_spec.rb b/spec/requests/api/v1/media_spec.rb index 26c76b9c5b98f6..c89c49afdf43aa 100644 --- a/spec/requests/api/v1/media_spec.rb +++ b/spec/requests/api/v1/media_spec.rb @@ -121,19 +121,19 @@ end end - context 'with image/jpeg', :paperclip_processing do + context 'with image/jpeg', :attachment_processing do let(:params) { { file: fixture_file_upload('attachment.jpg', 'image/jpeg'), description: 'jpeg image' } } it_behaves_like 'a successful media upload', 'image' end - context 'with image/gif', :paperclip_processing do + context 'with image/gif', :attachment_processing do let(:params) { { file: fixture_file_upload('attachment.gif', 'image/gif') } } it_behaves_like 'a successful media upload', 'image' end - context 'with video/webm', :paperclip_processing do + context 'with video/webm', :attachment_processing do let(:params) { { file: fixture_file_upload('attachment.webm', 'video/webm') } } it_behaves_like 'a successful media upload', 'gifv' diff --git a/spec/requests/api/v1/notifications/policies_spec.rb b/spec/requests/api/v1/notifications/policies_spec.rb index d02d2ed0d7b27d..cbd4499772d443 100644 --- a/spec/requests/api/v1/notifications/policies_spec.rb +++ b/spec/requests/api/v1/notifications/policies_spec.rb @@ -8,7 +8,7 @@ let(:scopes) { 'read:notifications write:notifications' } let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } - describe 'GET /api/v1/notifications/policy', :sidekiq_inline do + describe 'GET /api/v1/notifications/policy', :inline_jobs do subject do get '/api/v1/notifications/policy', headers: headers, params: params end diff --git a/spec/requests/api/v1/notifications/requests_spec.rb b/spec/requests/api/v1/notifications/requests_spec.rb index 772402a6b5a089..23ddfd2bdabe45 100644 --- a/spec/requests/api/v1/notifications/requests_spec.rb +++ b/spec/requests/api/v1/notifications/requests_spec.rb @@ -8,7 +8,7 @@ let(:scopes) { 'read:notifications write:notifications' } let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } - describe 'GET /api/v1/notifications/requests', :sidekiq_inline do + describe 'GET /api/v1/notifications/requests', :inline_jobs do subject do get '/api/v1/notifications/requests', headers: headers, params: params end diff --git a/spec/requests/api/v1/notifications_spec.rb b/spec/requests/api/v1/notifications_spec.rb index 55d3cdac948896..9ff0674317bedd 100644 --- a/spec/requests/api/v1/notifications_spec.rb +++ b/spec/requests/api/v1/notifications_spec.rb @@ -8,7 +8,7 @@ let(:scopes) { 'read:notifications write:notifications' } let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } - describe 'GET /api/v1/notifications', :sidekiq_inline do + describe 'GET /api/v1/notifications', :inline_jobs do subject do get '/api/v1/notifications', headers: headers, params: params end diff --git a/spec/requests/api/v1/reports_spec.rb b/spec/requests/api/v1/reports_spec.rb index 9e8954a4c65dd8..491c6263d3397e 100644 --- a/spec/requests/api/v1/reports_spec.rb +++ b/spec/requests/api/v1/reports_spec.rb @@ -33,7 +33,7 @@ it_behaves_like 'forbidden for wrong scope', 'read read:reports' - it 'creates a report', :aggregate_failures, :sidekiq_inline do + it 'creates a report', :aggregate_failures, :inline_jobs do emails = capture_emails { subject } expect(response).to have_http_status(200) diff --git a/spec/requests/api/v1/statuses/favourites_spec.rb b/spec/requests/api/v1/statuses/favourites_spec.rb index 033aed7e285ddc..22d0e4831fc64d 100644 --- a/spec/requests/api/v1/statuses/favourites_spec.rb +++ b/spec/requests/api/v1/statuses/favourites_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -RSpec.describe 'Favourites', :sidekiq_inline do +RSpec.describe 'Favourites', :inline_jobs do let(:user) { Fabricate(:user) } let(:scopes) { 'write:favourites' } let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } diff --git a/spec/requests/api/v1/statuses/reblogs_spec.rb b/spec/requests/api/v1/statuses/reblogs_spec.rb index cf0a1f861d9bde..503d804ed01c0b 100644 --- a/spec/requests/api/v1/statuses/reblogs_spec.rb +++ b/spec/requests/api/v1/statuses/reblogs_spec.rb @@ -41,7 +41,7 @@ end end - describe 'POST /api/v1/statuses/:status_id/unreblog', :sidekiq_inline do + describe 'POST /api/v1/statuses/:status_id/unreblog', :inline_jobs do context 'with public status' do let(:status) { Fabricate(:status, account: user.account) } diff --git a/spec/requests/api/v1/timelines/home_spec.rb b/spec/requests/api/v1/timelines/home_spec.rb index 2bebe8cf452847..96bd153affe5a4 100644 --- a/spec/requests/api/v1/timelines/home_spec.rb +++ b/spec/requests/api/v1/timelines/home_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe 'Home', :sidekiq_inline do +describe 'Home', :inline_jobs do let(:user) { Fabricate(:user) } let(:scopes) { 'read:statuses' } let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } diff --git a/spec/requests/api/v2/media_spec.rb b/spec/requests/api/v2/media_spec.rb index 990fa5d0baeeab..97540413f152d4 100644 --- a/spec/requests/api/v2/media_spec.rb +++ b/spec/requests/api/v2/media_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -RSpec.describe 'Media API', :paperclip_processing do +RSpec.describe 'Media API', :attachment_processing do let(:user) { Fabricate(:user) } let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } let(:scopes) { 'write' } diff --git a/spec/requests/api/v2_alpha/notifications_spec.rb b/spec/requests/api/v2_alpha/notifications_spec.rb index ac44605ac5d212..104651ebe39f44 100644 --- a/spec/requests/api/v2_alpha/notifications_spec.rb +++ b/spec/requests/api/v2_alpha/notifications_spec.rb @@ -8,7 +8,7 @@ let(:scopes) { 'read:notifications write:notifications' } let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } - describe 'GET /api/v2_alpha/notifications', :sidekiq_inline do + describe 'GET /api/v2_alpha/notifications', :inline_jobs do subject do get '/api/v2_alpha/notifications', headers: headers, params: params end diff --git a/spec/search/models/concerns/account/statuses_search_spec.rb b/spec/search/models/concerns/account/statuses_search_spec.rb index a1b0bf405c5133..b1bf4968ca9abd 100644 --- a/spec/search/models/concerns/account/statuses_search_spec.rb +++ b/spec/search/models/concerns/account/statuses_search_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe Account::StatusesSearch, :sidekiq_inline do +describe Account::StatusesSearch, :inline_jobs do describe 'a non-indexable account becoming indexable' do let(:account) { Account.find_by(username: 'search_test_account_1') } diff --git a/spec/services/activitypub/fetch_remote_status_service_spec.rb b/spec/services/activitypub/fetch_remote_status_service_spec.rb index a86f141fe0b747..c3adb9c470afd1 100644 --- a/spec/services/activitypub/fetch_remote_status_service_spec.rb +++ b/spec/services/activitypub/fetch_remote_status_service_spec.rb @@ -225,7 +225,7 @@ end end - context 'with statuses referencing other statuses', :sidekiq_inline do + context 'with statuses referencing other statuses', :inline_jobs do before do stub_const 'ActivityPub::FetchRemoteStatusService::DISCOVERIES_PER_REQUEST', 5 end diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb index 8b80dafe45a924..4fbb527b39041e 100644 --- a/spec/services/activitypub/process_account_service_spec.rb +++ b/spec/services/activitypub/process_account_service_spec.rb @@ -224,7 +224,7 @@ end end - it 'creates accounts without exceeding rate limit', :sidekiq_inline do + it 'creates accounts without exceeding rate limit', :inline_jobs do expect { subject.call('user1', 'foo.test', payload) } .to create_some_remote_accounts .and create_fewer_than_rate_limit_accounts diff --git a/spec/services/appeal_service_spec.rb b/spec/services/appeal_service_spec.rb index 3fad74db9d8951..6a47bb2cea61d2 100644 --- a/spec/services/appeal_service_spec.rb +++ b/spec/services/appeal_service_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -RSpec.describe AppealService, :sidekiq_inline do +RSpec.describe AppealService, :inline_jobs do describe '#call' do let!(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) } diff --git a/spec/services/authorize_follow_service_spec.rb b/spec/services/authorize_follow_service_spec.rb index be2a8641854054..533b791fb7f6e7 100644 --- a/spec/services/authorize_follow_service_spec.rb +++ b/spec/services/authorize_follow_service_spec.rb @@ -41,7 +41,7 @@ expect(bob.following?(sender)).to be true end - it 'sends an accept activity', :sidekiq_inline do + it 'sends an accept activity', :inline_jobs do expect(a_request(:post, bob.inbox_url)).to have_been_made.once end end diff --git a/spec/services/batched_remove_status_service_spec.rb b/spec/services/batched_remove_status_service_spec.rb index e501b9ba84a94c..628bb198ef06f3 100644 --- a/spec/services/batched_remove_status_service_spec.rb +++ b/spec/services/batched_remove_status_service_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -RSpec.describe BatchedRemoveStatusService, :sidekiq_inline do +RSpec.describe BatchedRemoveStatusService, :inline_jobs do subject { described_class.new } let!(:alice) { Fabricate(:account) } diff --git a/spec/services/block_domain_service_spec.rb b/spec/services/block_domain_service_spec.rb index d4f0c042d453c9..839137db4498e8 100644 --- a/spec/services/block_domain_service_spec.rb +++ b/spec/services/block_domain_service_spec.rb @@ -49,7 +49,7 @@ end describe 'for a silence with reject media' do - it 'does not mark the domain as blocked, but silences accounts with an appropriate silencing date, clears media', :aggregate_failures, :sidekiq_inline do + it 'does not mark the domain as blocked, but silences accounts with an appropriate silencing date, clears media', :aggregate_failures, :inline_jobs do subject.call(DomainBlock.create!(domain: 'evil.org', severity: :silence, reject_media: true)) expect(DomainBlock.blocked?('evil.org')).to be false diff --git a/spec/services/block_service_spec.rb b/spec/services/block_service_spec.rb index d096aa1ea35566..46dd69198699a4 100644 --- a/spec/services/block_service_spec.rb +++ b/spec/services/block_service_spec.rb @@ -33,7 +33,7 @@ expect(sender.blocking?(bob)).to be true end - it 'sends a block activity', :sidekiq_inline do + it 'sends a block activity', :inline_jobs do expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once end end diff --git a/spec/services/delete_account_service_spec.rb b/spec/services/delete_account_service_spec.rb index de93862435afbe..741ac340cfa0be 100644 --- a/spec/services/delete_account_service_spec.rb +++ b/spec/services/delete_account_service_spec.rb @@ -62,7 +62,7 @@ def expect_deletion_of_associated_target_notifications end end - describe '#call on local account', :sidekiq_inline do + describe '#call on local account', :inline_jobs do before do stub_request(:post, remote_alice.inbox_url).to_return(status: 201) stub_request(:post, remote_bob.inbox_url).to_return(status: 201) @@ -83,7 +83,7 @@ def expect_deletion_of_associated_target_notifications end end - describe '#call on remote account', :sidekiq_inline do + describe '#call on remote account', :inline_jobs do before do stub_request(:post, account.inbox_url).to_return(status: 201) end diff --git a/spec/services/fan_out_on_write_service_spec.rb b/spec/services/fan_out_on_write_service_spec.rb index b51d802a5b9ae2..82cdffb8cf9f5c 100644 --- a/spec/services/fan_out_on_write_service_spec.rb +++ b/spec/services/fan_out_on_write_service_spec.rb @@ -38,7 +38,7 @@ def home_feed_of(account) expect(home_feed_of(alice)).to include status.id end - it 'is added to the home feed of a follower', :sidekiq_inline do + it 'is added to the home feed of a follower', :inline_jobs do expect(home_feed_of(bob)).to include status.id expect(home_feed_of(tom)).to include status.id end @@ -62,7 +62,7 @@ def home_feed_of(account) expect(home_feed_of(alice)).to include status.id end - it 'is added to the home feed of the mentioned follower', :sidekiq_inline do + it 'is added to the home feed of the mentioned follower', :inline_jobs do expect(home_feed_of(bob)).to include status.id end @@ -83,7 +83,7 @@ def home_feed_of(account) expect(home_feed_of(alice)).to include status.id end - it 'is added to the home feed of a follower', :sidekiq_inline do + it 'is added to the home feed of a follower', :inline_jobs do expect(home_feed_of(bob)).to include status.id expect(home_feed_of(tom)).to include status.id end @@ -101,7 +101,7 @@ def home_feed_of(account) expect(home_feed_of(alice)).to include status.id end - it 'is added to the home feed of the mentioned follower', :sidekiq_inline do + it 'is added to the home feed of the mentioned follower', :inline_jobs do expect(home_feed_of(bob)).to include status.id end diff --git a/spec/services/favourite_service_spec.rb b/spec/services/favourite_service_spec.rb index d0f1ff17c1ebd9..c39362def2f7f3 100644 --- a/spec/services/favourite_service_spec.rb +++ b/spec/services/favourite_service_spec.rb @@ -33,7 +33,7 @@ expect(status.favourites.first).to_not be_nil end - it 'sends a like activity', :sidekiq_inline do + it 'sends a like activity', :inline_jobs do expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once end end diff --git a/spec/services/follow_service_spec.rb b/spec/services/follow_service_spec.rb index bea2412a3d36fe..0c4cd600460922 100644 --- a/spec/services/follow_service_spec.rb +++ b/spec/services/follow_service_spec.rb @@ -150,7 +150,7 @@ expect(FollowRequest.find_by(account: sender, target_account: bob)).to_not be_nil end - it 'sends a follow activity to the inbox', :sidekiq_inline do + it 'sends a follow activity to the inbox', :inline_jobs do expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once end end diff --git a/spec/services/import_service_spec.rb b/spec/services/import_service_spec.rb index 90877d99975200..0a99c5e748db55 100644 --- a/spec/services/import_service_spec.rb +++ b/spec/services/import_service_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -RSpec.describe ImportService, :sidekiq_inline do +RSpec.describe ImportService, :inline_jobs do include RoutingHelper let!(:account) { Fabricate(:account, locked: false) } diff --git a/spec/services/mute_service_spec.rb b/spec/services/mute_service_spec.rb index 681afc0b16ec39..3bde92b87aebdc 100644 --- a/spec/services/mute_service_spec.rb +++ b/spec/services/mute_service_spec.rb @@ -17,7 +17,7 @@ redis.del(home_timeline_key) end - it "clears account's statuses", :sidekiq_inline do + it "clears account's statuses", :inline_jobs do FeedManager.instance.push_to_home(account, status) FeedManager.instance.push_to_home(account, other_account_status) diff --git a/spec/services/notify_service_spec.rb b/spec/services/notify_service_spec.rb index 8c810f1c32b521..c695855bec0d1c 100644 --- a/spec/services/notify_service_spec.rb +++ b/spec/services/notify_service_spec.rb @@ -105,7 +105,7 @@ context 'when email notification is enabled' do let(:enabled) { true } - it 'sends email', :sidekiq_inline do + it 'sends email', :inline_jobs do emails = capture_emails { subject } expect(emails.size) diff --git a/spec/services/reject_follow_service_spec.rb b/spec/services/reject_follow_service_spec.rb index 98aaf70478539b..d2c7a00206210b 100644 --- a/spec/services/reject_follow_service_spec.rb +++ b/spec/services/reject_follow_service_spec.rb @@ -41,7 +41,7 @@ expect(bob.following?(sender)).to be false end - it 'sends a reject activity', :sidekiq_inline do + it 'sends a reject activity', :inline_jobs do expect(a_request(:post, bob.inbox_url)).to have_been_made.once end end diff --git a/spec/services/remove_from_followers_service_spec.rb b/spec/services/remove_from_followers_service_spec.rb index d6420f76742dfe..515600096cfc04 100644 --- a/spec/services/remove_from_followers_service_spec.rb +++ b/spec/services/remove_from_followers_service_spec.rb @@ -33,7 +33,7 @@ expect(bob.followed_by?(sender)).to be false end - it 'sends a reject activity', :sidekiq_inline do + it 'sends a reject activity', :inline_jobs do expect(a_request(:post, sender.inbox_url)).to have_been_made.once end end diff --git a/spec/services/remove_status_service_spec.rb b/spec/services/remove_status_service_spec.rb index 917b66c6df467b..08f519b5360d99 100644 --- a/spec/services/remove_status_service_spec.rb +++ b/spec/services/remove_status_service_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -RSpec.describe RemoveStatusService, :sidekiq_inline do +RSpec.describe RemoveStatusService, :inline_jobs do subject { described_class.new } let!(:alice) { Fabricate(:account) } diff --git a/spec/services/report_service_spec.rb b/spec/services/report_service_spec.rb index 141dc8c3be5623..bf1f937191b74c 100644 --- a/spec/services/report_service_spec.rb +++ b/spec/services/report_service_spec.rb @@ -23,7 +23,7 @@ stub_request(:post, 'http://example.com/inbox').to_return(status: 200) end - context 'when forward is true', :sidekiq_inline do + context 'when forward is true', :inline_jobs do let(:forward) { true } it 'sends ActivityPub payload when forward is true' do diff --git a/spec/services/resolve_account_service_spec.rb b/spec/services/resolve_account_service_spec.rb index 316266c8f8e539..e0084a15791a1a 100644 --- a/spec/services/resolve_account_service_spec.rb +++ b/spec/services/resolve_account_service_spec.rb @@ -195,7 +195,7 @@ expect(account.uri).to eq 'https://ap.example.com/users/foo' end - it 'merges accounts', :sidekiq_inline do + it 'merges accounts', :inline_jobs do account = subject.call('foo@ap.example.com') expect(status.reload.account_id).to eq account.id diff --git a/spec/services/suspend_account_service_spec.rb b/spec/services/suspend_account_service_spec.rb index 7c72a4776b4b1d..4a2f494e0cb1b8 100644 --- a/spec/services/suspend_account_service_spec.rb +++ b/spec/services/suspend_account_service_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -RSpec.describe SuspendAccountService, :sidekiq_inline do +RSpec.describe SuspendAccountService, :inline_jobs do shared_examples 'common behavior' do subject { described_class.new.call(account) } diff --git a/spec/services/unallow_domain_service_spec.rb b/spec/services/unallow_domain_service_spec.rb index caec3d596ff144..4bf6c540432027 100644 --- a/spec/services/unallow_domain_service_spec.rb +++ b/spec/services/unallow_domain_service_spec.rb @@ -13,7 +13,7 @@ let!(:already_banned_account) { Fabricate(:account, username: 'badguy', domain: bad_domain, suspended: true, silenced: true) } let!(:domain_allow) { Fabricate(:domain_allow, domain: bad_domain) } - context 'with limited federation mode', :sidekiq_inline do + context 'with limited federation mode', :inline_jobs do before do allow(Rails.configuration.x).to receive(:limited_federation_mode).and_return(true) end diff --git a/spec/services/unblock_service_spec.rb b/spec/services/unblock_service_spec.rb index 4c9fcb9aeecdd0..6132e74415ecd6 100644 --- a/spec/services/unblock_service_spec.rb +++ b/spec/services/unblock_service_spec.rb @@ -33,7 +33,7 @@ expect(sender.blocking?(bob)).to be false end - it 'sends an unblock activity', :sidekiq_inline do + it 'sends an unblock activity', :inline_jobs do expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once end end diff --git a/spec/services/unfollow_service_spec.rb b/spec/services/unfollow_service_spec.rb index bba17a8d2709a2..0c206c4b983215 100644 --- a/spec/services/unfollow_service_spec.rb +++ b/spec/services/unfollow_service_spec.rb @@ -20,7 +20,7 @@ end end - describe 'remote ActivityPub', :sidekiq_inline do + describe 'remote ActivityPub', :inline_jobs do let(:bob) { Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') } before do @@ -38,7 +38,7 @@ end end - describe 'remote ActivityPub (reverse)', :sidekiq_inline do + describe 'remote ActivityPub (reverse)', :inline_jobs do let(:bob) { Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') } before do diff --git a/spec/services/unsuspend_account_service_spec.rb b/spec/services/unsuspend_account_service_spec.rb index 79a4441d3ec9ef..8d4882c37f88aa 100644 --- a/spec/services/unsuspend_account_service_spec.rb +++ b/spec/services/unsuspend_account_service_spec.rb @@ -45,7 +45,7 @@ def match_update_actor_request(req, account) remote_follower.follow!(account) end - it 'merges back into feeds of local followers and sends update', :sidekiq_inline do + it 'merges back into feeds of local followers and sends update', :inline_jobs do subject expect_feeds_merged diff --git a/spec/services/update_account_service_spec.rb b/spec/services/update_account_service_spec.rb index 5204f1f34d469d..d066db481e5568 100644 --- a/spec/services/update_account_service_spec.rb +++ b/spec/services/update_account_service_spec.rb @@ -5,7 +5,7 @@ RSpec.describe UpdateAccountService do subject { described_class.new } - describe 'switching form locked to unlocked accounts', :sidekiq_inline do + describe 'switching form locked to unlocked accounts', :inline_jobs do let(:account) { Fabricate(:account, locked: true) } let(:alice) { Fabricate(:account) } let(:bob) { Fabricate(:account) } diff --git a/spec/support/examples/models/concerns/account_avatar.rb b/spec/support/examples/models/concerns/account_avatar.rb index 2c9b5514aad7d6..ab6020d834a8f7 100644 --- a/spec/support/examples/models/concerns/account_avatar.rb +++ b/spec/support/examples/models/concerns/account_avatar.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true shared_examples 'AccountAvatar' do |fabricator| - describe 'static avatars', :paperclip_processing do + describe 'static avatars', :attachment_processing do describe 'when GIF' do it 'creates a png static style' do account = Fabricate(fabricator, avatar: attachment_fixture('avatar.gif')) @@ -17,7 +17,7 @@ end end - describe 'base64-encoded files', :paperclip_processing do + describe 'base64-encoded files', :attachment_processing do let(:base64_attachment) { "data:image/jpeg;base64,#{Base64.encode64(attachment_fixture('attachment.jpg').read)}" } let(:account) { Fabricate(fabricator, avatar: base64_attachment) } diff --git a/spec/support/examples/models/concerns/account_header.rb b/spec/support/examples/models/concerns/account_header.rb index ce09eb006b8ae4..43bbdaacf425d9 100644 --- a/spec/support/examples/models/concerns/account_header.rb +++ b/spec/support/examples/models/concerns/account_header.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true shared_examples 'AccountHeader' do |fabricator| - describe 'base64-encoded files', :paperclip_processing do + describe 'base64-encoded files', :attachment_processing do let(:base64_attachment) { "data:image/jpeg;base64,#{Base64.encode64(attachment_fixture('attachment.jpg').read)}" } let(:account) { Fabricate(fabricator, header: base64_attachment) } diff --git a/spec/system/admin/accounts_spec.rb b/spec/system/admin/accounts_spec.rb index 54d755b914de12..20813f6be42505 100644 --- a/spec/system/admin/accounts_spec.rb +++ b/spec/system/admin/accounts_spec.rb @@ -48,7 +48,7 @@ end end - context 'with action of `reject`', :sidekiq_inline do + context 'with action of `reject`', :inline_jobs do it 'rejects and removes the account' do batch_checkbox_for(unapproved_user_account).check diff --git a/spec/system/new_statuses_spec.rb b/spec/system/new_statuses_spec.rb index 725ea8fe5209d4..2f2fcf22485ee4 100644 --- a/spec/system/new_statuses_spec.rb +++ b/spec/system/new_statuses_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe 'NewStatuses', :js, :sidekiq_inline, :streaming do +describe 'NewStatuses', :inline_jobs, :js, :streaming do include ProfileStories subject { page } diff --git a/spec/system/ocr_spec.rb b/spec/system/ocr_spec.rb index 4f4941adca581c..17d18af1586ed7 100644 --- a/spec/system/ocr_spec.rb +++ b/spec/system/ocr_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe 'OCR', :js, :paperclip_processing, :sidekiq_inline, :streaming do +describe 'OCR', :attachment_processing, :inline_jobs, :js, :streaming do include ProfileStories let(:email) { 'test@example.com' } diff --git a/spec/system/report_interface_spec.rb b/spec/system/report_interface_spec.rb index f82604aae89643..e6cc3b1b68870f 100644 --- a/spec/system/report_interface_spec.rb +++ b/spec/system/report_interface_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe 'report interface', :js, :paperclip_processing, :streaming do +describe 'report interface', :attachment_processing, :js, :streaming do include ProfileStories let(:email) { 'admin@example.com' } diff --git a/spec/workers/backup_worker_spec.rb b/spec/workers/backup_worker_spec.rb index 74928c7ca6ba63..db1b50140b6790 100644 --- a/spec/workers/backup_worker_spec.rb +++ b/spec/workers/backup_worker_spec.rb @@ -14,7 +14,7 @@ let(:backup) { Fabricate(:backup) } let!(:other_backup) { Fabricate(:backup, user: backup.user) } - it 'sends the backup to the service and removes other backups', :sidekiq_inline do + it 'sends the backup to the service and removes other backups', :inline_jobs do emails = capture_emails { worker.perform(backup.id) } expect(service).to have_received(:call).with(backup) diff --git a/spec/workers/move_worker_spec.rb b/spec/workers/move_worker_spec.rb index 0513dc42e5282d..b25992e44bd615 100644 --- a/spec/workers/move_worker_spec.rb +++ b/spec/workers/move_worker_spec.rb @@ -104,7 +104,7 @@ def mute_account_comment end shared_examples 'lists handling' do - it 'puts the new account on the list and makes valid lists', :sidekiq_inline do + it 'puts the new account on the list and makes valid lists', :inline_jobs do subject.perform(source_account.id, target_account.id) expect(list.accounts.include?(target_account)).to be true diff --git a/spec/workers/post_process_media_worker_spec.rb b/spec/workers/post_process_media_worker_spec.rb index 828da5244f55f9..1a274623d73fba 100644 --- a/spec/workers/post_process_media_worker_spec.rb +++ b/spec/workers/post_process_media_worker_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe PostProcessMediaWorker, :paperclip_processing do +describe PostProcessMediaWorker, :attachment_processing do let(:worker) { described_class.new } describe '#perform' do From fa8e972722fb8fc056aa348dddaee4005b4a8ac4 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Mon, 8 Jul 2024 18:04:36 +0200 Subject: [PATCH 026/101] Fix author names as arrays in linked data. (#30957) --- app/lib/link_details_extractor.rb | 5 +++-- spec/lib/link_details_extractor_spec.rb | 29 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/app/lib/link_details_extractor.rb b/app/lib/link_details_extractor.rb index a62ede2bb1f356..72992d2f42ad80 100644 --- a/app/lib/link_details_extractor.rb +++ b/app/lib/link_details_extractor.rb @@ -62,7 +62,8 @@ def date_modified end def author_name - author['name'] + name = author['name'] + name.is_a?(Array) ? name.join(', ') : name end def author_url @@ -294,7 +295,7 @@ def detector def html_entities_decode(string) return if string.nil? - unicode_string = string.encode('UTF-8') + unicode_string = string.to_s.encode('UTF-8') raise EncodingError, 'cannot convert string to valid UTF-8' unless unicode_string.valid_encoding? html_entities.decode(unicode_string) diff --git a/spec/lib/link_details_extractor_spec.rb b/spec/lib/link_details_extractor_spec.rb index 26d9d4e2659821..2a4df70a8bf226 100644 --- a/spec/lib/link_details_extractor_spec.rb +++ b/spec/lib/link_details_extractor_spec.rb @@ -192,6 +192,35 @@ include_examples 'structured data' end + + context 'with author names as array' do + let(:ld_json) do + { + '@context' => 'https://schema.org', + '@type' => 'NewsArticle', + 'headline' => 'A lot of authors', + 'description' => 'But we decided to cram them into one', + 'author' => { + '@type' => 'Person', + 'name' => ['Author 1', 'Author 2'], + }, + }.to_json + end + let(:html) { <<~HTML } + + + + + + + HTML + + it 'joins author names' do + expect(subject.author_name).to eq 'Author 1, Author 2' + end + end end context 'when Open Graph protocol data is present' do From 2fa3c3a748a3a647bae2d8fc2d1bf4776871ea8b Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Sun, 7 Jul 2024 18:13:10 +0200 Subject: [PATCH 027/101] [Glitch] Add the account hover card for account search results Port 0899c91d4be0e0846caf53a4b065c4904f34d23a to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/components/account.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/components/account.jsx b/app/javascript/flavours/glitch/components/account.jsx index 038009cfa86e5a..e2ea8995242b3b 100644 --- a/app/javascript/flavours/glitch/components/account.jsx +++ b/app/javascript/flavours/glitch/components/account.jsx @@ -131,7 +131,7 @@ const Account = ({ size = 46, account, onFollow, onBlock, onMute, onMuteNotifica return (
- +
From aa8ad92fa423d02991e92cfd571ef7b5393ebbb5 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Sun, 7 Jul 2024 18:14:15 +0200 Subject: [PATCH 028/101] [Glitch] Remove the `title` attribute when hovering a card anchor Port 981395e4d68a1c0cfccbdb8924bc11ac912d1e05 to glitch-soc Signed-off-by: Claire --- .../glitch/components/hover_card_controller.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/components/hover_card_controller.tsx b/app/javascript/flavours/glitch/components/hover_card_controller.tsx index 347dcd4f2f99a4..d2a636e939ca4b 100644 --- a/app/javascript/flavours/glitch/components/hover_card_controller.tsx +++ b/app/javascript/flavours/glitch/components/hover_card_controller.tsx @@ -43,6 +43,7 @@ export const HoverCardController: React.FC = () => { useEffect(() => { let isScrolling = false; let currentAnchor: HTMLElement | null = null; + let currentTitle: string | null = null; const open = (target: HTMLElement) => { target.setAttribute('aria-describedby', 'hover-card'); @@ -75,6 +76,9 @@ export const HoverCardController: React.FC = () => { currentAnchor?.removeAttribute('aria-describedby'); currentAnchor = target; + currentTitle = target.getAttribute('title'); + target.removeAttribute('title'); + setEnterTimeout(() => { open(target); }, enterDelay); @@ -90,11 +94,20 @@ export const HoverCardController: React.FC = () => { }; const handleMouseLeave = (e: MouseEvent) => { + const { target } = e; + if (!currentAnchor) { return; } - if (e.target === currentAnchor || e.target === cardRef.current) { + if ( + currentTitle && + target instanceof HTMLElement && + target === currentAnchor + ) + target.setAttribute('title', currentTitle); + + if (target === currentAnchor || target === cardRef.current) { cancelEnterTimeout(); setLeaveTimeout(() => { From 80179d53ba825c2c82178a267498311edde077f3 Mon Sep 17 00:00:00 2001 From: Tianwei Dong Date: Mon, 8 Jul 2024 09:10:57 +0100 Subject: [PATCH 029/101] [Glitch] Change to use an unified constant for max media attachments per status Port 36d819bef34279bb36cdfa3ed89711405ad79591 to glitch-soc Co-authored-by: Renaud Chaput Signed-off-by: Claire --- app/javascript/flavours/glitch/actions/compose.js | 4 ++-- app/javascript/flavours/glitch/components/media_gallery.jsx | 4 ++-- .../features/compose/containers/upload_button_container.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js index 61245acdba9873..8eab532f554fa4 100644 --- a/app/javascript/flavours/glitch/actions/compose.js +++ b/app/javascript/flavours/glitch/actions/compose.js @@ -310,7 +310,7 @@ export function doodleSet(options) { export function uploadCompose(files) { return function (dispatch, getState) { - const uploadLimit = 4; + const uploadLimit = getState().getIn(['server', 'server', 'configuration', 'statuses', 'max_media_attachments']); const media = getState().getIn(['compose', 'media_attachments']); const pending = getState().getIn(['compose', 'pending_media_attachments']); const progress = new Array(files.length).fill(0); @@ -330,7 +330,7 @@ export function uploadCompose(files) { dispatch(uploadComposeRequest()); for (const [i, f] of Array.from(files).entries()) { - if (media.size + i > 3) break; + if (media.size + i > (uploadLimit - 1)) break; resizeImage(f).then(file => { const data = new FormData(); diff --git a/app/javascript/flavours/glitch/components/media_gallery.jsx b/app/javascript/flavours/glitch/components/media_gallery.jsx index ec18f882b64047..5c40e2b22fcad1 100644 --- a/app/javascript/flavours/glitch/components/media_gallery.jsx +++ b/app/javascript/flavours/glitch/components/media_gallery.jsx @@ -311,7 +311,7 @@ class MediaGallery extends PureComponent { render () { const { media, lang, intl, sensitive, letterbox, fullwidth, defaultWidth, autoplay } = this.props; const { visible } = this.state; - const size = media.take(4).size; + const size = media.size; const uncached = media.every(attachment => attachment.get('type') === 'unknown'); const width = this.state.width || defaultWidth; @@ -331,7 +331,7 @@ class MediaGallery extends PureComponent { if (this.isStandaloneEligible()) { children = ; } else { - children = media.take(4).map((attachment, i) => ); + children = media.map((attachment, i) => ); } if (uncached) { diff --git a/app/javascript/flavours/glitch/features/compose/containers/upload_button_container.js b/app/javascript/flavours/glitch/features/compose/containers/upload_button_container.js index 2e835464b85119..39cc12078c5116 100644 --- a/app/javascript/flavours/glitch/features/compose/containers/upload_button_container.js +++ b/app/javascript/flavours/glitch/features/compose/containers/upload_button_container.js @@ -10,7 +10,7 @@ const mapStateToProps = state => { const readyAttachmentsSize = state.getIn(['compose', 'media_attachments']).size ?? 0; const pendingAttachmentsSize = state.getIn(['compose', 'pending_media_attachments']).size ?? 0; const attachmentsSize = readyAttachmentsSize + pendingAttachmentsSize; - const isOverLimit = attachmentsSize > 3; + const isOverLimit = attachmentsSize > state.getIn(['server', 'server', 'configuration', 'statuses', 'max_media_attachments'])-1; const hasVideoOrAudio = state.getIn(['compose', 'media_attachments']).some(m => ['video', 'audio'].includes(m.get('type'))); return { From 0dd4595704896fecb69315c1617dcb74b1cfaf67 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 09:27:13 +0200 Subject: [PATCH 030/101] fix(deps): update dependency glob to v10.4.4 (#30967) 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 87586a916c66ff..e377c52129c097 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9009,8 +9009,8 @@ __metadata: linkType: hard "glob@npm:^10.2.2, glob@npm:^10.2.6, glob@npm:^10.3.10": - version: 10.4.3 - resolution: "glob@npm:10.4.3" + version: 10.4.4 + resolution: "glob@npm:10.4.4" dependencies: foreground-child: "npm:^3.1.0" jackspeak: "npm:^3.1.2" @@ -9020,7 +9020,7 @@ __metadata: path-scurry: "npm:^1.11.1" bin: glob: dist/esm/bin.mjs - checksum: 10c0/bea148e5dae96c17e2764f4764c72376a6ab7072b27a21e861ae4af6f97f3e810d79d67f64de52f63ce1d7fdb73b7306f61c65b48d0f61ca7c8647ce8acaf9a7 + checksum: 10c0/8f0887ae6b9e7ec97841c88f3189643a326c9c37f9881050979c131a2198f2230d4b0a196b71ec6a6694871c25fb8631a72af6e2ea941a667c55f155765546ab languageName: node linkType: hard From 7a30c689530c93e8ea473e39cfb2e2ff6d9d2807 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 9 Jul 2024 03:34:15 -0400 Subject: [PATCH 031/101] Use `scope module: ...` block for `.well-known` routes (#30959) --- config/routes.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index 4b3bd4f187a158..5862731ba98a05 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -63,12 +63,16 @@ def redirect_with_vary(path) tokens: 'oauth/tokens' end - get '.well-known/oauth-authorization-server', to: 'well_known/oauth_metadata#show', as: :oauth_metadata, defaults: { format: 'json' } - get '.well-known/host-meta', to: 'well_known/host_meta#show', as: :host_meta, defaults: { format: 'xml' } - get '.well-known/nodeinfo', to: 'well_known/node_info#index', as: :nodeinfo, defaults: { format: 'json' } - get '.well-known/webfinger', to: 'well_known/webfinger#show', as: :webfinger - get '.well-known/change-password', to: redirect('/auth/edit') - get '.well-known/proxy', to: redirect { |_, request| "/authorize_interaction?#{request.params.to_query}" } + scope path: '.well-known' do + scope module: :well_known do + get 'oauth-authorization-server', to: 'oauth_metadata#show', as: :oauth_metadata, defaults: { format: 'json' } + get 'host-meta', to: 'host_meta#show', as: :host_meta, defaults: { format: 'xml' } + get 'nodeinfo', to: 'node_info#index', as: :nodeinfo, defaults: { format: 'json' } + get 'webfinger', to: 'webfinger#show', as: :webfinger + end + get 'change-password', to: redirect('/auth/edit'), as: nil + get 'proxy', to: redirect { |_, request| "/authorize_interaction?#{request.params.to_query}" }, as: nil + end get '/nodeinfo/2.0', to: 'well_known/node_info#show', as: :nodeinfo_schema From f47d761e12a16c4338447aa8dbeca8365ee812cc Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 9 Jul 2024 03:34:19 -0400 Subject: [PATCH 032/101] Remove unneeded `controller` option in routes (#30958) --- config/routes.rb | 2 +- config/routes/settings.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index 5862731ba98a05..2c06762c229224 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -98,7 +98,7 @@ def redirect_with_vary(path) namespace :auth do resource :setup, only: [:show, :update], controller: :setup - resource :challenge, only: [:create], controller: :challenges + resource :challenge, only: [:create] get 'sessions/security_key_options', to: 'sessions#webauthn_options' post 'captcha_confirmation', to: 'confirmations#confirm_captcha', as: :captcha_confirmation end diff --git a/config/routes/settings.rb b/config/routes/settings.rb index b14606656b680f..297b80942c9b5c 100644 --- a/config/routes/settings.rb +++ b/config/routes/settings.rb @@ -26,9 +26,9 @@ resources :follows, only: :index, controller: :following_accounts resources :blocks, only: :index, controller: :blocked_accounts resources :mutes, only: :index, controller: :muted_accounts - resources :lists, only: :index, controller: :lists + resources :lists, only: :index resources :domain_blocks, only: :index, controller: :blocked_domains - resources :bookmarks, only: :index, controller: :bookmarks + resources :bookmarks, only: :index end resources :two_factor_authentication_methods, only: [:index] do From 249b4117f9fbdac3888d3807a81c0e83e39d7de5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 07:36:13 +0000 Subject: [PATCH 033/101] New Crowdin Translations (automated) (#30970) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/ry.json | 39 +++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/locales/ry.json b/app/javascript/mastodon/locales/ry.json index 67aad910054010..f3a68b15a12a9d 100644 --- a/app/javascript/mastodon/locales/ry.json +++ b/app/javascript/mastodon/locales/ry.json @@ -3,6 +3,8 @@ "about.contact": "Контакт:", "about.disclaimer": "Mastodon є задарьнов проґрамов из удпертым кодом тай торговов значков Mastodon gGmbH.", "about.domain_blocks.no_reason_available": "Причины не ясні", + "about.domain_blocks.preamble": "Майбульш Mastodon поволят вам позирати контент тай комуніковати из хосновачами из другых федерованых серверув. Туй лиш уняткы учинені про сись конкретный сервер.", + "about.domain_blocks.silenced.explanation": "Вы майбульш не будете видіти профілі тай контент из сього сервера, кидь не будете го самі глядати авадь пудпишете ся на нього.", "about.domain_blocks.silenced.title": "Обмежено", "about.domain_blocks.suspended.explanation": "Ниякі податкы из сього сервера не будут уброблені, усокочені ци поміняні, што чинит невозможнов хоть-яку інтеракцію ци зязок из хосновачами из сього сервера.", "about.domain_blocks.suspended.title": "Заблоковано", @@ -20,6 +22,7 @@ "account.browse_more_on_origin_server": "Позирайте бульше на ориґіналнум профілю", "account.cancel_follow_request": "Удмінити пудписку", "account.copy": "Зкопіровати удкликованя на профіл", + "account.direct": "Пошептати @{name}", "account.disable_notifications": "Бульше не сповіщати ми коли {name} пише", "account.domain_blocked": "Домен заблокованый", "account.edit_profile": "Управити профіл", @@ -39,8 +42,10 @@ "account.joined_short": "Датум прикапчованя", "account.languages": "Поміняти убрані языкы", "account.link_verified_on": "Властность сього удкликованя было звірено {date}", + "account.locked_info": "Сись профіл є замкнутый. Ґазда акаунта буде ручно провіряти тко го може зафоловити.", "account.media": "Медіа", - "account.moved_to": "Хосновач {name} указав, ож новый профіл йим є:", + "account.mention": "Спомянути @{name}", + "account.moved_to": "Хосновач {name} указав, ож новый профіл му є:", "account.mute": "Стишити {name}", "account.mute_notifications_short": "Стишити голошіня", "account.mute_short": "Стишити", @@ -60,9 +65,12 @@ "account.unblock_short": "Розблоковати", "account.unendorse": "Не указовати на профілови", "account.unfollow": "Удписати ся", + "account.unmute": "Указовати {name}", "account.unmute_notifications_short": "Указовати голошіня", "account.unmute_short": "Указовати", "account_note.placeholder": "Клопкніт обы додати примітку", + "admin.dashboard.retention.average": "Середньоє", + "admin.dashboard.retention.cohort": "Місяць прикапчованя", "admin.dashboard.retention.cohort_size": "Нові хосновачі", "admin.impact_report.instance_accounts": "Профілі из акаунтув, котрі ся удалят", "admin.impact_report.instance_followers": "Пудписникы, котрых стратят наші хосновачі", @@ -70,11 +78,38 @@ "admin.impact_report.title": "Вплыв цілком", "alert.rate_limited.message": "Попробуйте зась по {retry_time, time, medium}.", "alert.rate_limited.title": "Частота обмежена", + "alert.unexpected.message": "Стала ся нечекана хыба.", + "alert.unexpected.title": "Ийой!", + "announcement.announcement": "Голошіня", + "audio.hide": "Зпрятати звук", + "block_modal.show_less": "Указати менше", + "block_modal.show_more": "Указати бульше", + "block_modal.they_cant_mention": "Они не можут вас споминати авадь слідовати.", + "block_modal.they_cant_see_posts": "Они не можут видіти ваші публикації, тай наспак — вы йихні.", + "block_modal.they_will_know": "Они видят, ож сут заблоковані.", + "block_modal.title": "Заблоковати хосновача?", + "block_modal.you_wont_see_mentions": "Не будете видіти публикації тай споминкы сього хосновача.", + "boost_modal.combo": "Можете клынцнути {combo} другый раз обы сесе пропустити", + "bundle_column_error.copy_stacktrace": "Укопіровати звіт за хыбу", + "bundle_column_error.error.body": "Не годни сьме указати зажадану сторунку. Годно быти спозад хыбы у нашум сістемі, авадь проблемы зумісности бравзера.", + "bundle_column_error.error.title": "Ийой!", + "bundle_column_error.network.body": "Стала ся хыба як сьме пробовали напаровати сторунку. Годно ся йсе было стати спозад слабого споєня вашого інтернета, авадь сервера.", + "bundle_column_error.network.title": "Хыба споєня", + "bundle_column_error.retry": "Попробуйте зась", "bundle_column_error.return": "Вернути ся на головну", "bundle_column_error.routing.body": "Не можеме найти сяку сторунку. Бизувні сьте, ож URL у адресному шорикови є добрый?", "bundle_column_error.routing.title": "404", "bundle_modal_error.close": "Заперти", "bundle_modal_error.message": "Штось ся показило, закидь сьме ладовали сись компонент.", "bundle_modal_error.retry": "Попробовати зась", - "closed_registrations.other_server_instructions": "Mastodon є децентралізованов платформов, можете си учинити профіл и на другому серверови тай комуніковати из сим." + "closed_registrations.other_server_instructions": "Mastodon є децентралізованов платформов, можете си учинити профіл и на другому серверови тай комуніковати из сим.", + "closed_registrations_modal.description": "Раз не мож учинити профіл на {domain}, айбо не мусите мати профіл ипен на серверови {domain} обы хосновати Mastodon.", + "closed_registrations_modal.find_another_server": "Найти другый сервер", + "column.about": "За сайт", + "column.blocks": "Заблоковані хосновачі", + "column.bookmarks": "Усокоченоє", + "column.direct": "Шептаня", + "column.directory": "Никати профілі", + "column.domain_blocks": "Заблоковані домены", + "column.favourites": "Убраноє" } From 7542a134d5fecf82d14dcc1860be87cf6645ec7f Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Tue, 9 Jul 2024 12:47:08 +0200 Subject: [PATCH 034/101] Add a file for Sidekiq to signal it is ready to process jobs (#30971) --- config/initializers/sidekiq.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index 53b02edc40227e..e1d98b8ba3be33 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -2,9 +2,21 @@ require_relative '../../lib/mastodon/sidekiq_middleware' +SIDEKIQ_WILL_PROCESSES_JOBS_FILE = Rails.root.join('tmp', 'sidekiq_process_has_started_and_will_begin_processing_jobs').freeze + Sidekiq.configure_server do |config| config.redis = REDIS_SIDEKIQ_PARAMS + # This is used in Kubernetes setups, to signal that the Sidekiq process has started and will begin processing jobs + # This comes from https://github.com/sidekiq/sidekiq/wiki/Kubernetes#sidekiq + config.on(:startup) do + FileUtils.touch(SIDEKIQ_WILL_PROCESSES_JOBS_FILE) + end + + config.on(:shutdown) do + FileUtils.rm_f(SIDEKIQ_WILL_PROCESSES_JOBS_FILE) + end + config.server_middleware do |chain| chain.add Mastodon::SidekiqMiddleware end From 3875bd138d279eb26efce9f446fe91f49cf38f55 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Tue, 9 Jul 2024 09:41:49 -0300 Subject: [PATCH 035/101] Fix HTTP 500 in `/api/v1/polls/:id/votes` (#25598) --- app/controllers/api/v1/polls/votes_controller.rb | 4 ++-- spec/requests/api/v1/polls/votes_spec.rb | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/v1/polls/votes_controller.rb b/app/controllers/api/v1/polls/votes_controller.rb index 513b937ef2de34..ad1b82cb522c69 100644 --- a/app/controllers/api/v1/polls/votes_controller.rb +++ b/app/controllers/api/v1/polls/votes_controller.rb @@ -8,7 +8,7 @@ class Api::V1::Polls::VotesController < Api::BaseController before_action :set_poll def create - VoteService.new.call(current_account, @poll, vote_params[:choices]) + VoteService.new.call(current_account, @poll, vote_params) render json: @poll, serializer: REST::PollSerializer end @@ -22,6 +22,6 @@ def set_poll end def vote_params - params.permit(choices: []) + params.require(:choices) end end diff --git a/spec/requests/api/v1/polls/votes_spec.rb b/spec/requests/api/v1/polls/votes_spec.rb index e2b22708be8720..669f64b6e4577e 100644 --- a/spec/requests/api/v1/polls/votes_spec.rb +++ b/spec/requests/api/v1/polls/votes_spec.rb @@ -10,9 +10,10 @@ describe 'POST /api/v1/polls/:poll_id/votes' do let(:poll) { Fabricate(:poll) } + let(:params) { { choices: %w(1) } } before do - post "/api/v1/polls/#{poll.id}/votes", params: { choices: %w(1) }, headers: headers + post "/api/v1/polls/#{poll.id}/votes", params: params, headers: headers end it 'creates a vote', :aggregate_failures do @@ -24,6 +25,14 @@ expect(poll.reload.cached_tallies).to eq [0, 1] end + context 'when the required choices param is not provided' do + let(:params) { {} } + + it 'returns http bad request' do + expect(response).to have_http_status(400) + end + end + private def vote From ef2e48e6dac1ed327fc6ac07b0adf252d10ff607 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 14:53:31 +0200 Subject: [PATCH 036/101] fix(deps): update dependency glob to v10.4.5 (#30972) 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 e377c52129c097..85a336a4a348f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9009,8 +9009,8 @@ __metadata: linkType: hard "glob@npm:^10.2.2, glob@npm:^10.2.6, glob@npm:^10.3.10": - version: 10.4.4 - resolution: "glob@npm:10.4.4" + version: 10.4.5 + resolution: "glob@npm:10.4.5" dependencies: foreground-child: "npm:^3.1.0" jackspeak: "npm:^3.1.2" @@ -9020,7 +9020,7 @@ __metadata: path-scurry: "npm:^1.11.1" bin: glob: dist/esm/bin.mjs - checksum: 10c0/8f0887ae6b9e7ec97841c88f3189643a326c9c37f9881050979c131a2198f2230d4b0a196b71ec6a6694871c25fb8631a72af6e2ea941a667c55f155765546ab + checksum: 10c0/19a9759ea77b8e3ca0a43c2f07ecddc2ad46216b786bb8f993c445aee80d345925a21e5280c7b7c6c59e860a0154b84e4b2b60321fea92cd3c56b4a7489f160e languageName: node linkType: hard From 967505ee9bcacf0e5189aa06c654ff586c198a46 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Tue, 9 Jul 2024 15:11:34 +0200 Subject: [PATCH 037/101] Add size limit for all PreviewCard URLs (#30973) --- app/models/preview_card.rb | 7 +++++- app/services/fetch_link_card_service.rb | 7 ++---- spec/fixtures/requests/long_canonical_url.txt | 18 +++++++++++++++ spec/services/fetch_link_card_service_spec.rb | 22 ++++++++----------- 4 files changed, 35 insertions(+), 19 deletions(-) create mode 100644 spec/fixtures/requests/long_canonical_url.txt diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb index eac02ac14ff05c..5a11351e58808a 100644 --- a/app/models/preview_card.rb +++ b/app/models/preview_card.rb @@ -46,6 +46,11 @@ class PreviewCard < ApplicationRecord y_comp: 4, }.freeze + # URL size limit to safely store in PosgreSQL's unique indexes + # Technically this is a byte-size limit but we use it as a + # character limit to work with length validation + URL_CHARACTER_LIMIT = 2692 + self.inheritance_column = false enum :type, { link: 0, photo: 1, video: 2, rich: 3 } @@ -63,7 +68,7 @@ class PreviewCard < ApplicationRecord convert_options: { all: '-quality 90 +profile "!icc,*" +set date:modify +set date:create +set date:timestamp' }, validate_media_type: false - validates :url, presence: true, uniqueness: true, url: true + validates :url, presence: true, uniqueness: true, url: true, length: { maximum: URL_CHARACTER_LIMIT } validates_attachment_content_type :image, content_type: IMAGE_MIME_TYPES validates_attachment_size :image, less_than: LIMIT remotable_attachment :image, LIMIT diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb index 436e024c99bc3d..adabb1096e86eb 100644 --- a/app/services/fetch_link_card_service.rb +++ b/app/services/fetch_link_card_service.rb @@ -15,9 +15,6 @@ class FetchLinkCardService < BaseService ) }iox - # URL size limit to safely store in PosgreSQL's unique indexes - BYTESIZE_LIMIT = 2692 - def call(status) @status = status @original_url = parse_urls @@ -32,7 +29,7 @@ def call(status) end attach_card if @card&.persisted? - rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, EncodingError => e + rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, EncodingError, ActiveRecord::RecordInvalid => e Rails.logger.debug { "Error fetching link #{@original_url}: #{e}" } nil end @@ -88,7 +85,7 @@ def parse_urls def bad_url?(uri) # Avoid local instance URLs and invalid URLs - uri.host.blank? || TagManager.instance.local_url?(uri.to_s) || !%w(http https).include?(uri.scheme) || uri.to_s.bytesize > BYTESIZE_LIMIT + uri.host.blank? || TagManager.instance.local_url?(uri.to_s) || !%w(http https).include?(uri.scheme) end def mention_link?(anchor) diff --git a/spec/fixtures/requests/long_canonical_url.txt b/spec/fixtures/requests/long_canonical_url.txt new file mode 100644 index 00000000000000..97d6c93961fb47 --- /dev/null +++ b/spec/fixtures/requests/long_canonical_url.txt @@ -0,0 +1,18 @@ +HTTP/1.1 200 OK +server: nginx +date: Thu, 13 Jun 2024 14:33:13 GMT +content-type: text/html; charset=utf-8 +content-length: 3225 +accept-ranges: bytes + + + + + + + Very long canonical URL + + +

We have very long URLs

+ + diff --git a/spec/services/fetch_link_card_service_spec.rb b/spec/services/fetch_link_card_service_spec.rb index b2cd99cea6d25d..342902cdb3d894 100644 --- a/spec/services/fetch_link_card_service_spec.rb +++ b/spec/services/fetch_link_card_service_spec.rb @@ -31,6 +31,7 @@ stub_request(:get, 'http://example.com/latin1_posing_as_utf8_recoverable').to_return(request_fixture('latin1_posing_as_utf8_recoverable.txt')) stub_request(:get, 'http://example.com/aergerliche-umlaute').to_return(request_fixture('redirect_with_utf8_url.txt')) stub_request(:get, 'http://example.com/page_without_title').to_return(request_fixture('page_without_title.txt')) + stub_request(:get, 'http://example.com/long_canonical_url').to_return(request_fixture('long_canonical_url.txt')) Rails.cache.write('oembed_endpoint:example.com', oembed_cache) if oembed_cache @@ -233,19 +234,6 @@ end end - context 'with an URL too long for PostgreSQL unique indexes' do - let(:url) { "http://example.com/#{'a' * 2674}" } - let(:status) { Fabricate(:status, text: url) } - - it 'does not fetch the URL' do - expect(a_request(:get, url)).to_not have_been_made - end - - it 'does not create a preview card' do - expect(status.preview_card).to be_nil - end - end - context 'with a URL of a page with oEmbed support' do let(:html) { 'Hello world' } let(:status) { Fabricate(:status, text: 'http://example.com/html') } @@ -296,6 +284,14 @@ end end end + + context 'with a URL of a page that includes a canonical URL too long for PostgreSQL unique indexes' do + let(:status) { Fabricate(:status, text: 'test http://example.com/long_canonical_url') } + + it 'does not create a preview card' do + expect(status.preview_card).to be_nil + end + end end context 'with a remote status' do From c9f08f77ed8a262a4db806d814e2701794873a70 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 08:55:26 +0200 Subject: [PATCH 038/101] fix(deps): update dependency sass to v1.77.7 (#30981) 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 85a336a4a348f3..cf7e5a8b952202 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15462,15 +15462,15 @@ __metadata: linkType: hard "sass@npm:^1.62.1": - version: 1.77.6 - resolution: "sass@npm:1.77.6" + version: 1.77.7 + resolution: "sass@npm:1.77.7" dependencies: chokidar: "npm:>=3.0.0 <4.0.0" immutable: "npm:^4.0.0" source-map-js: "npm:>=0.6.2 <2.0.0" bin: sass: sass.js - checksum: 10c0/fe5a393c0aa29eda9f83c06be9b94788b61fe8bad0616ee6e3a25d21ab504f430d40c0064fdca89b02b8e426411ae6dcd906c91f2e48c263575c3d392b6daeb1 + checksum: 10c0/6cacbf4b5165d30a9be0f09438aed85ff0617e5087442e65c23c8464750ff1b9988855a58f36b420b62f992d1e82403f99810aba7731519d3d026847e21a5635 languageName: node linkType: hard From d0137910c61a565cfdaa8cf29d3e15e6345e5066 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 12:04:11 +0200 Subject: [PATCH 039/101] chore(deps): update dependency ruby to v3.3.4 (#30969) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .ruby-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ruby-version b/.ruby-version index 619b537668489e..a0891f563f38b0 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.3.3 +3.3.4 From 8dea897a3b2a6be08911515d291eb679dc3cd792 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:04:52 +0000 Subject: [PATCH 040/101] chore(deps): update docker.io/ruby docker tag to v3.3.4 (#30980) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 7f7eca06dac040..52d3758dc67cf7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ ARG BUILDPLATFORM=${BUILDPLATFORM} # Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.3.x"] # renovate: datasource=docker depName=docker.io/ruby -ARG RUBY_VERSION="3.3.3" +ARG RUBY_VERSION="3.3.4" # # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"] # renovate: datasource=node-version depName=node ARG NODE_MAJOR_VERSION="20" From 9984fca9a065e49acde0858487bc3cf10a744cea Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 12:40:19 +0200 Subject: [PATCH 041/101] New Crowdin Translations (automated) (#30983) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/cs.json | 16 +++++----- app/javascript/mastodon/locales/ry.json | 41 ++++++++++++++++++++++++- config/locales/cs.yml | 2 +- config/locales/doorkeeper.lv.yml | 6 ++-- config/locales/ry.yml | 20 ++++++++++++ 5 files changed, 73 insertions(+), 12 deletions(-) diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json index e96e283970f0ae..12de5d5ecd4794 100644 --- a/app/javascript/mastodon/locales/cs.json +++ b/app/javascript/mastodon/locales/cs.json @@ -197,7 +197,7 @@ "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", + "directory.federated": "Ze známého fediversu", "directory.local": "Pouze z {domain}", "directory.new_arrivals": "Nově příchozí", "directory.recently_active": "Nedávno aktivní", @@ -213,7 +213,7 @@ "domain_block_modal.block_account_instead": "Raději blokovat @{name}", "domain_block_modal.they_can_interact_with_old_posts": "Lidé z tohoto serveru mohou interagovat s vašimi starými příspěvky.", "domain_block_modal.they_cant_follow": "Nikdo z tohoto serveru vás nemůže sledovat.", - "domain_block_modal.they_wont_know": "Nebude vědět, že je zablokován.", + "domain_block_modal.they_wont_know": "Nebude vědět, že je zablokován*a.", "domain_block_modal.title": "Blokovat doménu?", "domain_block_modal.you_will_lose_followers": "Všichni vaši sledující z tohoto serveru budou odstraněni.", "domain_block_modal.you_wont_see_posts": "Neuvidíte příspěvky ani upozornění od uživatelů z tohoto serveru.", @@ -341,7 +341,7 @@ "hashtag.column_settings.tag_mode.any": "Jakýkoliv z těchto", "hashtag.column_settings.tag_mode.none": "Žádný z těchto", "hashtag.column_settings.tag_toggle": "Zahrnout v tomto sloupci další štítky", - "hashtag.counter_by_accounts": "{count, plural, one {{counter} účastník} few {{counter} účastníci} other {{counter} účastníků}}", + "hashtag.counter_by_accounts": "{count, plural, one {{counter} účastník*ice} few {{counter} účastníci} other {{counter} účastníků}}", "hashtag.counter_by_uses": "{count, plural, one {{counter} příspěvek} few {{counter} příspěvky} other {{counter} příspěvků}}", "hashtag.counter_by_uses_today": "Dnes {count, plural, one {{counter} příspěvek} few {{counter} příspěvky} other {{counter} příspěvků}}", "hashtag.follow": "Sledovat hashtag", @@ -440,7 +440,7 @@ "mute_modal.show_options": "Zobrazit možnosti", "mute_modal.they_can_mention_and_follow": "Mohou vás zmínit a sledovat, ale neuvidíte je.", "mute_modal.they_wont_know": "Nebudou vědět, že byli skryti.", - "mute_modal.title": "Ztlumit uživatele?", + "mute_modal.title": "Ztlumit uživatele*ku?", "mute_modal.you_wont_see_mentions": "Neuvidíte příspěvky, které je zmiňují.", "mute_modal.you_wont_see_posts": "Stále budou moci vidět vaše příspěvky, ale vy jejich neuvidíte.", "navigation_bar.about": "O aplikaci", @@ -566,8 +566,8 @@ "onboarding.share.message": "Jsem {username} na #Mastodonu! Pojď mě sledovat na {url}", "onboarding.share.next_steps": "Možné další kroky:", "onboarding.share.title": "Sdílejte svůj profil", - "onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:", - "onboarding.start.skip": "Want to skip right ahead?", + "onboarding.start.lead": "Nyní jste součástí Mastodonu, unikátní sociální sítě, kde vy - ne algoritmus - vytváří vaše vlastní prožitky. Začněte na této nové sociální platformě:", + "onboarding.start.skip": "Nepotřebujete pomoci začít?", "onboarding.start.title": "Dokázali jste to!", "onboarding.steps.follow_people.body": "Mastodon je o sledování zajimavých lidí.", "onboarding.steps.follow_people.title": "Přispůsobit vlastní domovský kanál", @@ -581,7 +581,7 @@ "onboarding.tips.accounts_from_other_servers": "Víte, že? Protože je Mastodon decentralizovaný, některé profily, na které narazíte, budou hostovány na jiných serverech, než je ten váš. A přesto s nimi můžete bezproblémově komunikovat! Jejich server se nachází v druhé polovině uživatelského jména!", "onboarding.tips.migration": "Víte, že? Pokud máte pocit, že {domain} pro vás v budoucnu není vhodnou volbou, můžete se přesunout na jiný Mastodon server, aniž byste přišli o své sledující. Můžete dokonce hostovat svůj vlastní server!", "onboarding.tips.verification": "Víte, že? Svůj účet můžete ověřit tak, že na své webové stránky umístíte odkaz na váš Mastodon profil a odkaz na stránku přidáte do svého profilu. Nejsou k tomu potřeba žádné poplatky ani dokumenty!", - "password_confirmation.exceeds_maxlength": "Potvrzení hesla překračuje maximální délku hesla", + "password_confirmation.exceeds_maxlength": "Potvrzení hesla překračuje maximální povolenou délku hesla", "password_confirmation.mismatching": "Zadaná hesla se neshodují", "picture_in_picture.restore": "Vrátit zpět", "poll.closed": "Uzavřeno", @@ -665,7 +665,7 @@ "report.unfollow": "Přestat sledovat @{name}", "report.unfollow_explanation": "Tento účet sledujete. Abyste už neviděli jeho příspěvky ve své domovské časové ose, přestaňte jej sledovat.", "report_notification.attached_statuses": "{count, plural, one {{count} připojený příspěvek} few {{count} připojené příspěvky} many {{count} připojených příspěvků} other {{count} připojených příspěvků}}", - "report_notification.categories.legal": "Zákonné", + "report_notification.categories.legal": "Právní ustanovení", "report_notification.categories.other": "Ostatní", "report_notification.categories.spam": "Spam", "report_notification.categories.violation": "Porušení pravidla", diff --git a/app/javascript/mastodon/locales/ry.json b/app/javascript/mastodon/locales/ry.json index f3a68b15a12a9d..4f2e2410eff4a2 100644 --- a/app/javascript/mastodon/locales/ry.json +++ b/app/javascript/mastodon/locales/ry.json @@ -82,6 +82,7 @@ "alert.unexpected.title": "Ийой!", "announcement.announcement": "Голошіня", "audio.hide": "Зпрятати звук", + "block_modal.remote_users_caveat": "Попросиме ґазду сервера {domain} честовати вашоє рішеня. Айбо не ґарантуєме повный соглас, бо даякі серверы можут брати блокованя по-инчакому. Публичні дописы годно быти видко незалоґованым хосновачам.", "block_modal.show_less": "Указати менше", "block_modal.show_more": "Указати бульше", "block_modal.they_cant_mention": "Они не можут вас споминати авадь слідовати.", @@ -111,5 +112,43 @@ "column.direct": "Шептаня", "column.directory": "Никати профілі", "column.domain_blocks": "Заблоковані домены", - "column.favourites": "Убраноє" + "column.favourites": "Убраноє", + "column.follow_requests": "Запросы на пудписку", + "column.lists": "Исписы", + "column.mutes": "Стишені хосновачі", + "column.notifications": "Убвіщеня", + "column.pins": "Закріплені публикації", + "column_back_button.label": "Назад", + "column_header.hide_settings": "Спрятати штімованя", + "column_header.moveLeft_settings": "Посунути колонку до ліва", + "column_header.moveRight_settings": "Посунути колонку до права", + "column_header.pin": "Закріпити", + "column_header.show_settings": "Указати штімованя", + "column_header.unpin": "Удкріпити", + "column_subheading.settings": "Штімованя", + "compose.language.change": "Поміняти язык", + "compose.language.search": "Глядати языкы...", + "compose.published.body": "Пост опубликованый.", + "compose.saved.body": "Пост усокоченый.", + "compose_form.direct_message_warning_learn_more": "Читайте бульше", + "compose_form.encryption_warning": "Публикації на Mastodon не шіфрувут ся. Не шырьте чутливу інформацію через Mastodon.", + "compose_form.hashtag_warning": "Сись пост не буде ся появляти у исписови по гештеґови, бо вун не є публичный. Лишек публичні посты буде видко за гештеґом.", + "compose_form.lock_disclaimer": "Ваш профіл є {locked}. Хоть-тко може ся на вас пудписати, обы видїти ваші ексклузівні посты.", + "compose_form.lock_disclaimer.lock": "замкнено", + "compose_form.placeholder": "Што нового?", + "compose_form.poll.duration": "Трывалость убзвідованя", + "compose_form.poll.multiple": "Дакулько варіантув", + "compose_form.poll.option_placeholder": "Варіант {number}", + "compose_form.poll.single": "Уберіт єден", + "compose_form.poll.switch_to_multiple": "Змінити убзвідованя обы поволити дакулько варіантув", + "compose_form.poll.switch_to_single": "Змінити убзвідованя обы поволити лишек єден варіант", + "compose_form.poll.type": "Стіл", + "compose_form.publish": "Публикація", + "compose_form.publish_form": "Нова публикація", + "compose_form.reply": "Удповідь", + "copypaste.copy_to_clipboard": "Копіровати у памнять", + "directory.recently_active": "Недавно актівні", + "disabled_account_banner.account_settings": "Штімованя акаунта", + "disabled_account_banner.text": "Ваш акаунт {disabledAccount} раз є неактівный.", + "dismissable_banner.community_timeline": "Туй сут недавні публикації уд профілув на серверови {domain}." } diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 20e7e4d46b81d5..7e0aaaeefb6fe3 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -118,7 +118,7 @@ cs: promote: Povýšit protocol: Protokol public: Veřejný - push_subscription_expires: Odebírání PuSH expiruje + push_subscription_expires: Odebírání PuSH vyprší redownload: Obnovit profil redownloaded_msg: Profil účtu %{username} byl úspěšně obnoven ze zdroje reject: Zamítnout diff --git a/config/locales/doorkeeper.lv.yml b/config/locales/doorkeeper.lv.yml index 5aa5daef3f6d16..11c50203050e4c 100644 --- a/config/locales/doorkeeper.lv.yml +++ b/config/locales/doorkeeper.lv.yml @@ -25,7 +25,7 @@ lv: edit: Labot submit: Apstiprināt confirmations: - destroy: Vai esi pārliecināts? + destroy: Vai tiešām? edit: title: Labot lietotni form: @@ -69,7 +69,7 @@ lv: buttons: revoke: Atsaukt confirmations: - revoke: Vai esi pārliecināts? + revoke: Vai tiešām? index: authorized_at: Autorizētas %{date} description_html: Šīs ir lietotnes, kas var piekļūt Tavam kontam ar API. Ja šeit ir lietotnes, kuras neatpazīsti, vai lietotne darbojas ne tā, kā paredzēts, vari atsaukt tās piekļuvi. @@ -135,6 +135,7 @@ lv: media: Multividesu pielikumi mutes: Apklusinātie notifications: Paziņojumi + profile: Tavs Mastodon profils push: Uznirstošie paziņojumi reports: Ziņojumi search: Meklēt @@ -165,6 +166,7 @@ lv: admin:write:reports: veikt moderācijas darbības pārskatos crypto: lieto pilnīgu šifrēšanu follow: mainīt konta attiecības + profile: lasīt tikai Tava konta profila informāciju push: saņemt savus push paziņojumus read: lasīt visus sava konta datus read:accounts: apskatīt kontu informāciju diff --git a/config/locales/ry.yml b/config/locales/ry.yml index 6fe57b65cdb30e..e384b7f1b74810 100644 --- a/config/locales/ry.yml +++ b/config/locales/ry.yml @@ -1 +1,21 @@ +--- ry: + accounts: + follow: Пудписати ся + following: Пудпискы + posts: + few: Публикації + one: Публикація + other: Публикації + posts_tab_heading: Публикації + imports: + titles: + following: Імпортованя пудписок + types: + following: Испис пудписок + notification_mailer: + follow: + body: "%{name} ся пудписує ся на вас!" + subject: "%{name} ся пудписує ся на вас" + relationships: + following: Пудпискы From 182b9248c0e798e4b0fba8339d2817ed2f094b42 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Wed, 10 Jul 2024 14:23:24 +0200 Subject: [PATCH 042/101] Fix SCSS mixed declarations deprecation (#30986) --- app/javascript/styles/mastodon/components.scss | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index c114885d171043..da86a4caa71aac 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1366,6 +1366,8 @@ body > [data-popper-placement] { min-height: 54px; border-bottom: 1px solid var(--background-border-color); cursor: auto; + opacity: 1; + animation: fade 150ms linear; @keyframes fade { 0% { @@ -1377,9 +1379,6 @@ body > [data-popper-placement] { } } - opacity: 1; - animation: fade 150ms linear; - .media-gallery, .video-player, .audio-player, @@ -4851,8 +4850,10 @@ a.status-card { &__menu { @include search-popout; - padding: 0; - background: $ui-secondary-color; + & { + padding: 0; + background: $ui-secondary-color; + } } &__menu-list { From 36592d10aa497db6c9a9764ee539242cc2cfdec7 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Wed, 10 Jul 2024 14:57:25 +0200 Subject: [PATCH 043/101] Change Sidekiq readiness file to use an environment variable (#30988) --- Dockerfile | 4 +++- config/initializers/sidekiq.rb | 19 ++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 52d3758dc67cf7..758db9bcc90b56 100644 --- a/Dockerfile +++ b/Dockerfile @@ -67,7 +67,9 @@ ENV \ # Optimize jemalloc 5.x performance MALLOC_CONF="narenas:2,background_thread:true,thp:never,dirty_decay_ms:1000,muzzy_decay_ms:0" \ # Enable libvips, should not be changed - MASTODON_USE_LIBVIPS=true + MASTODON_USE_LIBVIPS=true \ +# Sidekiq will touch tmp/sidekiq_process_has_started_and_will_begin_processing_jobs to indicate it is ready. This can be used for a readiness check in Kubernetes + MASTODON_SIDEKIQ_READY_FILENAME=sidekiq_process_has_started_and_will_begin_processing_jobs # Set default shell used for running commands SHELL ["/bin/bash", "-o", "pipefail", "-o", "errexit", "-c"] diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index e1d98b8ba3be33..5b2f317bf2efc1 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -2,19 +2,24 @@ require_relative '../../lib/mastodon/sidekiq_middleware' -SIDEKIQ_WILL_PROCESSES_JOBS_FILE = Rails.root.join('tmp', 'sidekiq_process_has_started_and_will_begin_processing_jobs').freeze - Sidekiq.configure_server do |config| config.redis = REDIS_SIDEKIQ_PARAMS # This is used in Kubernetes setups, to signal that the Sidekiq process has started and will begin processing jobs # This comes from https://github.com/sidekiq/sidekiq/wiki/Kubernetes#sidekiq - config.on(:startup) do - FileUtils.touch(SIDEKIQ_WILL_PROCESSES_JOBS_FILE) - end + ready_filename = ENV.fetch('MASTODON_SIDEKIQ_READY_FILENAME', nil) + if ready_filename + raise 'MASTODON_SIDEKIQ_READY_FILENAME is not a valid filename' if File.basename(ready_filename) != ready_filename + + ready_path = Rails.root.join('tmp', ready_filename) - config.on(:shutdown) do - FileUtils.rm_f(SIDEKIQ_WILL_PROCESSES_JOBS_FILE) + config.on(:startup) do + FileUtils.touch(ready_path) + end + + config.on(:shutdown) do + FileUtils.rm_f(ready_path) + end end config.server_middleware do |chain| From 2ea9336b6855b44126a55106a81e96ea68eaf8ff Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Wed, 10 Jul 2024 16:25:39 +0200 Subject: [PATCH 044/101] Do not pass unknown encoding names to nokogiri. (#30987) --- app/lib/link_details_extractor.rb | 9 ++++++++- .../alternative_utf8_spelling_in_header.txt | 18 ++++++++++++++++++ spec/services/fetch_link_card_service_spec.rb | 9 +++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 spec/fixtures/requests/alternative_utf8_spelling_in_header.txt diff --git a/app/lib/link_details_extractor.rb b/app/lib/link_details_extractor.rb index 72992d2f42ad80..d81f4a30622dec 100644 --- a/app/lib/link_details_extractor.rb +++ b/app/lib/link_details_extractor.rb @@ -274,7 +274,7 @@ def document end def detect_encoding_and_parse_document - [detect_encoding, nil, @html_charset].uniq.each do |encoding| + [detect_encoding, nil, header_encoding].uniq.each do |encoding| document = Nokogiri::HTML(@html, nil, encoding) return document if document.to_s.valid_encoding? end @@ -286,6 +286,13 @@ def detect_encoding guess&.fetch(:confidence, 0).to_i > 60 ? guess&.fetch(:encoding, nil) : nil end + def header_encoding + Encoding.find(@html_charset).name if @html_charset + rescue ArgumentError + # Encoding from HTTP header is not recognized by ruby + nil + end + def detector @detector ||= CharlockHolmes::EncodingDetector.new.tap do |detector| detector.strip_tags = true diff --git a/spec/fixtures/requests/alternative_utf8_spelling_in_header.txt b/spec/fixtures/requests/alternative_utf8_spelling_in_header.txt new file mode 100644 index 00000000000000..7aaea370e86230 --- /dev/null +++ b/spec/fixtures/requests/alternative_utf8_spelling_in_header.txt @@ -0,0 +1,18 @@ +HTTP/1.1 200 OK +server: nginx +date: Thu, 13 Jun 2024 14:33:13 GMT +content-type: text/html; charset=utf8 +content-length: 192 +accept-ranges: bytes + + + + + + Webserver Configs R Us + + +

Welcome

+

Sneaky non-UTF character:

+ + diff --git a/spec/services/fetch_link_card_service_spec.rb b/spec/services/fetch_link_card_service_spec.rb index 342902cdb3d894..2f64f405583955 100644 --- a/spec/services/fetch_link_card_service_spec.rb +++ b/spec/services/fetch_link_card_service_spec.rb @@ -32,6 +32,7 @@ stub_request(:get, 'http://example.com/aergerliche-umlaute').to_return(request_fixture('redirect_with_utf8_url.txt')) stub_request(:get, 'http://example.com/page_without_title').to_return(request_fixture('page_without_title.txt')) stub_request(:get, 'http://example.com/long_canonical_url').to_return(request_fixture('long_canonical_url.txt')) + stub_request(:get, 'http://example.com/alternative_utf8_spelling_in_header').to_return(request_fixture('alternative_utf8_spelling_in_header.txt')) Rails.cache.write('oembed_endpoint:example.com', oembed_cache) if oembed_cache @@ -292,6 +293,14 @@ expect(status.preview_card).to be_nil end end + + context 'with a URL where the `Content-Type` header uses `utf8` instead of `utf-8`' do + let(:status) { Fabricate(:status, text: 'test http://example.com/alternative_utf8_spelling_in_header') } + + it 'does not create a preview card' do + expect(status.preview_card.title).to eq 'Webserver Configs R Us' + end + end end context 'with a remote status' do From 0d4a8a53d548aa4db43e52085f85126c2bf543d1 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Wed, 10 Jul 2024 14:23:24 +0200 Subject: [PATCH 045/101] [Glitch] Fix SCSS mixed declarations deprecation Port 182b9248c0e798e4b0fba8339d2817ed2f094b42 to glitch-soc --- app/javascript/flavours/glitch/styles/components.scss | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/javascript/flavours/glitch/styles/components.scss b/app/javascript/flavours/glitch/styles/components.scss index 0f78aa45dc766f..533a564e99687e 100644 --- a/app/javascript/flavours/glitch/styles/components.scss +++ b/app/javascript/flavours/glitch/styles/components.scss @@ -1430,6 +1430,8 @@ body > [data-popper-placement] { min-height: 54px; border-bottom: 1px solid var(--background-border-color); cursor: auto; + opacity: 1; + animation: fade 150ms linear; @keyframes fade { 0% { @@ -1441,9 +1443,6 @@ body > [data-popper-placement] { } } - opacity: 1; - animation: fade 150ms linear; - .media-gallery, .video-player, .audio-player, @@ -5199,8 +5198,10 @@ a.status-card { &__menu { @include search-popout; - padding: 0; - background: $ui-secondary-color; + & { + padding: 0; + background: $ui-secondary-color; + } } &__menu-list { From 959ad1d6be5b84bd55df4e7ad04cc94e824fce5c Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 10 Jul 2024 17:46:33 +0200 Subject: [PATCH 046/101] Fix deprecation warning in win95 theme --- app/javascript/styles/win95.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/javascript/styles/win95.scss b/app/javascript/styles/win95.scss index 66d451303ab64b..2302dc40d5b6f7 100644 --- a/app/javascript/styles/win95.scss +++ b/app/javascript/styles/win95.scss @@ -2503,6 +2503,7 @@ body { background: $win95-tooltip-yellow; border: 1px solid black; padding: 4px; + margin-bottom: 24px; h1, h1 small { color:black; @@ -2510,8 +2511,6 @@ body { text-overflow: unset; } - margin-bottom: 24px; - &:after { content: ""; display:block; From d436696f46814eb4c786e48dec6dd05b4bb5c278 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Wed, 10 Jul 2024 17:59:18 +0200 Subject: [PATCH 047/101] Streaming: Fix incorrect type definitions (#30977) --- streaming/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/streaming/index.js b/streaming/index.js index 65a63bb114326a..2ba8b1540781b4 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -1107,7 +1107,7 @@ const startServer = async () => { /** * @param {http.IncomingMessage} req - * @param {WebSocket} ws + * @param {import('ws').WebSocket} ws * @param {string[]} streamName * @returns {function(string, string): void} */ @@ -1324,7 +1324,7 @@ const startServer = async () => { /** * @typedef WebSocketSession - * @property {WebSocket & { isAlive: boolean}} websocket + * @property {import('ws').WebSocket & { isAlive: boolean}} websocket * @property {http.IncomingMessage & ResolvedAccount} request * @property {import('pino').Logger} logger * @property {Object.} subscriptions @@ -1450,7 +1450,7 @@ const startServer = async () => { }; /** - * @param {WebSocket & { isAlive: boolean }} ws + * @param {import('ws').WebSocket & { isAlive: boolean }} ws * @param {http.IncomingMessage & ResolvedAccount} req * @param {import('pino').Logger} log */ From 182fee1c454dcd71ecf408ddc2c534bf6ee3a49a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 18:18:15 +0200 Subject: [PATCH 048/101] New Crowdin Translations (automated) (#2738) * New Crowdin translations * Fix bogus no.yml * Fix bogus simple_form.no.yml --------- Co-authored-by: GitHub Actions Co-authored-by: Claire --- app/javascript/flavours/glitch/locales/de.json | 3 +-- app/javascript/flavours/glitch/locales/es-AR.json | 3 +-- app/javascript/flavours/glitch/locales/ko.json | 5 +++++ app/javascript/flavours/glitch/locales/zh-CN.json | 3 +-- app/javascript/flavours/glitch/locales/zh-TW.json | 3 +-- config/locales-glitch/simple_form.ko.yml | 2 ++ 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/javascript/flavours/glitch/locales/de.json b/app/javascript/flavours/glitch/locales/de.json index caad1ebcfe1ebe..6a20c8c396fda3 100644 --- a/app/javascript/flavours/glitch/locales/de.json +++ b/app/javascript/flavours/glitch/locales/de.json @@ -154,6 +154,5 @@ "status.in_reply_to": "Dieser Toot ist eine Antwort", "status.is_poll": "Dieser Toot ist eine Umfrage", "status.local_only": "Nur auf deiner Instanz sichtbar", - "status.uncollapse": "Ausklappen", - "suggestions.dismiss": "Vorschlag ablehnen" + "status.uncollapse": "Ausklappen" } diff --git a/app/javascript/flavours/glitch/locales/es-AR.json b/app/javascript/flavours/glitch/locales/es-AR.json index 95dc7c85a6fdaf..706109d6e86fd5 100644 --- a/app/javascript/flavours/glitch/locales/es-AR.json +++ b/app/javascript/flavours/glitch/locales/es-AR.json @@ -155,6 +155,5 @@ "status.in_reply_to": "Esta publicación es una respuesta", "status.is_poll": "Esta publicación es una encuesta", "status.local_only": "Sólo visible para tu instancia", - "status.uncollapse": "Descolapsar", - "suggestions.dismiss": "Descartar sugerencia" + "status.uncollapse": "Descolapsar" } diff --git a/app/javascript/flavours/glitch/locales/ko.json b/app/javascript/flavours/glitch/locales/ko.json index b1da79d102e65c..18e61dc03418eb 100644 --- a/app/javascript/flavours/glitch/locales/ko.json +++ b/app/javascript/flavours/glitch/locales/ko.json @@ -25,6 +25,9 @@ "compose.content-type.plain_meta": "고급 양식 없이 작성", "compose.disable_threaded_mode": "글타래 모드 비활성화", "compose.enable_threaded_mode": "글타래 모드 활성화", + "compose_form.sensitive.hide": "{count, plural, other {미디어를 민감함으로 표시}}", + "compose_form.sensitive.marked": "{count, plural, other {미디어가 민감함으로 표시되었습니다}}", + "compose_form.sensitive.unmarked": "{count, plural, other {미디어가 민감함으로 표시되지 않았습니다}}", "confirmation_modal.do_not_ask_again": "다음부터 확인창을 띄우지 않기", "confirmations.deprecated_settings.confirm": "마스토돈 설정 사용", "confirmations.deprecated_settings.message": "사용하던 몇몇 기기별 글리치 {app_settings}은 마스토돈 {preferences}으로 대체되었습니다:", @@ -61,6 +64,7 @@ "notification_purge.btn_invert": "선택반전", "notification_purge.btn_none": "전체선택해제", "notification_purge.start": "알림 삭제모드로 들어가기", + "notifications.column_settings.filter_bar.show_bar": "필터 막대 표시", "notifications.marked_clear": "선택된 알림 모두 삭제", "notifications.marked_clear_confirmation": "정말로 선택된 알림들을 영구적으로 삭제할까요?", "settings.always_show_spoilers_field": "열람주의 항목을 언제나 활성화", @@ -124,6 +128,7 @@ "settings.shared_settings_link": "사용자 설정", "settings.show_action_bar": "접힌 글에 액션 버튼들 보이기", "settings.show_content_type_choice": "글을 작성할 때 콘텐트 타입을 고를 수 있도록 합니다", + "settings.show_published_toast": "게시물을 게시/저장할 때 토스트 표시", "settings.show_reply_counter": "대략적인 답글 개수를 표시합니다", "settings.side_arm": "보조 작성 버튼:", "settings.side_arm.none": "없음", diff --git a/app/javascript/flavours/glitch/locales/zh-CN.json b/app/javascript/flavours/glitch/locales/zh-CN.json index 26f142807abd88..e9999136fb97b7 100644 --- a/app/javascript/flavours/glitch/locales/zh-CN.json +++ b/app/javascript/flavours/glitch/locales/zh-CN.json @@ -155,6 +155,5 @@ "status.in_reply_to": "此嘟文是回复", "status.is_poll": "此嘟文是投票", "status.local_only": "此嘟文仅本站可见", - "status.uncollapse": "展开", - "suggestions.dismiss": "关闭建议" + "status.uncollapse": "展开" } diff --git a/app/javascript/flavours/glitch/locales/zh-TW.json b/app/javascript/flavours/glitch/locales/zh-TW.json index 2fcb217434afd4..aaf00651506c40 100644 --- a/app/javascript/flavours/glitch/locales/zh-TW.json +++ b/app/javascript/flavours/glitch/locales/zh-TW.json @@ -151,6 +151,5 @@ "status.in_reply_to": "貼文有回覆", "status.is_poll": "貼文有投票", "status.local_only": "只在此實例可見", - "status.uncollapse": "展開", - "suggestions.dismiss": "關閉建議" + "status.uncollapse": "展開" } diff --git a/config/locales-glitch/simple_form.ko.yml b/config/locales-glitch/simple_form.ko.yml index f9420f1336dafa..96e13201419de7 100644 --- a/config/locales-glitch/simple_form.ko.yml +++ b/config/locales-glitch/simple_form.ko.yml @@ -8,6 +8,7 @@ ko: setting_default_content_type_markdown: 게시물을 작성할 때, 형식을 지정하지 않았다면, 마크다운이라고 가정합니다 setting_default_content_type_plain: 게시물을 작성할 때, 형식을 지정하지 않았다면, 일반적인 텍스트라고 가정합니다. (마스토돈의 기본 동작) setting_default_language: 작성하는 게시물의 언어는 자동으로 설정될 수 있습니다, 하지만 언제나 정확하지는 않습니다 + setting_show_followers_count: 팔로워 카운트를 프로필에서 숨깁니다. 팔로워 수를 숨기면 나에게도 보이지 않으며 몇몇 앱에서는 팔로워 수가 음수로 표시될 수 있습니다. setting_skin: 선택한 마스토돈 풍미의 스킨을 바꿉니다 labels: defaults: @@ -16,6 +17,7 @@ ko: setting_default_content_type_markdown: 마크다운 setting_default_content_type_plain: 일반 텍스트 setting_favourite_modal: 관심글을 지정할 때 확인 창을 띄웁니다(글리치 풍미에만 적용됨) + setting_show_followers_count: 팔로워 수 표시 setting_skin: 스킨 setting_system_emoji_font: 에모지에 시스템 기본 폰트 적용하기 (글리치 풍미에만 적용됨) notification_emails: From 631a5eec85946848bf0b15562f82260c1f7721d8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 21:59:34 +0200 Subject: [PATCH 049/101] chore(deps): update dependency charlock_holmes to v0.7.9 (#30992) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1c253719e4ebae..18c2d34808d79a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -159,7 +159,7 @@ GEM case_transform (0.2) activesupport cbor (0.5.9.8) - charlock_holmes (0.7.8) + charlock_holmes (0.7.9) chewy (7.6.0) activesupport (>= 5.2) elasticsearch (>= 7.14.0, < 8) From 24bdba34132b0a6e193e7246304601a8ad453425 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Jul 2024 12:30:55 +0200 Subject: [PATCH 050/101] chore(deps): update dependency rubocop to v1.65.0 (#30985) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 18c2d34808d79a..2d085152e861b0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -583,7 +583,7 @@ GEM orm_adapter (0.5.0) ox (2.14.18) parallel (1.25.1) - parser (3.3.3.0) + parser (3.3.4.0) ast (~> 2.4.1) racc parslet (2.0.0) @@ -733,13 +733,13 @@ GEM rspec-mocks (~> 3.0) sidekiq (>= 5, < 8) rspec-support (3.13.1) - rubocop (1.64.1) + rubocop (1.65.0) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) + regexp_parser (>= 2.4, < 3.0) rexml (>= 3.2.5, < 4.0) rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (~> 1.7) From c244b70dc21f1a41f16588820490082a1e99b7ae Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Jul 2024 10:38:35 +0000 Subject: [PATCH 051/101] New Crowdin Translations (automated) (#30993) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/ar.json | 24 ++++++++++++------------ app/javascript/mastodon/locales/kab.json | 6 ++++++ config/locales/ar.yml | 5 +++++ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index a9e1bdb270ebad..115f6335b6f12c 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -4,7 +4,7 @@ "about.disclaimer": "ماستدون برنامج حر ومفتوح المصدر وعلامة تجارية لـ Mastodon GmbH.", "about.domain_blocks.no_reason_available": "السبب غير متوفر", "about.domain_blocks.preamble": "يسمح لك ماستدون عموماً بعرض المحتوى من المستخدمين من أي خادم آخر في الفدرالية والتفاعل معهم. وهذه هي الاستثناءات التي وضعت على هذا الخادم بالذات.", - "about.domain_blocks.silenced.explanation": "عموماً، لن ترى ملفات التعريف والمحتوى من هذا الخادم، إلا إذا كنت تبحث عنه بشكل صريح أو تختار أن تتابعه.", + "about.domain_blocks.silenced.explanation": "لن تظهر لك ملفات التعريف الشخصية والمحتوى من هذا الخادوم، إلا إن بحثت عنه عمدًا أو تابعته.", "about.domain_blocks.silenced.title": "محدود", "about.domain_blocks.suspended.explanation": "لن يتم معالجة أي بيانات من هذا الخادم أو تخزينها أو تبادلها، مما يجعل أي تفاعل أو اتصال مع المستخدمين من هذا الخادم مستحيلا.", "about.domain_blocks.suspended.title": "مُعلّق", @@ -21,7 +21,7 @@ "account.blocked": "محظور", "account.browse_more_on_origin_server": "تصفح المزيد في الملف الشخصي الأصلي", "account.cancel_follow_request": "إلغاء طلب المتابعة", - "account.copy": "نسخ الرابط إلى الحساب", + "account.copy": "نسخ الرابط إلى الملف الشخصي", "account.direct": "إشارة خاصة لـ @{name}", "account.disable_notifications": "توقف عن إشعاري عندما ينشر @{name}", "account.domain_blocked": "اسم النِّطاق محظور", @@ -32,7 +32,7 @@ "account.featured_tags.last_status_never": "لا توجد رسائل", "account.featured_tags.title": "وسوم {name} المميَّزة", "account.follow": "متابعة", - "account.follow_back": "رد المتابعة", + "account.follow_back": "تابعهم بالمثل", "account.followers": "مُتابِعون", "account.followers.empty": "لا أحدَ يُتابع هذا المُستخدم إلى حد الآن.", "account.following": "الاشتراكات", @@ -51,7 +51,7 @@ "account.mute_notifications_short": "كتم الإشعارات", "account.mute_short": "اكتم", "account.muted": "مَكتوم", - "account.mutual": "متبادل", + "account.mutual": "متبادلة", "account.no_bio": "لم يتم تقديم وصف.", "account.open_original_page": "افتح الصفحة الأصلية", "account.posts": "منشورات", @@ -70,8 +70,8 @@ "account.unmute_notifications_short": "إلغاء كَتم الإشعارات", "account.unmute_short": "إلغاء الكتم", "account_note.placeholder": "اضغط لإضافة مُلاحظة", - "admin.dashboard.daily_retention": "معدل الاحتفاظ بالمستخدم بعد التسجيل بيوم", - "admin.dashboard.monthly_retention": "معدل الاحتفاظ بالمستخدم بعد التسجيل بالشهور", + "admin.dashboard.daily_retention": "معدّل بقاء المستخدمين بعد إنشاء الحسابات، بالأيام", + "admin.dashboard.monthly_retention": "معدّل بقاء المستخدمين بعد إنشاء الحسابات، بالشهور", "admin.dashboard.retention.average": "المعدل", "admin.dashboard.retention.cohort": "شهر التسجيل", "admin.dashboard.retention.cohort_size": "المستخدمون الجدد", @@ -87,12 +87,12 @@ "attachments_list.unprocessed": "(غير معالَج)", "audio.hide": "إخفاء المقطع الصوتي", "block_modal.remote_users_caveat": "سوف نطلب من الخادم {domain} أن يحترم قرارك، لكن الالتزام غير مضمون لأن بعض الخواديم قد تتعامل مع نصوص الكتل بشكل مختلف. قد تظل المنشورات العامة مرئية للمستخدمين غير المسجلين الدخول.", - "block_modal.show_less": "أظهر الأقل", - "block_modal.show_more": "أظهر المزيد", + "block_modal.show_less": "تفاصيل أقلّ", + "block_modal.show_more": "تفاصيل أكثر", "block_modal.they_cant_mention": "لن يستطيع ذِكرك أو متابعتك.", - "block_modal.they_cant_see_posts": "لن يستطيع رؤية منشوراتك ولن ترى منشوراته.", - "block_modal.they_will_know": "يمكنه أن يرى أنه قد تم حظره.", - "block_modal.title": "أتريد حظر المستخدم؟", + "block_modal.they_cant_see_posts": "لن يستطيع مطالعة منشوراتك ولن تطالع منشوراته.", + "block_modal.they_will_know": "سيعلم أنه قد حُظِر.", + "block_modal.title": "أتريد حظر هذا المستخدم؟", "block_modal.you_wont_see_mentions": "لن تر المنشورات التي يُشار فيهم إليه.", "boost_modal.combo": "يُمكنك الضّغط على {combo} لتخطي هذا في المرة المُقبلة", "bundle_column_error.copy_stacktrace": "انسخ تقرير الخطأ", @@ -156,7 +156,7 @@ "compose_form.poll.single": "اختر واحدا", "compose_form.poll.switch_to_multiple": "تغيِير الاستطلاع للسماح باِخيارات مُتعدِّدة", "compose_form.poll.switch_to_single": "تغيِير الاستطلاع للسماح باِخيار واحد فقط", - "compose_form.poll.type": "الأسلوب", + "compose_form.poll.type": "الطراز", "compose_form.publish": "نشر", "compose_form.publish_form": "منشور جديد", "compose_form.reply": "ردّ", diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json index fcaa8f143ed445..868edbc8c0fe4c 100644 --- a/app/javascript/mastodon/locales/kab.json +++ b/app/javascript/mastodon/locales/kab.json @@ -28,9 +28,12 @@ "account.featured_tags.last_status_at": "Tasuffeɣt taneggarut ass n {date}", "account.featured_tags.last_status_never": "Ulac tisuffaɣ", "account.follow": "Ḍfer", + "account.follow_back": "Ḍfer-it ula d kečč·m", "account.followers": "Imeḍfaren", "account.followers.empty": "Ar tura, ulac yiwen i yeṭṭafaṛen amseqdac-agi.", + "account.followers_counter": "{count, plural, one {{counter} n umḍfar} other {{counter} n yimeḍfaren}}", "account.following": "Yeṭṭafaṛ", + "account.following_counter": "{count, plural, one {{counter} yettwaḍfaren} other {{counter} yettwaḍfaren}}", "account.follows.empty": "Ar tura, amseqdac-agi ur yeṭṭafaṛ yiwen.", "account.go_to_profile": "Ddu ɣer umaɣnu", "account.hide_reblogs": "Ffer ayen i ibeṭṭu @{name}", @@ -53,6 +56,7 @@ "account.requested_follow": "{name} yessuter ad k·m-yeḍfer", "account.share": "Bḍu amaɣnu n @{name}", "account.show_reblogs": "Ssken-d inebḍa n @{name}", + "account.statuses_counter": "{count, plural, one {{counter} n tsuffeɣt} other {{counter} n tsuffaɣ}}", "account.unblock": "Serreḥ i @{name}", "account.unblock_domain": "Ssken-d {domain}", "account.unblock_short": "Serreḥ", @@ -560,6 +564,7 @@ "status.edited_x_times": "Tettwaẓreg {count, plural, one {{count} n tikkelt} other {{count} n tikkal}}", "status.embed": "Seddu", "status.favourite": "Amenyaf", + "status.favourites": "{count, plural, one {n usmenyaf} other {n ismenyafen}}", "status.filter": "Sizdeg tassufeɣt-a", "status.filtered": "Yettwasizdeg", "status.hide": "Ffer tasuffeɣt", @@ -579,6 +584,7 @@ "status.read_more": "Issin ugar", "status.reblog": "Bḍu", "status.reblogged_by": "Yebḍa-tt {name}", + "status.reblogs": "{count, plural, one {n usnerni} other {n yisnernuyen}}", "status.reblogs.empty": "Ula yiwen ur yebḍi tajewwiqt-agi ar tura. Ticki yebḍa-tt yiwen, ad d-iban da.", "status.redraft": "Kkes tɛiwdeḍ tira", "status.remove_bookmark": "Kkes tacreḍt", diff --git a/config/locales/ar.yml b/config/locales/ar.yml index 2ca7538c324d00..4df24c984c0ed0 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -1762,6 +1762,10 @@ ar: webauthn_authentication: مفاتيح الأمان severed_relationships: download: تنزيل (%{count}) + lost_followers: المتابعون المفقودون + lost_follows: المتابعات المفقودة + preamble: بحجبكم اسم نطاق قد تخسرون متابَعاتٍ، و كذلك إذا قرّر مديرو الخادوم حظر خادوم ما. و في هذه الحالات يكون بوسعكم تنزيل قائمة بالصلات المبتورة لمعاينتها، مع القدرة على استيرادها إلى خادوم آخر. + purged: حذف مدير خادومكم المعلومات عن هذا الخادوم. statuses: attached: audio: @@ -1978,6 +1982,7 @@ ar: edit_profile_title: قم بتخصيص ملفك التعريفي explanation: ها هي بعض النصائح قبل بداية الاستخدام feature_action: اعرف المزيد + feature_audience: يتيح لكم مًستُدون إدارة جمهوركم بلا وسطاء. فبتنصيب و تشغيل مَستُودون على بنيتكم التحتية تمكنكم متابعة مستخدمي مَستُدون من أيّ خادوم،كما يمكنهم متابعتكم، بلا تحكُّم من أي طرف ثالث. feature_audience_title: اِبنوا جُمهورَكم بِثِقَة feature_control: أنتم الأدرى بالمحتوى الذي تريدون أن تطالعوه في فيض المنشورات الرئيس. لا خوارزميات تتحكم فيما يظهر لكم ولا إعلانات تضيع وقتكم. بحساب واحد تمكنكم متابعة من تشاؤون على أيّ خادوم ماستدون، وتلقّى منشوراتهم بترتيبها الزمني، لتصنعوا ركنكم الأليف في الإنترنت. feature_control_title: تحكَّموا في فيض المنشورات الخاص بكم From 6c375297236ecd1df7a5e82c24ad025b46be46cc Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 11 Jul 2024 07:13:55 -0400 Subject: [PATCH 052/101] Use `module: :auth` to wrap `devise_for` routes config (#30990) --- config/routes.rb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index 2c06762c229224..e4f091043d6c2a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -104,13 +104,9 @@ def redirect_with_vary(path) end end - devise_for :users, path: 'auth', format: false, controllers: { - omniauth_callbacks: 'auth/omniauth_callbacks', - sessions: 'auth/sessions', - registrations: 'auth/registrations', - passwords: 'auth/passwords', - confirmations: 'auth/confirmations', - } + scope module: :auth do + devise_for :users, path: 'auth', format: false + end with_options constraints: ->(req) { req.format.nil? || req.format.html? } do get '/users/:username', to: redirect_with_vary('/@%{username}') From 8c8bee5a36e0cd0b5d35903f8891896447c3e060 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Thu, 11 Jul 2024 15:43:42 +0200 Subject: [PATCH 053/101] Change disabled buttons color in light mode to make it more visible (#30998) --- app/javascript/styles/mastodon-light/diff.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/javascript/styles/mastodon-light/diff.scss b/app/javascript/styles/mastodon-light/diff.scss index 07e9d9868b4272..a41272364a3106 100644 --- a/app/javascript/styles/mastodon-light/diff.scss +++ b/app/javascript/styles/mastodon-light/diff.scss @@ -48,6 +48,10 @@ html { } } +.icon-button:disabled { + color: darken($action-button-color, 25%); +} + .account__header__bar .avatar .account__avatar { border-color: $white; } From e26052192d2bc11ef8adb7bb5f9d4d0fcbfc94f4 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Thu, 11 Jul 2024 15:43:42 +0200 Subject: [PATCH 054/101] [Glitch] Change disabled buttons color in light mode to make it more visible Port 8c8bee5a36e0cd0b5d35903f8891896447c3e060 to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/styles/mastodon-light/diff.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss b/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss index 4040ee0fe0c598..1f5059c321e765 100644 --- a/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss +++ b/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss @@ -48,6 +48,10 @@ html { } } +.icon-button:disabled { + color: darken($action-button-color, 25%); +} + .account__header__bar .avatar .account__avatar { border-color: $white; } From ad52b04a1c88574ae3be2c56bead1b0638b253fc Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 11 Jul 2024 21:42:58 +0200 Subject: [PATCH 055/101] Add setting to disable hover cards (#30931) Co-authored-by: Eugen Rochko --- app/javascript/mastodon/components/status.jsx | 2 +- app/javascript/mastodon/components/status_content.jsx | 2 +- .../features/notifications/components/notification.jsx | 4 ++-- app/javascript/mastodon/features/ui/index.jsx | 4 ++-- app/javascript/mastodon/initial_state.js | 2 ++ app/models/concerns/user/has_settings.rb | 4 ++++ app/models/user_settings.rb | 1 + app/serializers/initial_state_serializer.rb | 1 + app/views/settings/preferences/appearance/show.html.haml | 1 + config/locales/simple_form.en.yml | 1 + 10 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status.jsx index dce48d70364965..502dc8bee3c0e6 100644 --- a/app/javascript/mastodon/components/status.jsx +++ b/app/javascript/mastodon/components/status.jsx @@ -562,7 +562,7 @@ class Status extends ImmutablePureComponent { {status.get('edited_at') && *} - +
{statusAvatar}
diff --git a/app/javascript/mastodon/components/status_content.jsx b/app/javascript/mastodon/components/status_content.jsx index 82135b85cad26e..96452374dcc639 100644 --- a/app/javascript/mastodon/components/status_content.jsx +++ b/app/javascript/mastodon/components/status_content.jsx @@ -116,7 +116,7 @@ class StatusContent extends PureComponent { if (mention) { link.addEventListener('click', this.onMentionClick.bind(this, mention), false); - link.removeAttribute('title'); + link.setAttribute('title', `@${mention.get('acct')}`); link.setAttribute('href', `/@${mention.get('acct')}`); link.setAttribute('data-hover-card-account', mention.get('id')); } else if (link.textContent[0] === '#' || (link.previousSibling && link.previousSibling.textContent && link.previousSibling.textContent[link.previousSibling.textContent.length - 1] === '#')) { diff --git a/app/javascript/mastodon/features/notifications/components/notification.jsx b/app/javascript/mastodon/features/notifications/components/notification.jsx index 272893042d7e40..986628fdc85a8d 100644 --- a/app/javascript/mastodon/features/notifications/components/notification.jsx +++ b/app/javascript/mastodon/features/notifications/components/notification.jsx @@ -435,7 +435,7 @@ class Notification extends ImmutablePureComponent { const targetAccount = report.get('target_account'); const targetDisplayNameHtml = { __html: targetAccount.get('display_name_html') }; - const targetLink = ; + const targetLink = ; return ( @@ -458,7 +458,7 @@ class Notification extends ImmutablePureComponent { const { notification } = this.props; const account = notification.get('account'); const displayNameHtml = { __html: account.get('display_name_html') }; - const link = ; + const link = ; switch(notification.get('type')) { case 'follow': diff --git a/app/javascript/mastodon/features/ui/index.jsx b/app/javascript/mastodon/features/ui/index.jsx index d41132f9ca1e40..d9f609620cde7b 100644 --- a/app/javascript/mastodon/features/ui/index.jsx +++ b/app/javascript/mastodon/features/ui/index.jsx @@ -25,7 +25,7 @@ import { clearHeight } from '../../actions/height_cache'; import { expandNotifications } from '../../actions/notifications'; import { fetchServer, fetchServerTranslationLanguages } from '../../actions/server'; import { expandHomeTimeline } from '../../actions/timelines'; -import initialState, { me, owner, singleUserMode, trendsEnabled, trendsAsLanding } from '../../initial_state'; +import initialState, { me, owner, singleUserMode, trendsEnabled, trendsAsLanding, disableHoverCards } from '../../initial_state'; import BundleColumnError from './components/bundle_column_error'; import Header from './components/header'; @@ -588,7 +588,7 @@ class UI extends PureComponent { {layout !== 'mobile' && } - + {!disableHoverCards && } diff --git a/app/javascript/mastodon/initial_state.js b/app/javascript/mastodon/initial_state.js index 9ec3df0df87814..60b35cb31ac72e 100644 --- a/app/javascript/mastodon/initial_state.js +++ b/app/javascript/mastodon/initial_state.js @@ -15,6 +15,7 @@ * @property {boolean=} boost_modal * @property {boolean=} delete_modal * @property {boolean=} disable_swiping + * @property {boolean=} disable_hover_cards * @property {string=} disabled_account_id * @property {string} display_media * @property {string} domain @@ -86,6 +87,7 @@ export const autoPlayGif = getMeta('auto_play_gif'); export const boostModal = getMeta('boost_modal'); export const deleteModal = getMeta('delete_modal'); export const disableSwiping = getMeta('disable_swiping'); +export const disableHoverCards = getMeta('disable_hover_cards'); export const disabledAccountId = getMeta('disabled_account_id'); export const displayMedia = getMeta('display_media'); export const domain = getMeta('domain'); diff --git a/app/models/concerns/user/has_settings.rb b/app/models/concerns/user/has_settings.rb index 65373325f0ba79..1202f2033f1ccf 100644 --- a/app/models/concerns/user/has_settings.rb +++ b/app/models/concerns/user/has_settings.rb @@ -91,6 +91,10 @@ def setting_disable_swiping settings['web.disable_swiping'] end + def setting_disable_hover_cards + settings['web.disable_hover_cards'] + end + def setting_always_send_emails settings['always_send_emails'] end diff --git a/app/models/user_settings.rb b/app/models/user_settings.rb index 6571632fcd7a72..0ae8ab8ec79bba 100644 --- a/app/models/user_settings.rb +++ b/app/models/user_settings.rb @@ -25,6 +25,7 @@ class KeyError < Error; end setting :use_pending_items, default: false setting :use_system_font, default: false setting :disable_swiping, default: false + setting :disable_hover_cards, default: false setting :delete_modal, default: true setting :reblog_modal, default: false setting :reduce_motion, default: false diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index 72aaabcfcb1f81..13f332c95c4d7b 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -24,6 +24,7 @@ def meta store[:expand_spoilers] = object_account_user.setting_expand_spoilers store[:reduce_motion] = object_account_user.setting_reduce_motion store[:disable_swiping] = object_account_user.setting_disable_swiping + store[:disable_hover_cards] = object_account_user.setting_disable_hover_cards store[:advanced_layout] = object_account_user.setting_advanced_layout store[:use_blurhash] = object_account_user.setting_use_blurhash store[:use_pending_items] = object_account_user.setting_use_pending_items diff --git a/app/views/settings/preferences/appearance/show.html.haml b/app/views/settings/preferences/appearance/show.html.haml index e89e015b298bee..f34ce4a6aa5e61 100644 --- a/app/views/settings/preferences/appearance/show.html.haml +++ b/app/views/settings/preferences/appearance/show.html.haml @@ -56,6 +56,7 @@ = ff.input :'web.auto_play', wrapper: :with_label, recommended: true, label: I18n.t('simple_form.labels.defaults.setting_auto_play_gif') = ff.input :'web.reduce_motion', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_reduce_motion') = ff.input :'web.disable_swiping', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_disable_swiping') + = ff.input :'web.disable_hover_cards', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_disable_hover_cards') = ff.input :'web.use_system_font', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_system_font_ui') %h4= t 'appearance.discovery' diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index fdc9f61813d9c7..0313d4683edc66 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -211,6 +211,7 @@ en: setting_default_privacy: Posting privacy setting_default_sensitive: Always mark media as sensitive setting_delete_modal: Show confirmation dialog before deleting a post + setting_disable_hover_cards: Disable profile preview on hover setting_disable_swiping: Disable swiping motions setting_display_media: Media display setting_display_media_default: Default From bb702e6b2037b5b0f2e76b6f9d390fa0fd7abc54 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 10:18:35 +0200 Subject: [PATCH 056/101] fix(deps): update dependency sass to v1.77.8 (#31005) 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 cf7e5a8b952202..403a8310d01c61 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15462,15 +15462,15 @@ __metadata: linkType: hard "sass@npm:^1.62.1": - version: 1.77.7 - resolution: "sass@npm:1.77.7" + version: 1.77.8 + resolution: "sass@npm:1.77.8" dependencies: chokidar: "npm:>=3.0.0 <4.0.0" immutable: "npm:^4.0.0" source-map-js: "npm:>=0.6.2 <2.0.0" bin: sass: sass.js - checksum: 10c0/6cacbf4b5165d30a9be0f09438aed85ff0617e5087442e65c23c8464750ff1b9988855a58f36b420b62f992d1e82403f99810aba7731519d3d026847e21a5635 + checksum: 10c0/2bfd62794070352c804f949e69bd8bb5b4ec846deeb924251b2c3f7b503170fb1ae186f513f0166907749eb34e0277dee747edcb78c886fb471aac01be1e864c languageName: node linkType: hard From 73d53827ea3ef1605f97f99bf3cbeeccb30638be Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 12 Jul 2024 04:19:15 -0400 Subject: [PATCH 057/101] Add change assertion to invites destroy spec (#31004) --- spec/controllers/invites_controller_spec.rb | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/spec/controllers/invites_controller_spec.rb b/spec/controllers/invites_controller_spec.rb index 3190c82884228f..5221941267fa00 100644 --- a/spec/controllers/invites_controller_spec.rb +++ b/spec/controllers/invites_controller_spec.rb @@ -69,19 +69,16 @@ end end - describe 'DELETE #create' do - let(:invite) { Fabricate(:invite, user: user, expires_at: nil) } - - before do - delete :destroy, params: { id: invite.id } - end + describe 'DELETE #destroy' do + subject { delete :destroy, params: { id: invite.id } } - it 'redirects' do - expect(response).to redirect_to invites_path - end + let(:invite) { Fabricate(:invite, user: user, expires_at: nil) } - it 'expires invite' do - expect(invite.reload).to be_expired + it 'expires invite and redirects' do + expect { subject } + .to(change { invite.reload.expired? }.to(true)) + expect(response) + .to redirect_to invites_path end end end From cd1bc94cd2232b2583c6d3d1924fd0ae00f542bc Mon Sep 17 00:00:00 2001 From: Nick Schonning Date: Fri, 12 Jul 2024 04:22:31 -0400 Subject: [PATCH 058/101] Regen RuboCop TODO for 1.65.0 (#31003) --- .rubocop_todo.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index b05d1bc6bd8bea..357ed99545028a 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --auto-gen-only-exclude --no-offense-counts --no-auto-gen-timestamp` -# using RuboCop version 1.64.1. +# using RuboCop version 1.65.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -14,7 +14,7 @@ Lint/NonLocalExitFromIterator: Metrics/AbcSize: Max: 82 -# Configuration parameters: CountBlocks, Max. +# Configuration parameters: CountBlocks, CountModifierForms, Max. Metrics/BlockNesting: Exclude: - 'lib/tasks/mastodon.rake' From fdad4dc918d0eb8538d79173e86c82fd6d0290a6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 10:23:04 +0200 Subject: [PATCH 059/101] fix(deps): update babel monorepo to v7.24.8 (#31002) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 222 +++++++++++++++++++++++++++--------------------------- 1 file changed, 111 insertions(+), 111 deletions(-) diff --git a/yarn.lock b/yarn.lock index 403a8310d01c61..988c6c79cfb5cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -52,45 +52,45 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/compat-data@npm:7.24.7" - checksum: 10c0/dcd93a5632b04536498fbe2be5af1057f635fd7f7090483d8e797878559037e5130b26862ceb359acbae93ed27e076d395ddb4663db6b28a665756ffd02d324f +"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/compat-data@npm:7.24.8" + checksum: 10c0/7f465e9d8e44c5b516eeb3001362a3cd9a6df51dd90d3ac9868e1e7fa631ac57fc781cec6700110d4f555ba37fe59c4a71927b445106fe0062e79e79ffe11091 languageName: node linkType: hard "@babel/core@npm:^7.10.4, @babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.22.1, @babel/core@npm:^7.24.4": - version: 7.24.7 - resolution: "@babel/core@npm:7.24.7" + version: 7.24.8 + resolution: "@babel/core@npm:7.24.8" dependencies: "@ampproject/remapping": "npm:^2.2.0" "@babel/code-frame": "npm:^7.24.7" - "@babel/generator": "npm:^7.24.7" - "@babel/helper-compilation-targets": "npm:^7.24.7" - "@babel/helper-module-transforms": "npm:^7.24.7" - "@babel/helpers": "npm:^7.24.7" - "@babel/parser": "npm:^7.24.7" + "@babel/generator": "npm:^7.24.8" + "@babel/helper-compilation-targets": "npm:^7.24.8" + "@babel/helper-module-transforms": "npm:^7.24.8" + "@babel/helpers": "npm:^7.24.8" + "@babel/parser": "npm:^7.24.8" "@babel/template": "npm:^7.24.7" - "@babel/traverse": "npm:^7.24.7" - "@babel/types": "npm:^7.24.7" + "@babel/traverse": "npm:^7.24.8" + "@babel/types": "npm:^7.24.8" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 10c0/4004ba454d3c20a46ea66264e06c15b82e9f6bdc35f88819907d24620da70dbf896abac1cb4cc4b6bb8642969e45f4d808497c9054a1388a386cf8c12e9b9e0d + checksum: 10c0/5e21b40cc69746deda3fe3d6540351d9cb0d1ad5aea055b7c319db26071ff5789fd9580d1aa47b114f07631e8e2109f4e71696ca11d7c7e60d157767022c1bd2 languageName: node linkType: hard -"@babel/generator@npm:^7.24.7, @babel/generator@npm:^7.7.2": - version: 7.24.7 - resolution: "@babel/generator@npm:7.24.7" +"@babel/generator@npm:^7.24.8, @babel/generator@npm:^7.7.2": + version: 7.24.8 + resolution: "@babel/generator@npm:7.24.8" dependencies: - "@babel/types": "npm:^7.24.7" + "@babel/types": "npm:^7.24.8" "@jridgewell/gen-mapping": "npm:^0.3.5" "@jridgewell/trace-mapping": "npm:^0.3.25" jsesc: "npm:^2.5.1" - checksum: 10c0/06b1f3350baf527a3309e50ffd7065f7aee04dd06e1e7db794ddfde7fe9d81f28df64edd587173f8f9295496a7ddb74b9a185d4bf4de7bb619e6d4ec45c8fd35 + checksum: 10c0/e8a278e75a895f13a7b17dd79abe1e894fe82a5ed3abb127c33c14c66773d69993762521c094c6c364723f8f7375683b0d4a96097781175a29407baedf67b769 languageName: node linkType: hard @@ -123,16 +123,16 @@ __metadata: languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/helper-compilation-targets@npm:7.24.7" +"@babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.24.7, @babel/helper-compilation-targets@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/helper-compilation-targets@npm:7.24.8" dependencies: - "@babel/compat-data": "npm:^7.24.7" - "@babel/helper-validator-option": "npm:^7.24.7" - browserslist: "npm:^4.22.2" + "@babel/compat-data": "npm:^7.24.8" + "@babel/helper-validator-option": "npm:^7.24.8" + browserslist: "npm:^4.23.1" lru-cache: "npm:^5.1.1" semver: "npm:^6.3.1" - checksum: 10c0/1d580a9bcacefe65e6bf02ba1dafd7ab278269fef45b5e281d8354d95c53031e019890464e7f9351898c01502dd2e633184eb0bcda49ed2ecd538675ce310f51 + checksum: 10c0/2885c44ef6aaf82b7e4352b30089bb09fbe08ed5ec24eb452c2bdc3c021e2a65ab412f74b3d67ec1398da0356c730b33a2ceca1d67d34c85080d31ca6efa9aec languageName: node linkType: hard @@ -231,9 +231,9 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/helper-module-transforms@npm:7.24.7" +"@babel/helper-module-transforms@npm:^7.24.7, @babel/helper-module-transforms@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/helper-module-transforms@npm:7.24.8" dependencies: "@babel/helper-environment-visitor": "npm:^7.24.7" "@babel/helper-module-imports": "npm:^7.24.7" @@ -242,7 +242,7 @@ __metadata: "@babel/helper-validator-identifier": "npm:^7.24.7" peerDependencies: "@babel/core": ^7.0.0 - checksum: 10c0/4f311755fcc3b4cbdb689386309cdb349cf0575a938f0b9ab5d678e1a81bbb265aa34ad93174838245f2ac7ff6d5ddbd0104638a75e4e961958ed514355687b6 + checksum: 10c0/b76496d5045af55be9de60e59e65e56a43033f01ccc746b26b7af911c358668c206b688ce70a23ab31ec04f9728f3a38e8d01073c85244115ab62f271a7fa3d1 languageName: node linkType: hard @@ -255,10 +255,10 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.24.7, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": - version: 7.24.7 - resolution: "@babel/helper-plugin-utils@npm:7.24.7" - checksum: 10c0/c3d38cd9b3520757bb4a279255cc3f956fc0ac1c193964bd0816ebd5c86e30710be8e35252227e0c9d9e0f4f56d9b5f916537f2bc588084b0988b4787a967d31 +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.24.7, @babel/helper-plugin-utils@npm:^7.24.8, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": + version: 7.24.8 + resolution: "@babel/helper-plugin-utils@npm:7.24.8" + checksum: 10c0/0376037f94a3bfe6b820a39f81220ac04f243eaee7193774b983e956c1750883ff236b30785795abbcda43fac3ece74750566830c2daa4d6e3870bb0dff34c2d languageName: node linkType: hard @@ -317,10 +317,10 @@ __metadata: languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/helper-string-parser@npm:7.24.7" - checksum: 10c0/47840c7004e735f3dc93939c77b099bb41a64bf3dda0cae62f60e6f74a5ff80b63e9b7cf77b5ec25a324516381fc994e1f62f922533236a8e3a6af57decb5e1e +"@babel/helper-string-parser@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/helper-string-parser@npm:7.24.8" + checksum: 10c0/6361f72076c17fabf305e252bf6d580106429014b3ab3c1f5c4eb3e6d465536ea6b670cc0e9a637a77a9ad40454d3e41361a2909e70e305116a23d68ce094c08 languageName: node linkType: hard @@ -331,10 +331,10 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/helper-validator-option@npm:7.24.7" - checksum: 10c0/21aea2b7bc5cc8ddfb828741d5c8116a84cbc35b4a3184ec53124f08e09746f1f67a6f9217850188995ca86059a7942e36d8965a6730784901def777b7e8a436 +"@babel/helper-validator-option@npm:^7.24.7, @babel/helper-validator-option@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/helper-validator-option@npm:7.24.8" + checksum: 10c0/73db93a34ae89201351288bee7623eed81a54000779462a986105b54ffe82069e764afd15171a428b82e7c7a9b5fec10b5d5603b216317a414062edf5c67a21f languageName: node linkType: hard @@ -350,13 +350,13 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/helpers@npm:7.24.7" +"@babel/helpers@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/helpers@npm:7.24.8" dependencies: "@babel/template": "npm:^7.24.7" - "@babel/types": "npm:^7.24.7" - checksum: 10c0/aa8e230f6668773e17e141dbcab63e935c514b4b0bf1fed04d2eaefda17df68e16b61a56573f7f1d4d1e605ce6cc162b5f7e9fdf159fde1fd9b77c920ae47d27 + "@babel/types": "npm:^7.24.8" + checksum: 10c0/42b8939b0a0bf72d6df9721973eb0fd7cd48f42641c5c9c740916397faa586255c06d36c6e6a7e091860723096281c620f6ffaee0011a3bb254a6f5475d89a12 languageName: node linkType: hard @@ -372,12 +372,12 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/parser@npm:7.24.7" +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.24.7, @babel/parser@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/parser@npm:7.24.8" bin: parser: ./bin/babel-parser.js - checksum: 10c0/8b244756872185a1c6f14b979b3535e682ff08cb5a2a5fd97cc36c017c7ef431ba76439e95e419d43000c5b07720495b00cf29a7f0d9a483643d08802b58819b + checksum: 10c0/ce69671de8fa6f649abf849be262707ac700b573b8b1ce1893c66cc6cd76aeb1294a19e8c290b0eadeb2f47d3f413a2e57a281804ffbe76bfb9fa50194cf3c52 languageName: node linkType: hard @@ -755,21 +755,21 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-classes@npm:7.24.7" +"@babel/plugin-transform-classes@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/plugin-transform-classes@npm:7.24.8" dependencies: "@babel/helper-annotate-as-pure": "npm:^7.24.7" - "@babel/helper-compilation-targets": "npm:^7.24.7" + "@babel/helper-compilation-targets": "npm:^7.24.8" "@babel/helper-environment-visitor": "npm:^7.24.7" "@babel/helper-function-name": "npm:^7.24.7" - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.8" "@babel/helper-replace-supers": "npm:^7.24.7" "@babel/helper-split-export-declaration": "npm:^7.24.7" globals: "npm:^11.1.0" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/e51dba7ce8b770d1eee929e098d5a3be3efc3e8b941e22dda7d0097dc4e7be5feabd2da7b707ac06fcac5661b31223c541941dec08ce76c1faa55544d87d06ec + checksum: 10c0/4423da0f747bdb6aab1995d98a74533fa679f637ec20706810dd57fb4ba2b1885ec8cae6a0b2c3f69f27165de6ff6aa2da9c4061c893848736a8267d0c653079 languageName: node linkType: hard @@ -785,14 +785,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-destructuring@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-destructuring@npm:7.24.7" +"@babel/plugin-transform-destructuring@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/plugin-transform-destructuring@npm:7.24.8" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.8" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/929f07a807fb62230bfbf881cfcedf187ac5daf2f1b01da94a75c7a0f6f72400268cf4bcfee534479e43260af8193e42c31ee03c8b0278ba77d0036ed6709c27 + checksum: 10c0/804968c1d5f5072c717505296c1e5d5ec33e90550423de66de82bbcb78157156e8470bbe77a04ab8c710a88a06360a30103cf223ac7eff4829adedd6150de5ce languageName: node linkType: hard @@ -938,16 +938,16 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-commonjs@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-modules-commonjs@npm:7.24.7" +"@babel/plugin-transform-modules-commonjs@npm:^7.24.7, @babel/plugin-transform-modules-commonjs@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.24.8" dependencies: - "@babel/helper-module-transforms": "npm:^7.24.7" - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-module-transforms": "npm:^7.24.8" + "@babel/helper-plugin-utils": "npm:^7.24.8" "@babel/helper-simple-access": "npm:^7.24.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/9442292b3daf6a5076cdc3c4c32bf423bda824ccaeb0dd0dc8b3effaa1fecfcb0130ae6e647fef12a5d5ff25bcc99a0d6bfc6d24a7525345e1bcf46fcdf81752 + checksum: 10c0/f1cf552307ebfced20d3907c1dd8be941b277f0364aa655e2b5fee828c84c54065745183104dae86f1f93ea0406db970a463ef7ceaaed897623748e99640e5a7 languageName: node linkType: hard @@ -1062,16 +1062,16 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-optional-chaining@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-optional-chaining@npm:7.24.7" +"@babel/plugin-transform-optional-chaining@npm:^7.24.7, @babel/plugin-transform-optional-chaining@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/plugin-transform-optional-chaining@npm:7.24.8" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.8" "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.7" "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/b9e3649b299e103b0d1767bbdba56574d065ff776e5350403b7bfd4e3982743c0cdb373d33bdbf94fa3c322d155e45d0aad946acf0aa741b870aed22dfec8b8e + checksum: 10c0/4ffbe1aad7dec7c9aa2bf6ceb4b2f91f96815b2784f2879bde80e46934f59d64a12cb2c6262e40897c4754d77d2c35d8a5cfed63044fdebf94978b1ed3d14b17 languageName: node linkType: hard @@ -1279,14 +1279,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-typeof-symbol@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-typeof-symbol@npm:7.24.7" +"@babel/plugin-transform-typeof-symbol@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.24.8" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.8" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/5649e7260a138681e68b296ab5931e2b1f132f287d6b4131d49b24f9dc20d62902b7e9d63c4d2decd5683b41df35ef4b9b03f58c7f9f65e4c25a6d8bbf04e9e9 + checksum: 10c0/2f570a4fbbdc5fd85f48165a97452826560051e3b8efb48c3bb0a0a33ee8485633439e7b71bfe3ef705583a1df43f854f49125bd759abdedc195b2cf7e60012a languageName: node linkType: hard @@ -1352,13 +1352,13 @@ __metadata: linkType: hard "@babel/preset-env@npm:^7.11.0, @babel/preset-env@npm:^7.12.1, @babel/preset-env@npm:^7.22.4": - version: 7.24.7 - resolution: "@babel/preset-env@npm:7.24.7" + version: 7.24.8 + resolution: "@babel/preset-env@npm:7.24.8" dependencies: - "@babel/compat-data": "npm:^7.24.7" - "@babel/helper-compilation-targets": "npm:^7.24.7" - "@babel/helper-plugin-utils": "npm:^7.24.7" - "@babel/helper-validator-option": "npm:^7.24.7" + "@babel/compat-data": "npm:^7.24.8" + "@babel/helper-compilation-targets": "npm:^7.24.8" + "@babel/helper-plugin-utils": "npm:^7.24.8" + "@babel/helper-validator-option": "npm:^7.24.8" "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "npm:^7.24.7" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.24.7" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "npm:^7.24.7" @@ -1389,9 +1389,9 @@ __metadata: "@babel/plugin-transform-block-scoping": "npm:^7.24.7" "@babel/plugin-transform-class-properties": "npm:^7.24.7" "@babel/plugin-transform-class-static-block": "npm:^7.24.7" - "@babel/plugin-transform-classes": "npm:^7.24.7" + "@babel/plugin-transform-classes": "npm:^7.24.8" "@babel/plugin-transform-computed-properties": "npm:^7.24.7" - "@babel/plugin-transform-destructuring": "npm:^7.24.7" + "@babel/plugin-transform-destructuring": "npm:^7.24.8" "@babel/plugin-transform-dotall-regex": "npm:^7.24.7" "@babel/plugin-transform-duplicate-keys": "npm:^7.24.7" "@babel/plugin-transform-dynamic-import": "npm:^7.24.7" @@ -1404,7 +1404,7 @@ __metadata: "@babel/plugin-transform-logical-assignment-operators": "npm:^7.24.7" "@babel/plugin-transform-member-expression-literals": "npm:^7.24.7" "@babel/plugin-transform-modules-amd": "npm:^7.24.7" - "@babel/plugin-transform-modules-commonjs": "npm:^7.24.7" + "@babel/plugin-transform-modules-commonjs": "npm:^7.24.8" "@babel/plugin-transform-modules-systemjs": "npm:^7.24.7" "@babel/plugin-transform-modules-umd": "npm:^7.24.7" "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.24.7" @@ -1414,7 +1414,7 @@ __metadata: "@babel/plugin-transform-object-rest-spread": "npm:^7.24.7" "@babel/plugin-transform-object-super": "npm:^7.24.7" "@babel/plugin-transform-optional-catch-binding": "npm:^7.24.7" - "@babel/plugin-transform-optional-chaining": "npm:^7.24.7" + "@babel/plugin-transform-optional-chaining": "npm:^7.24.8" "@babel/plugin-transform-parameters": "npm:^7.24.7" "@babel/plugin-transform-private-methods": "npm:^7.24.7" "@babel/plugin-transform-private-property-in-object": "npm:^7.24.7" @@ -1425,7 +1425,7 @@ __metadata: "@babel/plugin-transform-spread": "npm:^7.24.7" "@babel/plugin-transform-sticky-regex": "npm:^7.24.7" "@babel/plugin-transform-template-literals": "npm:^7.24.7" - "@babel/plugin-transform-typeof-symbol": "npm:^7.24.7" + "@babel/plugin-transform-typeof-symbol": "npm:^7.24.8" "@babel/plugin-transform-unicode-escapes": "npm:^7.24.7" "@babel/plugin-transform-unicode-property-regex": "npm:^7.24.7" "@babel/plugin-transform-unicode-regex": "npm:^7.24.7" @@ -1434,11 +1434,11 @@ __metadata: babel-plugin-polyfill-corejs2: "npm:^0.4.10" babel-plugin-polyfill-corejs3: "npm:^0.10.4" babel-plugin-polyfill-regenerator: "npm:^0.6.1" - core-js-compat: "npm:^3.31.0" + core-js-compat: "npm:^3.37.1" semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/c6714346f3ccc1271eaa90051c75b8bb57b20ef57408ab68740e2f3552693ae0ee5a4bcce3a00211d40e4947af1f7b8ab422066b953f0095461937fb72d11274 + checksum: 10c0/a6f29498ec58989845a61f9c10b1b4e80586f1810a33db461d597cdb0ad2cd847381a993038b09f727512a08b2c1a33a330a5d4e6d65463ee98a1b4302d52ec6 languageName: node linkType: hard @@ -1503,11 +1503,11 @@ __metadata: linkType: hard "@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.3, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": - version: 7.24.7 - resolution: "@babel/runtime@npm:7.24.7" + version: 7.24.8 + resolution: "@babel/runtime@npm:7.24.8" dependencies: regenerator-runtime: "npm:^0.14.0" - checksum: 10c0/b6fa3ec61a53402f3c1d75f4d808f48b35e0dfae0ec8e2bb5c6fc79fb95935da75766e0ca534d0f1c84871f6ae0d2ebdd950727cfadb745a2cdbef13faef5513 + checksum: 10c0/f24b30af6b3ecae19165b3b032f9bc37b2d1769677bd63b69a6f81061967cfc847aa822518402ea6616b1d301d7eb46986b99c9f69cdb5880834fca2e6b34881 languageName: node linkType: hard @@ -1522,32 +1522,32 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:7, @babel/traverse@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/traverse@npm:7.24.7" +"@babel/traverse@npm:7, @babel/traverse@npm:^7.24.7, @babel/traverse@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/traverse@npm:7.24.8" dependencies: "@babel/code-frame": "npm:^7.24.7" - "@babel/generator": "npm:^7.24.7" + "@babel/generator": "npm:^7.24.8" "@babel/helper-environment-visitor": "npm:^7.24.7" "@babel/helper-function-name": "npm:^7.24.7" "@babel/helper-hoist-variables": "npm:^7.24.7" "@babel/helper-split-export-declaration": "npm:^7.24.7" - "@babel/parser": "npm:^7.24.7" - "@babel/types": "npm:^7.24.7" + "@babel/parser": "npm:^7.24.8" + "@babel/types": "npm:^7.24.8" debug: "npm:^4.3.1" globals: "npm:^11.1.0" - checksum: 10c0/a5135e589c3f1972b8877805f50a084a04865ccb1d68e5e1f3b94a8841b3485da4142e33413d8fd76bc0e6444531d3adf1f59f359c11ffac452b743d835068ab + checksum: 10c0/67a5cc35824455cdb54fb9e196a44b3186283e29018a9c2331f51763921e18e891b3c60c283615a27540ec8eb4c8b89f41c237b91f732a7aa518b2eb7a0d434d languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.0.0-beta.49, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.6, @babel/types@npm:^7.20.7, @babel/types@npm:^7.24.7, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": - version: 7.24.7 - resolution: "@babel/types@npm:7.24.7" +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.0.0-beta.49, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.6, @babel/types@npm:^7.20.7, @babel/types@npm:^7.24.7, @babel/types@npm:^7.24.8, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": + version: 7.24.8 + resolution: "@babel/types@npm:7.24.8" dependencies: - "@babel/helper-string-parser": "npm:^7.24.7" + "@babel/helper-string-parser": "npm:^7.24.8" "@babel/helper-validator-identifier": "npm:^7.24.7" to-fast-properties: "npm:^2.0.0" - checksum: 10c0/d9ecbfc3eb2b05fb1e6eeea546836ac30d990f395ef3fe3f75ced777a222c3cfc4489492f72e0ce3d9a5a28860a1ce5f81e66b88cf5088909068b3ff4fab72c1 + checksum: 10c0/2d7bf561ae993e794cb052c5a81d3a6d1877da13e1e2eb2a59ae75a8fb1c965b618fb3e4abd42548f5f9a4587d3a149185a32d6c4c4ea82195da7dd86f2da0f1 languageName: node linkType: hard @@ -5676,7 +5676,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.0.0, browserslist@npm:^4.22.2, browserslist@npm:^4.23.0, browserslist@npm:^4.23.1": +"browserslist@npm:^4.0.0, browserslist@npm:^4.23.0, browserslist@npm:^4.23.1": version: 4.23.1 resolution: "browserslist@npm:4.23.1" dependencies: @@ -6392,12 +6392,12 @@ __metadata: languageName: node linkType: hard -"core-js-compat@npm:^3.31.0, core-js-compat@npm:^3.36.1": - version: 3.36.1 - resolution: "core-js-compat@npm:3.36.1" +"core-js-compat@npm:^3.36.1, core-js-compat@npm:^3.37.1": + version: 3.37.1 + resolution: "core-js-compat@npm:3.37.1" dependencies: browserslist: "npm:^4.23.0" - checksum: 10c0/70fba18a4095cd8ac04e5ba8cee251e328935859cf2851c1f67770068ea9f9fe71accb1b7de17cd3c9a28d304a4c41712bd9aa895110ebb6e3be71b666b029d1 + checksum: 10c0/4e2da9c900f2951a57947af7aeef4d16f2c75d7f7e966c0d0b62953f65225003ade5e84d3ae98847f65b24c109c606821d9dc925db8ca418fb761e7c81963c2a languageName: node linkType: hard From 28ad3588e4936f3429e5f062c76c37f50a2f38fb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 08:25:27 +0000 Subject: [PATCH 060/101] New Crowdin Translations (automated) (#31006) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/be.json | 4 ++++ config/locales/simple_form.da.yml | 1 + config/locales/simple_form.de.yml | 1 + config/locales/simple_form.es-AR.yml | 1 + config/locales/simple_form.es-MX.yml | 1 + config/locales/simple_form.es.yml | 1 + config/locales/simple_form.fi.yml | 1 + config/locales/simple_form.fo.yml | 1 + config/locales/simple_form.gl.yml | 1 + config/locales/simple_form.is.yml | 1 + config/locales/simple_form.it.yml | 1 + config/locales/simple_form.nl.yml | 1 + config/locales/simple_form.pl.yml | 1 + config/locales/simple_form.pt-PT.yml | 1 + config/locales/simple_form.sl.yml | 1 + config/locales/simple_form.sr-Latn.yml | 1 + config/locales/simple_form.sr.yml | 1 + config/locales/simple_form.tr.yml | 1 + config/locales/simple_form.vi.yml | 1 + config/locales/simple_form.zh-TW.yml | 1 + 20 files changed, 23 insertions(+) diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json index 643725270c729a..df29fbd4184fdf 100644 --- a/app/javascript/mastodon/locales/be.json +++ b/app/javascript/mastodon/locales/be.json @@ -35,7 +35,9 @@ "account.follow_back": "Падпісацца ў адказ", "account.followers": "Падпісчыкі", "account.followers.empty": "Ніхто пакуль не падпісаны на гэтага карыстальніка.", + "account.followers_counter": "{count, plural, one {{counter} падпісчык} few {{counter} падпісчыкі} many {{counter} падпісчыкаў} other {{counter} падпісчыка}}", "account.following": "Падпіскі", + "account.following_counter": "{count, plural, one {{counter} падпіска} few {{counter} падпіскі} many {{counter} падпісак} other {{counter} падпіскі}}", "account.follows.empty": "Карыстальнік ні на каго не падпісаны.", "account.go_to_profile": "Перайсці да профілю", "account.hide_reblogs": "Схаваць пашырэнні ад @{name}", @@ -61,6 +63,7 @@ "account.requested_follow": "{name} адправіў запыт на падпіску", "account.share": "Абагуліць профіль @{name}", "account.show_reblogs": "Паказаць падштурхоўванні ад @{name}", + "account.statuses_counter": "{count, plural, one {{counter} допіс} few {{counter} допісы} many {{counter} допісаў} other {{counter} допісу}}", "account.unblock": "Разблакіраваць @{name}", "account.unblock_domain": "Разблакіраваць дамен {domain}", "account.unblock_short": "Разблакіраваць", @@ -412,6 +415,7 @@ "limited_account_hint.title": "Гэты профіль быў схаваны мадэратарамі", "link_preview.author": "Ад {name}", "link_preview.more_from_author": "Больш ад {name}", + "link_preview.shares": "{count, plural, one {{counter} допіс} few {{counter} допісы} many {{counter} допісаў} other {{counter} допісу}}", "lists.account.add": "Дадаць да спісу", "lists.account.remove": "Выдаліць са спісу", "lists.delete": "Выдаліць спіс", diff --git a/config/locales/simple_form.da.yml b/config/locales/simple_form.da.yml index 0719e26430dd4c..73a32cf2fdee2d 100644 --- a/config/locales/simple_form.da.yml +++ b/config/locales/simple_form.da.yml @@ -211,6 +211,7 @@ da: setting_default_privacy: Fortrolighed for indlæg setting_default_sensitive: Markér altid medier som sensitive setting_delete_modal: Vis bekræftelsesdialog før et indlæg slettes + setting_disable_hover_cards: Deaktivér profilforhåndsvisning ved svæv (hover) setting_disable_swiping: Deaktivér strygebevægelser setting_display_media: Medievisning setting_display_media_default: Standard diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml index 7d397866977712..5711d2a12a4993 100644 --- a/config/locales/simple_form.de.yml +++ b/config/locales/simple_form.de.yml @@ -211,6 +211,7 @@ de: setting_default_privacy: Beitragssichtbarkeit setting_default_sensitive: Medien immer mit einer Inhaltswarnung versehen setting_delete_modal: Bestätigungsdialog beim Löschen eines Beitrags anzeigen + setting_disable_hover_cards: Profilvorschau deaktivieren, wenn die Maus über das Profil bewegt wird setting_disable_swiping: Wischgesten deaktivieren setting_display_media: Darstellung von Medien setting_display_media_default: Standard diff --git a/config/locales/simple_form.es-AR.yml b/config/locales/simple_form.es-AR.yml index e346a23a0282e8..1d1ac876894f94 100644 --- a/config/locales/simple_form.es-AR.yml +++ b/config/locales/simple_form.es-AR.yml @@ -211,6 +211,7 @@ es-AR: setting_default_privacy: Privacidad de mensajes setting_default_sensitive: Siempre marcar medios como sensibles setting_delete_modal: Mostrar diálogo de confirmación antes de eliminar un mensaje + setting_disable_hover_cards: Deshabilitar previsualización del perfil al pasar el cursor setting_disable_swiping: Deshabilitar movimientos de deslizamiento setting_display_media: Visualización de medios setting_display_media_default: Predeterminada diff --git a/config/locales/simple_form.es-MX.yml b/config/locales/simple_form.es-MX.yml index b3c8a857e83411..19a1c9d0c095f9 100644 --- a/config/locales/simple_form.es-MX.yml +++ b/config/locales/simple_form.es-MX.yml @@ -211,6 +211,7 @@ es-MX: setting_default_privacy: Privacidad de publicaciones setting_default_sensitive: Marcar siempre imágenes como sensibles setting_delete_modal: Mostrar diálogo de confirmación antes de borrar un toot + setting_disable_hover_cards: Desactivar vista previa del perfil al pasar el cursor setting_disable_swiping: Deshabilitar movimientos de deslizamiento setting_display_media: Visualización multimedia setting_display_media_default: Por defecto diff --git a/config/locales/simple_form.es.yml b/config/locales/simple_form.es.yml index 2fb5cab987f793..cc71e192665609 100644 --- a/config/locales/simple_form.es.yml +++ b/config/locales/simple_form.es.yml @@ -211,6 +211,7 @@ es: setting_default_privacy: Privacidad de publicaciones setting_default_sensitive: Marcar siempre imágenes como sensibles setting_delete_modal: Mostrar diálogo de confirmación antes de borrar una publicación + setting_disable_hover_cards: Desactivar vista previa del perfil al pasar el cursor setting_disable_swiping: Deshabilitar movimientos de deslizamiento setting_display_media: Visualización multimedia setting_display_media_default: Por defecto diff --git a/config/locales/simple_form.fi.yml b/config/locales/simple_form.fi.yml index b0bc8c735b4881..214308ba5cc6e6 100644 --- a/config/locales/simple_form.fi.yml +++ b/config/locales/simple_form.fi.yml @@ -211,6 +211,7 @@ fi: setting_default_privacy: Julkaisun näkyvyys setting_default_sensitive: Merkitse media aina arkaluonteiseksi setting_delete_modal: Kysy vahvistusta ennen julkaisun poistamista + setting_disable_hover_cards: Poista käytöstä profiilin esikatselu osoitettaessa setting_disable_swiping: Poista pyyhkäisyeleet käytöstä setting_display_media: Median näyttäminen setting_display_media_default: Oletus diff --git a/config/locales/simple_form.fo.yml b/config/locales/simple_form.fo.yml index 7d4da2b51ee38e..8f0b51719fceed 100644 --- a/config/locales/simple_form.fo.yml +++ b/config/locales/simple_form.fo.yml @@ -211,6 +211,7 @@ fo: setting_default_privacy: Hvussu privatir eru postar? setting_default_sensitive: Merk altíð miðlafílur sum viðkvæmar setting_delete_modal: Vís váttanarmynd, áðrenn postar verða strikaðir + setting_disable_hover_cards: Ger undanvísing, tá músin verður flutt yvir vangan, óvirkna setting_disable_swiping: Ger sveipurørslur óvirknar setting_display_media: Vístir miðlar setting_display_media_default: Sjálvvirði diff --git a/config/locales/simple_form.gl.yml b/config/locales/simple_form.gl.yml index e46ccb8737fd09..0653d7620430ec 100644 --- a/config/locales/simple_form.gl.yml +++ b/config/locales/simple_form.gl.yml @@ -211,6 +211,7 @@ gl: setting_default_privacy: Privacidade da publicación setting_default_sensitive: Marcar sempre multimedia como sensible setting_delete_modal: Solicitar confirmación antes de eliminar unha publicación + setting_disable_hover_cards: Desactivar vista previa do perfil ao poñerse enriba setting_disable_swiping: Desactivar opcións de desprazamento setting_display_media: Mostrar multimedia setting_display_media_default: Por defecto diff --git a/config/locales/simple_form.is.yml b/config/locales/simple_form.is.yml index 044e24deb03ac9..cc267db8d5629e 100644 --- a/config/locales/simple_form.is.yml +++ b/config/locales/simple_form.is.yml @@ -211,6 +211,7 @@ is: setting_default_privacy: Gagnaleynd færslna setting_default_sensitive: Alltaf merkja myndefni sem viðkvæmt setting_delete_modal: Birta staðfestingarglugga áður en færslu er eytt + setting_disable_hover_cards: Gera óvirka forskoðun notandasniðs við yfirsvif setting_disable_swiping: Gera strokuhreyfingar óvirkar setting_display_media: Birting myndefnis setting_display_media_default: Sjálfgefið diff --git a/config/locales/simple_form.it.yml b/config/locales/simple_form.it.yml index f5624344b9a3e7..540812faa9958f 100644 --- a/config/locales/simple_form.it.yml +++ b/config/locales/simple_form.it.yml @@ -211,6 +211,7 @@ it: setting_default_privacy: Privacy dei post setting_default_sensitive: Segna sempre i media come sensibili setting_delete_modal: Mostra dialogo di conferma prima di eliminare un post + setting_disable_hover_cards: Disabilita l'anteprima del profilo al passaggio del mouse setting_disable_swiping: Disabilita i movimenti di scorrimento setting_display_media: Visualizzazione dei media setting_display_media_default: Predefinita diff --git a/config/locales/simple_form.nl.yml b/config/locales/simple_form.nl.yml index 2271d7037efa45..d6bdb7a6f0a488 100644 --- a/config/locales/simple_form.nl.yml +++ b/config/locales/simple_form.nl.yml @@ -211,6 +211,7 @@ nl: setting_default_privacy: Zichtbaarheid van nieuwe berichten setting_default_sensitive: Media altijd als gevoelig markeren setting_delete_modal: Vraag voor het verwijderen van een bericht een bevestiging + setting_disable_hover_cards: Profielvoorbeeld uitschakelen bij zweven setting_disable_swiping: Swipebewegingen uitschakelen setting_display_media: Mediaweergave setting_display_media_default: Standaard diff --git a/config/locales/simple_form.pl.yml b/config/locales/simple_form.pl.yml index 9d82384af9d4a9..b8de10de26a336 100644 --- a/config/locales/simple_form.pl.yml +++ b/config/locales/simple_form.pl.yml @@ -211,6 +211,7 @@ pl: setting_default_privacy: Widoczność wpisów setting_default_sensitive: Zawsze oznaczaj zawartość multimedialną jako wrażliwą setting_delete_modal: Pytaj o potwierdzenie przed usunięciem wpisu + setting_disable_hover_cards: Wyłącz podgląd profilu po najechaniu setting_disable_swiping: Wyłącz ruchy przesuwania setting_display_media: Wyświetlanie zawartości multimedialnej setting_display_media_default: Domyślne diff --git a/config/locales/simple_form.pt-PT.yml b/config/locales/simple_form.pt-PT.yml index 7fcbb210c5b145..f993687b91bee4 100644 --- a/config/locales/simple_form.pt-PT.yml +++ b/config/locales/simple_form.pt-PT.yml @@ -211,6 +211,7 @@ pt-PT: setting_default_privacy: Privacidade da publicação setting_default_sensitive: Marcar sempre os media como problemáticos setting_delete_modal: Solicitar confirmação antes de eliminar uma publicação + setting_disable_hover_cards: Desativar visualização de perfil ao passar o cursor setting_disable_swiping: Desativar os movimentos de deslize setting_display_media: Visualização de media setting_display_media_default: Pré-definição diff --git a/config/locales/simple_form.sl.yml b/config/locales/simple_form.sl.yml index 96b36307a5795d..1e3f3046ad3256 100644 --- a/config/locales/simple_form.sl.yml +++ b/config/locales/simple_form.sl.yml @@ -211,6 +211,7 @@ sl: setting_default_privacy: Zasebnost objave setting_default_sensitive: Vedno označi medije kot občutljive setting_delete_modal: Pred brisanjem objave prikaži okno za pritrditev + setting_disable_hover_cards: Onemogoči predogled profila pod kazalko setting_disable_swiping: Onemogoči poteze drsanja setting_display_media: Prikaz medijev setting_display_media_default: Privzeto diff --git a/config/locales/simple_form.sr-Latn.yml b/config/locales/simple_form.sr-Latn.yml index 710f81e84fd1bd..22c07aa7c65938 100644 --- a/config/locales/simple_form.sr-Latn.yml +++ b/config/locales/simple_form.sr-Latn.yml @@ -211,6 +211,7 @@ sr-Latn: setting_default_privacy: Privatnost objava setting_default_sensitive: Uvek označi multimediju kao osetljivu setting_delete_modal: Prikaži dijalog za potvrdu pre brisanja objave + setting_disable_hover_cards: Onemogući pregled profila prelaskom kursora setting_disable_swiping: Onemogući pokrete prevlačenja setting_display_media: Prikaz medija setting_display_media_default: Podrazumevano diff --git a/config/locales/simple_form.sr.yml b/config/locales/simple_form.sr.yml index c5fbc9185a517e..10c1796637e999 100644 --- a/config/locales/simple_form.sr.yml +++ b/config/locales/simple_form.sr.yml @@ -211,6 +211,7 @@ sr: setting_default_privacy: Приватност објава setting_default_sensitive: Увек означи мултимедију као осетљиву setting_delete_modal: Прикажи дијалог за потврду пре брисања објаве + setting_disable_hover_cards: Онемогући преглед профила преласком курсора setting_disable_swiping: Онемогући покрете превлачења setting_display_media: Приказ медија setting_display_media_default: Подразумевано diff --git a/config/locales/simple_form.tr.yml b/config/locales/simple_form.tr.yml index 697417a5415956..b1e90122eca499 100644 --- a/config/locales/simple_form.tr.yml +++ b/config/locales/simple_form.tr.yml @@ -211,6 +211,7 @@ tr: setting_default_privacy: Gönderi gizliliği setting_default_sensitive: Medyayı her zaman hassas olarak işaretle setting_delete_modal: Bir gönderiyi silmeden önce onay iletişim kutusu göster + setting_disable_hover_cards: Üstüne geldiğinde profil önizlemesini devre dışı bırak setting_disable_swiping: Kaydırma hareketlerini devre dışı bırak setting_display_media: Medya görüntüleme setting_display_media_default: Varsayılan diff --git a/config/locales/simple_form.vi.yml b/config/locales/simple_form.vi.yml index e5063e611225de..9006156db1a4c7 100644 --- a/config/locales/simple_form.vi.yml +++ b/config/locales/simple_form.vi.yml @@ -211,6 +211,7 @@ vi: setting_default_privacy: Kiểu đăng setting_default_sensitive: Đánh dấu media nhạy cảm setting_delete_modal: Hỏi trước khi xóa tút + setting_disable_hover_cards: Tắt thẻ xem trước hồ sơ setting_disable_swiping: Không dùng chuyển động vuốt setting_display_media: Media nhạy cảm setting_display_media_default: Mặc định diff --git a/config/locales/simple_form.zh-TW.yml b/config/locales/simple_form.zh-TW.yml index b7a67c6a6fb8a9..7c460a25f58e24 100644 --- a/config/locales/simple_form.zh-TW.yml +++ b/config/locales/simple_form.zh-TW.yml @@ -211,6 +211,7 @@ zh-TW: setting_default_privacy: 嘟文隱私設定 setting_default_sensitive: 總是將媒體標記為敏感內容 setting_delete_modal: 刪除嘟文前先詢問我 + setting_disable_hover_cards: 停用於滑鼠懸停時預覽個人檔案 setting_disable_swiping: 停用滑動手勢 setting_display_media: 媒體顯示 setting_display_media_default: 預設 From 43e24dbb13f497cf13bf301e40f8790ba5960f2d Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 12 Jul 2024 04:30:52 -0400 Subject: [PATCH 061/101] Use `include_pagination_headers` in more places (#30999) --- .../requests/api/v1/accounts/statuses_spec.rb | 50 ++++++------------- spec/requests/api/v1/conversations_spec.rb | 8 ++- .../statuses/favourited_by_accounts_spec.rb | 6 ++- .../v1/statuses/reblogged_by_accounts_spec.rb | 6 ++- spec/requests/api/v2/admin/accounts_spec.rb | 3 +- 5 files changed, 31 insertions(+), 42 deletions(-) diff --git a/spec/requests/api/v1/accounts/statuses_spec.rb b/spec/requests/api/v1/accounts/statuses_spec.rb index 371867b21572c4..97cdbe0156f7b0 100644 --- a/spec/requests/api/v1/accounts/statuses_spec.rb +++ b/spec/requests/api/v1/accounts/statuses_spec.rb @@ -10,12 +10,15 @@ describe 'GET /api/v1/accounts/:account_id/statuses' do it 'returns expected headers', :aggregate_failures do - Fabricate(:status, account: user.account) + status = Fabricate(:status, account: user.account) get "/api/v1/accounts/#{user.account.id}/statuses", params: { limit: 1 }, headers: headers - expect(response).to have_http_status(200) - expect(links_from_header.size) - .to eq(2) + expect(response) + .to have_http_status(200) + .and include_pagination_headers( + prev: api_v1_account_statuses_url(limit: 1, min_id: status.id), + next: api_v1_account_statuses_url(limit: 1, max_id: status.id) + ) end context 'with only media' do @@ -55,16 +58,9 @@ it 'returns http success and includes a header link' do get "/api/v1/accounts/#{user.account.id}/statuses", params: { pinned: true }, headers: headers - expect(response).to have_http_status(200) - expect(links_from_header.size) - .to eq(1) - expect(links_from_header) - .to contain_exactly( - have_attributes( - href: /pinned=true/, - attr_pairs: contain_exactly(['rel', 'prev']) - ) - ) + expect(response) + .to have_http_status(200) + .and include_pagination_headers(prev: api_v1_account_statuses_url(pinned: true, min_id: Status.first.id)) end end @@ -77,19 +73,11 @@ it 'returns http success and header pagination links to prev and next' do get "/api/v1/accounts/#{user.account.id}/statuses", params: { pinned: true }, headers: headers - expect(response).to have_http_status(200) - expect(links_from_header.size) - .to eq(2) - expect(links_from_header) - .to contain_exactly( - have_attributes( - href: /pinned=true/, - attr_pairs: contain_exactly(['rel', 'next']) - ), - have_attributes( - href: /pinned=true/, - attr_pairs: contain_exactly(['rel', 'prev']) - ) + expect(response) + .to have_http_status(200) + .and include_pagination_headers( + prev: api_v1_account_statuses_url(pinned: true, min_id: Status.first.id), + next: api_v1_account_statuses_url(pinned: true, max_id: Status.first.id) ) end end @@ -138,12 +126,4 @@ end end end - - private - - def links_from_header - response - .headers['Link'] - .links - end end diff --git a/spec/requests/api/v1/conversations_spec.rb b/spec/requests/api/v1/conversations_spec.rb index f4776f18d97453..f136e1f4e84dfd 100644 --- a/spec/requests/api/v1/conversations_spec.rb +++ b/spec/requests/api/v1/conversations_spec.rb @@ -20,8 +20,12 @@ it 'returns pagination headers', :aggregate_failures do get '/api/v1/conversations', params: { limit: 1 }, headers: headers - expect(response).to have_http_status(200) - expect(response.headers['Link'].links.size).to eq(2) + expect(response) + .to have_http_status(200) + .and include_pagination_headers( + prev: api_v1_conversations_url(limit: 1, min_id: Status.first.id), + next: api_v1_conversations_url(limit: 1, max_id: Status.first.id) + ) end it 'returns conversations', :aggregate_failures do diff --git a/spec/requests/api/v1/statuses/favourited_by_accounts_spec.rb b/spec/requests/api/v1/statuses/favourited_by_accounts_spec.rb index 44296f4c37e5d5..2fd79f424b12b6 100644 --- a/spec/requests/api/v1/statuses/favourited_by_accounts_spec.rb +++ b/spec/requests/api/v1/statuses/favourited_by_accounts_spec.rb @@ -29,8 +29,10 @@ expect(response) .to have_http_status(200) - expect(response.headers['Link'].links.size) - .to eq(2) + .and include_pagination_headers( + prev: api_v1_status_favourited_by_index_url(limit: 2, since_id: Favourite.last.id), + next: api_v1_status_favourited_by_index_url(limit: 2, max_id: Favourite.first.id) + ) expect(body_as_json.size) .to eq(2) diff --git a/spec/requests/api/v1/statuses/reblogged_by_accounts_spec.rb b/spec/requests/api/v1/statuses/reblogged_by_accounts_spec.rb index 6f99ce94646670..5fc54042f91a8f 100644 --- a/spec/requests/api/v1/statuses/reblogged_by_accounts_spec.rb +++ b/spec/requests/api/v1/statuses/reblogged_by_accounts_spec.rb @@ -28,8 +28,10 @@ expect(response) .to have_http_status(200) - expect(response.headers['Link'].links.size) - .to eq(2) + .and include_pagination_headers( + prev: api_v1_status_reblogged_by_index_url(limit: 2, since_id: bob.statuses.first.id), + next: api_v1_status_reblogged_by_index_url(limit: 2, max_id: alice.statuses.first.id) + ) expect(body_as_json.size) .to eq(2) diff --git a/spec/requests/api/v2/admin/accounts_spec.rb b/spec/requests/api/v2/admin/accounts_spec.rb index f5db93233c2012..8f52c6a6134ed6 100644 --- a/spec/requests/api/v2/admin/accounts_spec.rb +++ b/spec/requests/api/v2/admin/accounts_spec.rb @@ -83,7 +83,8 @@ def body_json_ids let(:params) { { limit: 1 } } it 'sets the correct pagination headers' do - expect(response.headers['Link'].find_link(%w(rel next)).href).to eq api_v2_admin_accounts_url(limit: 1, max_id: admin_account.id) + expect(response) + .to include_pagination_headers(next: api_v2_admin_accounts_url(limit: 1, max_id: admin_account.id)) end end end From 9cc0607358dda4e3dd46e7bcee05e6545175f626 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 11:29:10 +0200 Subject: [PATCH 062/101] chore(deps): update dependency database_cleaner-active_record to v2.2.0 (#31007) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2d085152e861b0..8bb16206460927 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -180,7 +180,7 @@ GEM css_parser (1.17.1) addressable csv (3.3.0) - database_cleaner-active_record (2.1.0) + database_cleaner-active_record (2.2.0) activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) From c929b4cace3f95fe54fdafe449ea7e972c8d61e8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 11:29:43 +0200 Subject: [PATCH 063/101] chore(deps): update dependency pghero to v3.6.0 (#30994) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8bb16206460927..5c673a6282094c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -590,8 +590,8 @@ GEM pastel (0.8.0) tty-color (~> 0.5) pg (1.5.6) - pghero (3.5.0) - activerecord (>= 6) + pghero (3.6.0) + activerecord (>= 6.1) premailer (1.23.0) addressable css_parser (>= 1.12.0) From 8d2a93b0cba756d1fffdbc6f2d2ffa39b571044b Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 11 Jul 2024 21:42:58 +0200 Subject: [PATCH 064/101] [Glitch] Add setting to disable hover cards Port ad52b04a1c88574ae3be2c56bead1b0638b253fc to glitch-soc Co-authored-by: Eugen Rochko --- app/javascript/flavours/glitch/components/status_content.jsx | 2 +- app/javascript/flavours/glitch/components/status_header.jsx | 1 + app/javascript/flavours/glitch/features/ui/index.jsx | 4 ++-- app/javascript/flavours/glitch/initial_state.js | 2 ++ 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/javascript/flavours/glitch/components/status_content.jsx b/app/javascript/flavours/glitch/components/status_content.jsx index c28f85eb724236..634ab0db29a061 100644 --- a/app/javascript/flavours/glitch/components/status_content.jsx +++ b/app/javascript/flavours/glitch/components/status_content.jsx @@ -181,7 +181,7 @@ class StatusContent extends PureComponent { if (mention) { link.addEventListener('click', this.onMentionClick.bind(this, mention), false); - link.removeAttribute('title'); + link.setAttribute('title', `@${mention.get('acct')}`); link.setAttribute('data-hover-card-account', mention.get('id')); if (rewriteMentions !== 'no') { while (link.firstChild) link.removeChild(link.firstChild); diff --git a/app/javascript/flavours/glitch/components/status_header.jsx b/app/javascript/flavours/glitch/components/status_header.jsx index ee4573659c4c83..2dd09d68ef80a7 100644 --- a/app/javascript/flavours/glitch/components/status_header.jsx +++ b/app/javascript/flavours/glitch/components/status_header.jsx @@ -51,6 +51,7 @@ export default class StatusHeader extends PureComponent { target='_blank' onClick={this.handleAccountClick} rel='noopener noreferrer' + title={status.getIn(['account', 'acct'])} data-hover-card-account={status.getIn(['account', 'id'])} >
diff --git a/app/javascript/flavours/glitch/features/ui/index.jsx b/app/javascript/flavours/glitch/features/ui/index.jsx index 747922f23e2dc9..221f789a315995 100644 --- a/app/javascript/flavours/glitch/features/ui/index.jsx +++ b/app/javascript/flavours/glitch/features/ui/index.jsx @@ -27,7 +27,7 @@ import { clearHeight } from '../../actions/height_cache'; import { expandNotifications, notificationsSetVisibility } from '../../actions/notifications'; import { fetchServer, fetchServerTranslationLanguages } from '../../actions/server'; import { expandHomeTimeline } from '../../actions/timelines'; -import initialState, { me, owner, singleUserMode, trendsEnabled, trendsAsLanding } from '../../initial_state'; +import initialState, { me, owner, singleUserMode, trendsEnabled, trendsAsLanding, disableHoverCards } from '../../initial_state'; import BundleColumnError from './components/bundle_column_error'; import Header from './components/header'; @@ -651,7 +651,7 @@ class UI extends PureComponent { {layout !== 'mobile' && } - + {!disableHoverCards && } diff --git a/app/javascript/flavours/glitch/initial_state.js b/app/javascript/flavours/glitch/initial_state.js index 4492813fd3a6ce..c5628f51ce475c 100644 --- a/app/javascript/flavours/glitch/initial_state.js +++ b/app/javascript/flavours/glitch/initial_state.js @@ -17,6 +17,7 @@ * @property {boolean} crop_images * @property {boolean=} delete_modal * @property {boolean=} disable_swiping + * @property {boolean=} disable_hover_cards * @property {string=} disabled_account_id * @property {string} display_media * @property {string} domain @@ -106,6 +107,7 @@ export const boostModal = getMeta('boost_modal'); export const cropImages = getMeta('crop_images'); export const deleteModal = getMeta('delete_modal'); export const disableSwiping = getMeta('disable_swiping'); +export const disableHoverCards = getMeta('disable_hover_cards'); export const disabledAccountId = getMeta('disabled_account_id'); export const displayMedia = getMeta('display_media'); export const domain = getMeta('domain'); From 35a437a03f4e1f606afea8953f0be62807da91cc Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Fri, 12 Jul 2024 14:09:52 +0200 Subject: [PATCH 065/101] Destroy `NotificationRequest`s that are dismissed (#31008) --- .../v1/notifications/requests_controller.rb | 8 ++--- app/models/notification_policy.rb | 2 +- app/models/notification_request.rb | 5 ++- ...ve_dismissed_from_notification_requests.rb | 14 ++++++++ db/schema.rb | 4 +-- .../notification_request_fabricator.rb | 1 - spec/models/notification_request_spec.rb | 12 +------ .../api/v1/notifications/requests_spec.rb | 18 ++-------- spec/services/notify_service_spec.rb | 33 +++++++++++++++++++ 9 files changed, 57 insertions(+), 40 deletions(-) create mode 100644 db/post_migrate/20240712064044_remove_dismissed_from_notification_requests.rb diff --git a/app/controllers/api/v1/notifications/requests_controller.rb b/app/controllers/api/v1/notifications/requests_controller.rb index 0e58379a382e85..9ae80c28ed0732 100644 --- a/app/controllers/api/v1/notifications/requests_controller.rb +++ b/app/controllers/api/v1/notifications/requests_controller.rb @@ -28,14 +28,14 @@ def accept end def dismiss - @request.update!(dismissed: true) + @request.destroy! render_empty end private def load_requests - requests = NotificationRequest.where(account: current_account).where(dismissed: truthy_param?(:dismissed) || false).includes(:last_status, from_account: [:account_stat, :user]).to_a_paginated_by_id( + requests = NotificationRequest.where(account: current_account).includes(:last_status, from_account: [:account_stat, :user]).to_a_paginated_by_id( limit_param(DEFAULT_ACCOUNTS_LIMIT), params_slice(:max_id, :since_id, :min_id) ) @@ -68,8 +68,4 @@ def pagination_max_id def pagination_since_id @requests.first.id end - - def pagination_params(core_params) - params.slice(:dismissed).permit(:dismissed).merge(core_params) - end end diff --git a/app/models/notification_policy.rb b/app/models/notification_policy.rb index f10b0c2a816e9e..2bb58004e37fad 100644 --- a/app/models/notification_policy.rb +++ b/app/models/notification_policy.rb @@ -31,6 +31,6 @@ def summarize! private def pending_notification_requests - @pending_notification_requests ||= notification_requests.where(dismissed: false).limit(MAX_MEANINGFUL_COUNT).pick(Arel.sql('count(*), coalesce(sum(notifications_count), 0)::bigint')) + @pending_notification_requests ||= notification_requests.limit(MAX_MEANINGFUL_COUNT).pick(Arel.sql('count(*), coalesce(sum(notifications_count), 0)::bigint')) end end diff --git a/app/models/notification_request.rb b/app/models/notification_request.rb index 6e9cae66255032..2f601ac36b7f82 100644 --- a/app/models/notification_request.rb +++ b/app/models/notification_request.rb @@ -9,12 +9,13 @@ # from_account_id :bigint(8) not null # last_status_id :bigint(8) # notifications_count :bigint(8) default(0), not null -# dismissed :boolean default(FALSE), not null # created_at :datetime not null # updated_at :datetime not null # class NotificationRequest < ApplicationRecord + self.ignored_columns += %w(dismissed) + include Paginable MAX_MEANINGFUL_COUNT = 100 @@ -34,8 +35,6 @@ def self.preload_cache_collection(requests) end def reconsider_existence! - return if dismissed? - prepare_notifications_count if notifications_count.positive? diff --git a/db/post_migrate/20240712064044_remove_dismissed_from_notification_requests.rb b/db/post_migrate/20240712064044_remove_dismissed_from_notification_requests.rb new file mode 100644 index 00000000000000..0d858380730ec4 --- /dev/null +++ b/db/post_migrate/20240712064044_remove_dismissed_from_notification_requests.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class RemoveDismissedFromNotificationRequests < ActiveRecord::Migration[7.1] + def up + safety_assured do + execute 'DELETE FROM notification_requests WHERE dismissed' + remove_column :notification_requests, :dismissed + end + end + + def down + add_column :notification_requests, :dismissed, :boolean, default: false, null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 5f8c7e693f466d..87c49d74152358 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_06_07_094856) do +ActiveRecord::Schema[7.1].define(version: 2024_07_12_064044) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -706,11 +706,9 @@ t.bigint "from_account_id", null: false t.bigint "last_status_id" t.bigint "notifications_count", default: 0, null: false - t.boolean "dismissed", default: false, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["account_id", "from_account_id"], name: "index_notification_requests_on_account_id_and_from_account_id", unique: true - t.index ["account_id", "id"], name: "index_notification_requests_on_account_id_and_id", order: { id: :desc }, where: "(dismissed = false)" t.index ["from_account_id"], name: "index_notification_requests_on_from_account_id" t.index ["last_status_id"], name: "index_notification_requests_on_last_status_id" end diff --git a/spec/fabricators/notification_request_fabricator.rb b/spec/fabricators/notification_request_fabricator.rb index 05a13b8ef8010d..a20d3b3ef2b83d 100644 --- a/spec/fabricators/notification_request_fabricator.rb +++ b/spec/fabricators/notification_request_fabricator.rb @@ -4,5 +4,4 @@ account from_account { Fabricate.build(:account) } last_status { Fabricate.build(:status) } - dismissed false end diff --git a/spec/models/notification_request_spec.rb b/spec/models/notification_request_spec.rb index 07bbc3e0a8e7e9..4adddc194f015c 100644 --- a/spec/models/notification_request_spec.rb +++ b/spec/models/notification_request_spec.rb @@ -4,9 +4,7 @@ RSpec.describe NotificationRequest do describe '#reconsider_existence!' do - subject { Fabricate(:notification_request, dismissed: dismissed) } - - let(:dismissed) { false } + subject { Fabricate(:notification_request) } context 'when there are remaining notifications' do before do @@ -28,14 +26,6 @@ subject.reconsider_existence! end - context 'when dismissed' do - let(:dismissed) { true } - - it 'leaves request intact' do - expect(subject.destroyed?).to be false - end - end - it 'removes the request' do expect(subject.destroyed?).to be true end diff --git a/spec/requests/api/v1/notifications/requests_spec.rb b/spec/requests/api/v1/notifications/requests_spec.rb index 23ddfd2bdabe45..d3a9753246a2ec 100644 --- a/spec/requests/api/v1/notifications/requests_spec.rb +++ b/spec/requests/api/v1/notifications/requests_spec.rb @@ -17,7 +17,6 @@ before do Fabricate(:notification_request, account: user.account) - Fabricate(:notification_request, account: user.account, dismissed: true) end it_behaves_like 'forbidden for wrong scope', 'write write:notifications' @@ -29,16 +28,6 @@ expect(response).to have_http_status(200) end end - - context 'with dismissed' do - let(:params) { { dismissed: '1' } } - - it 'returns http success', :aggregate_failures do - subject - - expect(response).to have_http_status(200) - end - end end describe 'POST /api/v1/notifications/requests/:id/accept' do @@ -78,15 +67,14 @@ post "/api/v1/notifications/requests/#{notification_request.id}/dismiss", headers: headers end - let(:notification_request) { Fabricate(:notification_request, account: user.account) } + let!(:notification_request) { Fabricate(:notification_request, account: user.account) } it_behaves_like 'forbidden for wrong scope', 'read read:notifications' - it 'returns http success and dismisses the notification request', :aggregate_failures do - subject + it 'returns http success and destroys the notification request', :aggregate_failures do + expect { subject }.to change(NotificationRequest, :count).by(-1) expect(response).to have_http_status(200) - expect(notification_request.reload.dismissed?).to be true end context 'when notification request belongs to someone else' do diff --git a/spec/services/notify_service_spec.rb b/spec/services/notify_service_spec.rb index c695855bec0d1c..c7e00129b2cf22 100644 --- a/spec/services/notify_service_spec.rb +++ b/spec/services/notify_service_spec.rb @@ -129,6 +129,39 @@ end end + context 'with filtered notifications' do + let(:unknown) { Fabricate(:account, username: 'unknown') } + let(:status) { Fabricate(:status, account: unknown) } + let(:activity) { Fabricate(:mention, account: recipient, status: status) } + let(:type) { :mention } + + before do + Fabricate(:notification_policy, account: recipient, filter_not_following: true) + end + + it 'creates a filtered notification' do + expect { subject }.to change(Notification, :count) + expect(Notification.last).to be_filtered + end + + context 'when no notification request exists' do + it 'creates a notification request' do + expect { subject }.to change(NotificationRequest, :count) + end + end + + context 'when a notification request exists' do + let!(:notification_request) do + Fabricate(:notification_request, account: recipient, from_account: unknown, last_status: Fabricate(:status, account: unknown)) + end + + it 'updates the existing notification request' do + expect { subject }.to_not change(NotificationRequest, :count) + expect(notification_request.reload.last_status).to eq status + end + end + end + describe NotifyService::DismissCondition do subject { described_class.new(notification) } From b87c41115e99becdfc8a6a66300b4fc6fe39bf3b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 14:20:32 +0200 Subject: [PATCH 066/101] chore(deps): update dependency rubocop-rspec to v3.0.3 (#31009) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5c673a6282094c..98a3571731fedd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -756,7 +756,7 @@ GEM rack (>= 1.1) rubocop (>= 1.33.0, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rspec (3.0.2) + rubocop-rspec (3.0.3) rubocop (~> 1.61) rubocop-rspec_rails (2.30.0) rubocop (~> 1.61) From c953dca1dee71f32b547e68977568cd076a6f7ae Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Fri, 12 Jul 2024 14:23:09 +0200 Subject: [PATCH 067/101] Streaming: use pgPool.query instead of manually acquiring & releasing a connection (#30964) --- streaming/index.js | 79 +++++++++++++++------------------------------- 1 file changed, 25 insertions(+), 54 deletions(-) diff --git a/streaming/index.js b/streaming/index.js index 2ba8b1540781b4..dd9ea0c7f68348 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -524,43 +524,27 @@ const startServer = async () => { * @param {any} req * @returns {Promise} */ - const accountFromToken = (token, req) => new Promise((resolve, reject) => { - pgPool.connect((err, client, done) => { - if (err) { - reject(err); - return; - } - - // @ts-ignore - client.query('SELECT oauth_access_tokens.id, oauth_access_tokens.resource_owner_id, users.account_id, users.chosen_languages, oauth_access_tokens.scopes, devices.device_id FROM oauth_access_tokens INNER JOIN users ON oauth_access_tokens.resource_owner_id = users.id LEFT OUTER JOIN devices ON oauth_access_tokens.id = devices.access_token_id WHERE oauth_access_tokens.token = $1 AND oauth_access_tokens.revoked_at IS NULL LIMIT 1', [token], (err, result) => { - done(); - - if (err) { - reject(err); - return; - } + const accountFromToken = async (token, req) => { + const result = await pgPool.query('SELECT oauth_access_tokens.id, oauth_access_tokens.resource_owner_id, users.account_id, users.chosen_languages, oauth_access_tokens.scopes, devices.device_id FROM oauth_access_tokens INNER JOIN users ON oauth_access_tokens.resource_owner_id = users.id LEFT OUTER JOIN devices ON oauth_access_tokens.id = devices.access_token_id WHERE oauth_access_tokens.token = $1 AND oauth_access_tokens.revoked_at IS NULL LIMIT 1', [token]); - if (result.rows.length === 0) { - reject(new AuthenticationError('Invalid access token')); - return; - } - - req.accessTokenId = result.rows[0].id; - req.scopes = result.rows[0].scopes.split(' '); - req.accountId = result.rows[0].account_id; - req.chosenLanguages = result.rows[0].chosen_languages; - req.deviceId = result.rows[0].device_id; + if (result.rows.length === 0) { + throw new AuthenticationError('Invalid access token'); + } - resolve({ - accessTokenId: result.rows[0].id, - scopes: result.rows[0].scopes.split(' '), - accountId: result.rows[0].account_id, - chosenLanguages: result.rows[0].chosen_languages, - deviceId: result.rows[0].device_id - }); - }); - }); - }); + req.accessTokenId = result.rows[0].id; + req.scopes = result.rows[0].scopes.split(' '); + req.accountId = result.rows[0].account_id; + req.chosenLanguages = result.rows[0].chosen_languages; + req.deviceId = result.rows[0].device_id; + + return { + accessTokenId: result.rows[0].id, + scopes: result.rows[0].scopes.split(' '), + accountId: result.rows[0].account_id, + chosenLanguages: result.rows[0].chosen_languages, + deviceId: result.rows[0].device_id + }; + }; /** * @param {any} req @@ -771,28 +755,15 @@ const startServer = async () => { * @param {any} req * @returns {Promise.} */ - const authorizeListAccess = (listId, req) => new Promise((resolve, reject) => { + const authorizeListAccess = async (listId, req) => { const { accountId } = req; - pgPool.connect((err, client, done) => { - if (err) { - reject(); - return; - } - - // @ts-ignore - client.query('SELECT id, account_id FROM lists WHERE id = $1 LIMIT 1', [listId], (err, result) => { - done(); - - if (err || result.rows.length === 0 || result.rows[0].account_id !== accountId) { - reject(); - return; - } + const result = await pgPool.query('SELECT id, account_id FROM lists WHERE id = $1 AND account_id = $2 LIMIT 1', [listId, accountId]); - resolve(); - }); - }); - }); + if (result.rows.length === 0) { + throw new AuthenticationError('List not found'); + } + }; /** * @param {string[]} channelIds From 00cb4a0313190bfa118966692a649db9c8328094 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 12 Jul 2024 10:09:16 -0400 Subject: [PATCH 068/101] Avoid repeated factory creation in media_attachments_vacuum_spec (#31000) --- .../vacuum/media_attachments_vacuum_spec.rb | 41 +++++++------------ 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/spec/lib/vacuum/media_attachments_vacuum_spec.rb b/spec/lib/vacuum/media_attachments_vacuum_spec.rb index 3c17ecb0003f83..1039c36cea6ff2 100644 --- a/spec/lib/vacuum/media_attachments_vacuum_spec.rb +++ b/spec/lib/vacuum/media_attachments_vacuum_spec.rb @@ -17,32 +17,21 @@ let!(:old_unattached_media) { Fabricate(:media_attachment, account_id: nil, created_at: 10.days.ago) } let!(:new_unattached_media) { Fabricate(:media_attachment, account_id: nil, created_at: 1.hour.ago) } - before do - subject.perform - end - - it 'deletes cache of remote media attachments past the retention period' do - expect(old_remote_media.reload.file).to be_blank - end - - it 'does not touch local media attachments past the retention period' do - expect(old_local_media.reload.file).to_not be_blank - end - - it 'does not delete cache of remote media attachments within the retention period' do - expect(new_remote_media.reload.file).to_not be_blank - end - - it 'does not touch local media attachments within the retention period' do - expect(new_local_media.reload.file).to_not be_blank - end - - it 'deletes unattached media attachments past TTL' do - expect { old_unattached_media.reload }.to raise_error(ActiveRecord::RecordNotFound) - end - - it 'does not delete unattached media attachments within TTL' do - expect(new_unattached_media.reload).to be_persisted + before { subject.perform } + + it 'handles attachments based on metadata details' do + expect(old_remote_media.reload.file) # Remote and past retention period + .to be_blank + expect(old_local_media.reload.file) # Local and past retention + .to_not be_blank + expect(new_remote_media.reload.file) # Remote and within retention + .to_not be_blank + expect(new_local_media.reload.file) # Local and within retention + .to_not be_blank + expect { old_unattached_media.reload } # Unattached and past TTL + .to raise_error(ActiveRecord::RecordNotFound) + expect(new_unattached_media.reload) # Unattached and within TTL + .to be_persisted end end end From d61b536df30c6368a45fd4750c01322b14011b48 Mon Sep 17 00:00:00 2001 From: mogaminsk Date: Mon, 15 Jul 2024 16:38:48 +0900 Subject: [PATCH 069/101] Add i18n strings for instance favicon and logo settings label (#31016) --- config/locales/simple_form.en.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 0313d4683edc66..6bc7c6ac525e9f 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -243,11 +243,13 @@ en: warn: Hide with a warning form_admin_settings: activity_api_enabled: Publish aggregate statistics about user activity in the API + app_icon: App icon backups_retention_period: User archive retention period bootstrap_timeline_accounts: Always recommend these accounts to new users closed_registrations_message: Custom message when sign-ups are not available content_cache_retention_period: Remote content retention period custom_css: Custom CSS + favicon: Favicon mascot: Custom mascot (legacy) media_cache_retention_period: Media cache retention period peers_api_enabled: Publish list of discovered servers in the API From 17117109adacd5811317b4927594992a9c5ddffb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 07:44:37 +0000 Subject: [PATCH 070/101] New Crowdin Translations (automated) (#31018) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/ar.json | 1 + app/javascript/mastodon/locales/de.json | 8 ++++---- app/javascript/mastodon/locales/ja.json | 4 ++++ app/javascript/mastodon/locales/si.json | 17 ++++++++++++++++ app/javascript/mastodon/locales/tok.json | 25 ++++++++++++++++++++++++ config/locales/devise.nl.yml | 2 +- config/locales/doorkeeper.nl.yml | 2 +- config/locales/ja.yml | 1 + config/locales/simple_form.ca.yml | 1 + config/locales/simple_form.eo.yml | 1 + config/locales/simple_form.he.yml | 1 + config/locales/simple_form.ja.yml | 1 + config/locales/simple_form.ko.yml | 1 + config/locales/simple_form.nl.yml | 2 +- config/locales/simple_form.sl.yml | 2 +- config/locales/simple_form.sq.yml | 1 + config/locales/simple_form.th.yml | 1 + config/locales/simple_form.uk.yml | 1 + config/locales/simple_form.zh-CN.yml | 1 + 19 files changed, 65 insertions(+), 8 deletions(-) diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index 115f6335b6f12c..5509769e04085b 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -35,6 +35,7 @@ "account.follow_back": "تابعهم بالمثل", "account.followers": "مُتابِعون", "account.followers.empty": "لا أحدَ يُتابع هذا المُستخدم إلى حد الآن.", + "account.followers_counter": "{count, plural, zero {}one {{counter} متابع} two {{counter} متابعين} few {{counter} متابعين} many {{counter} متابعين} other {{counter} متابعين}}", "account.following": "الاشتراكات", "account.follows.empty": "لا يُتابع هذا المُستخدمُ أيَّ أحدٍ حتى الآن.", "account.go_to_profile": "اذهب إلى الملف الشخصي", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 86438757a30755..0f3b3c91a9bfd0 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -205,10 +205,10 @@ "disabled_account_banner.text": "Dein Konto {disabledAccount} ist derzeit deaktiviert.", "dismissable_banner.community_timeline": "Das sind die neuesten öffentlichen Beiträge von Profilen, deren Konten von {domain} verwaltet werden.", "dismissable_banner.dismiss": "Ablehnen", - "dismissable_banner.explore_links": "Diese Nachrichten werden heute am häufigsten im sozialen Netzwerk geteilt. Neuere Nachrichten, die von vielen verschiedenen Profilen veröffentlicht wurden, werden höher eingestuft.", - "dismissable_banner.explore_statuses": "Diese Beiträge stammen aus dem gesamten sozialen Netzwerk und gewinnen derzeit an Reichweite. Neuere Beiträge, die häufiger geteilt und favorisiert wurden, werden höher eingestuft.", - "dismissable_banner.explore_tags": "Das sind Hashtags, die derzeit an Reichweite gewinnen. Hashtags, die von vielen verschiedenen Profilen verwendet werden, werden höher eingestuft.", - "dismissable_banner.public_timeline": "Das sind die neuesten öffentlichen Beiträge von Profilen im sozialen Netzwerk, denen Leute auf {domain} folgen.", + "dismissable_banner.explore_links": "Diese Nachrichten werden heute am häufigsten im Social Web geteilt. Neuere Nachrichten, die von vielen verschiedenen Profilen geteilt wurden, erscheinen weiter oben.", + "dismissable_banner.explore_statuses": "Diese Beiträge sind heute im Social Web sehr beliebt. Neuere Beiträge, die häufiger geteilt und favorisiert wurden, erscheinen weiter oben.", + "dismissable_banner.explore_tags": "Diese Hashtags sind heute im Social Web sehr beliebt. Hashtags, die von vielen verschiedenen Profilen verwendet werden, erscheinen weiter oben.", + "dismissable_banner.public_timeline": "Das sind die neuesten öffentlichen Beiträge von Profilen im Social Web, denen Leute auf {domain} folgen.", "domain_block_modal.block": "Server blockieren", "domain_block_modal.block_account_instead": "Stattdessen @{name} blockieren", "domain_block_modal.they_can_interact_with_old_posts": "Profile von diesem Server werden mit deinen älteren Beiträgen interagieren können.", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index f3ba028eabde00..36974c76026ecc 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -35,7 +35,9 @@ "account.follow_back": "フォローバック", "account.followers": "フォロワー", "account.followers.empty": "まだ誰もフォローしていません。", + "account.followers_counter": "{count, plural, other {{counter} フォロワー}}", "account.following": "フォロー中", + "account.following_counter": "{count, plural, other {{counter} フォロー}}", "account.follows.empty": "まだ誰もフォローしていません。", "account.go_to_profile": "プロフィールページへ", "account.hide_reblogs": "@{name}さんからのブーストを非表示", @@ -61,6 +63,7 @@ "account.requested_follow": "{name}さんがあなたにフォローリクエストしました", "account.share": "@{name}さんのプロフィールを共有する", "account.show_reblogs": "@{name}さんからのブーストを表示", + "account.statuses_counter": "{count, plural, other {{counter} 投稿}}", "account.unblock": "@{name}さんのブロックを解除", "account.unblock_domain": "{domain}のブロックを解除", "account.unblock_short": "ブロック解除", @@ -412,6 +415,7 @@ "limited_account_hint.title": "このプロフィールは{domain}のモデレーターによって非表示にされています。", "link_preview.author": "{name}", "link_preview.more_from_author": "{name}さんの投稿をもっと読む", + "link_preview.shares": "{count, plural, other {{counter}件の投稿}}", "lists.account.add": "リストに追加", "lists.account.remove": "リストから外す", "lists.delete": "リストを削除", diff --git a/app/javascript/mastodon/locales/si.json b/app/javascript/mastodon/locales/si.json index fbfdfaa659d131..3a67beed527a59 100644 --- a/app/javascript/mastodon/locales/si.json +++ b/app/javascript/mastodon/locales/si.json @@ -33,6 +33,7 @@ "account.mute": "@{name} නිහඬ කරන්න", "account.mute_short": "නිහඬ", "account.muted": "නිහඬ කළා", + "account.open_original_page": "මුල් පිටුව අරින්න", "account.posts": "ලිපි", "account.posts_with_replies": "ලිපි සහ පිළිතුරු", "account.report": "@{name} වාර්තා කරන්න", @@ -51,6 +52,10 @@ "alert.unexpected.title": "අපොයි!", "announcement.announcement": "නිවේදනය", "audio.hide": "හඬපටය සඟවන්න", + "block_modal.show_less": "අඩුවෙන් පෙන්වන්න", + "block_modal.show_more": "තව පෙන්වන්න", + "block_modal.they_will_know": "අවහිර කළ බව දකිනු ඇත.", + "block_modal.title": "අවහිර කරන්නද?", "boost_modal.combo": "ඊළඟ වතාවේ මෙය මඟ හැරීමට {combo} එබීමට හැකිය", "bundle_column_error.copy_stacktrace": "දෝෂ වාර්තාවේ පිටපතක්", "bundle_column_error.error.title": "අපොයි!", @@ -100,10 +105,13 @@ "compose_form.lock_disclaimer.lock": "අගුළු දමා ඇත", "compose_form.placeholder": "ඔබගේ සිතුවිලි මොනවාද?", "compose_form.poll.duration": "මත විමසීමේ කාලය", + "compose_form.poll.option_placeholder": "විකල්පය {number}", "compose_form.poll.switch_to_multiple": "තේරීම් කිහිපයකට මත විමසුම වෙනස් කරන්න", "compose_form.poll.switch_to_single": "තනි තේරීමකට මත විමසුම වෙනස් කරන්න", + "compose_form.poll.type": "ශෛලිය", "compose_form.publish": "ප්‍රකාශනය", "compose_form.publish_form": "නව ලිපිය", + "compose_form.reply": "පිළිතුරු", "compose_form.spoiler.marked": "අන්තර්ගත අවවාදය ඉවත් කරන්න", "compose_form.spoiler.unmarked": "අන්තර්ගත අවවාදයක් එක් කරන්න", "confirmation_modal.cancel": "අවලංගු", @@ -123,6 +131,7 @@ "conversation.mark_as_read": "කියවූ බව යොදන්න", "conversation.open": "සංවාදය බලන්න", "conversation.with": "{names} සමඟ", + "copy_icon_button.copied": "පසුරුපුවරුවට පිටපත් විය", "copypaste.copied": "පිටපත් විය", "copypaste.copy_to_clipboard": "පසුරුපුවරුවට පිටපතක්", "directory.federated": "දන්නා ෆෙඩිවර්ස් වෙතින්", @@ -130,6 +139,9 @@ "directory.new_arrivals": "නව පැමිණීම්", "directory.recently_active": "මෑත දී සක්‍රියයි", "disabled_account_banner.account_settings": "ගිණුමේ සැකසුම්", + "dismissable_banner.dismiss": "ඉවතලන්න", + "domain_pill.server": "සේවාදායකය", + "domain_pill.username": "පරිශ්‍රීලක නාමය", "embed.instructions": "පහත කේතය පිටපත් කිරීමෙන් මෙම ලිපිය ඔබගේ අඩවියට කාවද්දන්න.", "embed.preview": "මෙන්න එය පෙනෙන අන්දම:", "emoji_button.activity": "ක්‍රියාකාරකම", @@ -178,9 +190,13 @@ "filter_modal.select_filter.search": "සොයන්න හෝ සාදන්න", "filter_modal.select_filter.title": "මෙම ලිපිය පෙරන්න", "filter_modal.title.status": "ලිපියක් පෙරන්න", + "filtered_notifications_banner.title": "පෙරූ දැනුම්දීම්", + "firehose.all": "සියල්ල", "firehose.local": "මෙම සේවාදායකය", "firehose.remote": "වෙනත් සේවාදායක", "follow_request.reject": "ප්‍රතික්‍ෂේප", + "follow_suggestions.dismiss": "නැවත පෙන්වන්න එපා", + "follow_suggestions.view_all": "සියල්ල බලන්න", "footer.about": "පිළිබඳව", "footer.directory": "පැතිකඩ නාමාවලිය", "footer.get_app": "යෙදුම ගන්න", @@ -202,6 +218,7 @@ "home.pending_critical_update.link": "යාවත්කාල බලන්න", "home.show_announcements": "නිවේදන පෙන්වන්න", "interaction_modal.login.action": "මුලට ගෙනයන්න", + "interaction_modal.on_another_server": "වෙනත් සේවාදායකයක", "interaction_modal.on_this_server": "මෙම සේවාදායකයෙහි", "interaction_modal.title.favourite": "{name}ගේ ලිපිය ප්‍රිය කරන්න", "interaction_modal.title.follow": "{name} අනුගමනය", diff --git a/app/javascript/mastodon/locales/tok.json b/app/javascript/mastodon/locales/tok.json index 80d412a20bc35a..fcdef5f32ce744 100644 --- a/app/javascript/mastodon/locales/tok.json +++ b/app/javascript/mastodon/locales/tok.json @@ -170,6 +170,8 @@ "domain_block_modal.block": "o weka e ma", "domain_block_modal.you_will_lose_followers": "ma ni la jan alasa ale sina li weka", "domain_block_modal.you_wont_see_posts": "sina ken ala lukin e toki tan jan pi ma ni", + "domain_pill.server": "ma", + "domain_pill.username": "nimi jan", "embed.preview": "ni li jo e sitelen ni:", "emoji_button.activity": "musi", "emoji_button.flags": "len ma", @@ -274,6 +276,7 @@ "load_pending": "{count, plural, other {ijo sin #}}", "loading_indicator.label": "ni li kama…", "media_gallery.toggle_visible": "{number, plural, other {o len e sitelen}}", + "mute_modal.title": "sina wile ala wile kute e jan ni?", "navigation_bar.about": "sona", "navigation_bar.blocks": "jan weka", "navigation_bar.compose": "o pali e toki sin", @@ -290,24 +293,33 @@ "notification.follow": " {name} li kute e sina", "notification.follow_request": "{name} li wile kute e sina", "notification.mention": "jan {name} li toki e sina", + "notification.moderation-warning.learn_more": "o kama sona e ijo ante", "notification.poll": "sina pana lon pana la pana ni li pini", "notification.reblog": "{name} li wawa e toki sina", "notification.status": "{name} li toki", "notification.update": "{name} li ante e toki", + "notification_requests.dismiss": "o weka", "notifications.column_settings.favourite": "ijo pona:", "notifications.column_settings.follow": "jan kute sin", "notifications.column_settings.poll": "pana lon pana ni:", "notifications.column_settings.reblog": "wawa:", + "notifications.column_settings.status": "toki sin:", "notifications.column_settings.update": "ante toki:", "notifications.filter.all": "ale", + "notifications.filter.boosts": "wawa", "notifications.filter.favourites": "ijo pona", + "notifications.filter.mentions": "toki pi toki sina", "notifications.filter.polls": "pana lon pana ni", + "onboarding.action.back": "o tawa monsi", + "onboarding.actions.back": "o tawa monsi", "onboarding.compose.template": "toki a, #Mastodon o!", "onboarding.profile.display_name": "nimi tawa jan ante", + "onboarding.profile.note": "sona sina", "onboarding.share.lead": "o toki lon nasin Masoton pi alasa sina tawa jan", "onboarding.share.message": "ilo #Mastodon la mi jan {username} a! o kute e mi lon ni: {url}", "onboarding.start.title": "sina o kama pona a!", "onboarding.tips.migration": "sina sona ala sona e ni? tenpo kama la sina pilin ike tawa ma {domain} la, sina ken tawa ma ante lon ilo Masoton. jan li kute e sina la jan ni li awen kute e sina. kin la sina ken lawa e ma pi sina taso a!", + "poll.closed": "ona li pini", "poll.total_people": "{count, plural, other {jan #}}", "poll.total_votes": "{count, plural, other {pana #}}", "poll.vote": "o pana", @@ -315,9 +327,15 @@ "poll.votes": "{votes, plural, other {pana #}}", "privacy.direct.long": "jan ale lon toki", "privacy.public.short": "tawa ale", + "regeneration_indicator.label": "ni li kama…", + "relative_time.days": "{number}d", "relative_time.full.just_now": "tenpo ni", + "relative_time.hours": "{number}h", "relative_time.just_now": "tenpo ni", + "relative_time.minutes": "{number}m", + "relative_time.seconds": "{number}s", "relative_time.today": "tenpo suno ni", + "reply_indicator.cancel": "o ala", "report.block": "o weka e jan", "report.block_explanation": "sina kama lukin ala e toki ona. ona li kama ala ken lukin e toki sina li kama ala ken kute e sina. ona li ken sona e kama ni.", "report.categories.other": "ante", @@ -336,6 +354,7 @@ "report.thanks.title": "sina wile ala lukin e ni anu seme?", "report.unfollow": "o pini kute e {name}", "report_notification.categories.legal": "ike tawa nasin lawa", + "report_notification.categories.other": "ante", "search.placeholder": "o alasa", "search.quick_action.go_to_account": "o tawa lipu jan {x}", "search_popout.language_code": "nimi toki kepeken nasin ISO", @@ -343,6 +362,7 @@ "search_results.see_all": "ale", "search_results.statuses": "toki", "search_results.title": "o alasa e {q}", + "server_banner.administered_by": "jan lawa:", "status.block": "o weka e @{name}", "status.cancel_reblog_private": "o pini e pana", "status.delete": "o weka", @@ -356,12 +376,14 @@ "status.media.open": "o open", "status.media.show": "o lukin", "status.media_hidden": "sitelen li len", + "status.more": "kin", "status.mute": "o len e @{name}", "status.mute_conversation": "o kute ala e ijo pi toki ni", "status.pin": "o sewi lon lipu sina", "status.pinned": "toki sewi", "status.reblog": "o wawa", "status.share": "o pana tawa ante", + "status.show_filter_reason": "o lukin", "status.show_less": "o lili e ni", "status.show_less_all": "o lili e ale", "status.show_more": "o suli e ni", @@ -378,7 +400,9 @@ "timeline_hint.resources.follows": "jan lukin", "timeline_hint.resources.statuses": "ijo pi tenpo suli", "trends.trending_now": "jan mute li toki", + "units.short.billion": "{count}B", "units.short.million": "{count}AAA", + "units.short.thousand": "{count}K", "upload_button.label": "o pana e sitelen anu kalama", "upload_error.limit": "ilo li ken ala e suli pi ijo ni.", "upload_form.audio_description": "o toki e ijo kute tawa jan pi kute ala, tawa jan pi kute lili", @@ -386,6 +410,7 @@ "upload_form.edit": "o ante", "upload_form.thumbnail": "o ante e sitelen lili", "upload_form.video_description": "o toki e ijo kute tawa jan pi kute ala, tawa jan pi kute lili, e ijo lukin tawa jan pi lukin ala, tawa jan pi lukin lili", + "upload_modal.analyzing_picture": "ilo li lukin e sitelen...", "upload_modal.choose_image": "o wile e sitelen", "upload_modal.description_placeholder": "mi pu jaki tan soweli", "upload_modal.detect_text": "ilo o alasa e nimi tan sitelen", diff --git a/config/locales/devise.nl.yml b/config/locales/devise.nl.yml index ab6ae84db4d229..662401a57a679a 100644 --- a/config/locales/devise.nl.yml +++ b/config/locales/devise.nl.yml @@ -75,7 +75,7 @@ nl: title: Een van jouw beveiligingssleutels is verwijderd webauthn_disabled: explanation: Verificatie met beveiligingssleutels is uitgeschakeld voor je account. - extra: Het is nu alleen mogelijk om in te loggen met een door de authenticatie-app gegeneerde toegangscode. + extra: Het is nu alleen mogelijk om in te loggen met een door de authenticatie-app gegeneerde toegangscode als extra controle. subject: 'Mastodon: Verificatie met beveiligingssleutels is uitgeschakeld' title: Beveiligingssleutels uitgeschakeld webauthn_enabled: diff --git a/config/locales/doorkeeper.nl.yml b/config/locales/doorkeeper.nl.yml index 4115e0a17eb341..0a3d8fd081a81d 100644 --- a/config/locales/doorkeeper.nl.yml +++ b/config/locales/doorkeeper.nl.yml @@ -129,7 +129,7 @@ nl: crypto: End-to-end-encryptie favourites: Favorieten filters: Filters - follow: Volgers, genegeerde en geblokkeerde gebruikers + follow: Gevolgde, genegeerde en geblokkeerde gebruikers follows: Volgend lists: Lijsten media: Mediabijlagen diff --git a/config/locales/ja.yml b/config/locales/ja.yml index ec6963517abdcb..5142b550321c48 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -290,6 +290,7 @@ ja: filter_by_action: アクションでフィルター filter_by_user: ユーザーでフィルター title: 操作履歴 + unavailable_instance: "(ドメイン名が利用できません)" announcements: destroyed_msg: お知らせが削除されました edit: diff --git a/config/locales/simple_form.ca.yml b/config/locales/simple_form.ca.yml index d3dc4b13f113f2..4bb906abdbd897 100644 --- a/config/locales/simple_form.ca.yml +++ b/config/locales/simple_form.ca.yml @@ -211,6 +211,7 @@ ca: setting_default_privacy: Privacitat dels tuts setting_default_sensitive: Marcar sempre el contingut gràfic com a sensible setting_delete_modal: Mostra la finestra de confirmació abans d'esborrar un tut + setting_disable_hover_cards: Deshabilita la vista prèvia del perfil en passar-hi per sobre setting_disable_swiping: Desactiva les animacions setting_display_media: Visualització multimèdia setting_display_media_default: Per defecte diff --git a/config/locales/simple_form.eo.yml b/config/locales/simple_form.eo.yml index e83f71a2cb5210..40535bf96bc12e 100644 --- a/config/locales/simple_form.eo.yml +++ b/config/locales/simple_form.eo.yml @@ -203,6 +203,7 @@ eo: setting_default_privacy: Privateco de afiŝado setting_default_sensitive: Ĉiam marki plurmediojn kiel tiklaj setting_delete_modal: Montri konfirman fenestron antaŭ ol forigi mesaĝon + setting_disable_hover_cards: Malebligi profilan antaŭmontron kiam oni musumas setting_disable_swiping: Malebligi svingajn movojn setting_display_media: Montrado de plurmedioj setting_display_media_default: Implicita diff --git a/config/locales/simple_form.he.yml b/config/locales/simple_form.he.yml index 65c6f6110fc2bf..1331d2f62bc157 100644 --- a/config/locales/simple_form.he.yml +++ b/config/locales/simple_form.he.yml @@ -211,6 +211,7 @@ he: setting_default_privacy: פרטיות ההודעות setting_default_sensitive: תמיד לתת סימון "רגיש" למדיה setting_delete_modal: להראות תיבת אישור לפני מחיקת חיצרוץ + setting_disable_hover_cards: כבה הצצה מקדימה לפרופיל בעת מעבר עכבר מעליו setting_disable_swiping: ביטול החלקת-צד setting_display_media: תצוגת מדיה setting_display_media_default: ברירת מחדל diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index 664082dabc41ce..56b17ab000718b 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -211,6 +211,7 @@ ja: setting_default_privacy: 投稿の公開範囲 setting_default_sensitive: メディアを常に閲覧注意としてマークする setting_delete_modal: 投稿を削除する前に確認ダイアログを表示する + setting_disable_hover_cards: マウスオーバーでプロフィールをポップアップしない setting_disable_swiping: スワイプでの切り替えを無効にする setting_display_media: メディアの表示 setting_display_media_default: 標準 diff --git a/config/locales/simple_form.ko.yml b/config/locales/simple_form.ko.yml index 54d36eafe0393f..1107e4cba5b736 100644 --- a/config/locales/simple_form.ko.yml +++ b/config/locales/simple_form.ko.yml @@ -211,6 +211,7 @@ ko: setting_default_privacy: 게시물 프라이버시 setting_default_sensitive: 미디어를 언제나 민감한 콘텐츠로 설정 setting_delete_modal: 게시물 삭제 전 확인 창을 표시 + setting_disable_hover_cards: 호버시 프로필 미리보기를 비활성화 setting_disable_swiping: 스와이프 모션 비활성화 setting_display_media: 미디어 표시 setting_display_media_default: 기본 diff --git a/config/locales/simple_form.nl.yml b/config/locales/simple_form.nl.yml index d6bdb7a6f0a488..5110e1e6d09b9a 100644 --- a/config/locales/simple_form.nl.yml +++ b/config/locales/simple_form.nl.yml @@ -211,7 +211,7 @@ nl: setting_default_privacy: Zichtbaarheid van nieuwe berichten setting_default_sensitive: Media altijd als gevoelig markeren setting_delete_modal: Vraag voor het verwijderen van een bericht een bevestiging - setting_disable_hover_cards: Profielvoorbeeld uitschakelen bij zweven + setting_disable_hover_cards: Profielvoorbeelden door eroverheen te zweven uitschakelen setting_disable_swiping: Swipebewegingen uitschakelen setting_display_media: Mediaweergave setting_display_media_default: Standaard diff --git a/config/locales/simple_form.sl.yml b/config/locales/simple_form.sl.yml index 1e3f3046ad3256..14551774fe0b29 100644 --- a/config/locales/simple_form.sl.yml +++ b/config/locales/simple_form.sl.yml @@ -211,7 +211,7 @@ sl: setting_default_privacy: Zasebnost objave setting_default_sensitive: Vedno označi medije kot občutljive setting_delete_modal: Pred brisanjem objave prikaži okno za pritrditev - setting_disable_hover_cards: Onemogoči predogled profila pod kazalko + setting_disable_hover_cards: Onemogoči predogled profila pod kazalcem setting_disable_swiping: Onemogoči poteze drsanja setting_display_media: Prikaz medijev setting_display_media_default: Privzeto diff --git a/config/locales/simple_form.sq.yml b/config/locales/simple_form.sq.yml index c3c3920962d635..6fedf315724746 100644 --- a/config/locales/simple_form.sq.yml +++ b/config/locales/simple_form.sq.yml @@ -211,6 +211,7 @@ sq: setting_default_privacy: Privatësi postimi setting_default_sensitive: Mediave vëru përherë shenjë si rezervat setting_delete_modal: Shfaq dialog ripohimi përpara fshirjes së një mesazhi + setting_disable_hover_cards: Çaktivizo paraparje profili, kur i kalohet kursori përsipër setting_disable_swiping: Çaktivizo lëvizje me fërkim setting_display_media: Shfaqje mediash setting_display_media_default: Parazgjedhje diff --git a/config/locales/simple_form.th.yml b/config/locales/simple_form.th.yml index 0d9a88bc380ff7..cdc82367f9346c 100644 --- a/config/locales/simple_form.th.yml +++ b/config/locales/simple_form.th.yml @@ -211,6 +211,7 @@ th: setting_default_privacy: ความเป็นส่วนตัวของการโพสต์ setting_default_sensitive: ทำเครื่องหมายสื่อว่าละเอียดอ่อนเสมอ setting_delete_modal: แสดงกล่องโต้ตอบการยืนยันก่อนลบโพสต์ + setting_disable_hover_cards: ปิดใช้งานตัวอย่างโปรไฟล์เมื่อวางเมาส์เหนือ setting_disable_swiping: ปิดใช้งานการเคลื่อนไหวในการปัด setting_display_media: การแสดงสื่อ setting_display_media_default: ค่าเริ่มต้น diff --git a/config/locales/simple_form.uk.yml b/config/locales/simple_form.uk.yml index 11337f2f61f689..332f2c91dc9daf 100644 --- a/config/locales/simple_form.uk.yml +++ b/config/locales/simple_form.uk.yml @@ -211,6 +211,7 @@ uk: setting_default_privacy: Видимість дописів setting_default_sensitive: Позначати медіа делікатними setting_delete_modal: Показувати діалог підтвердження під час видалення допису + setting_disable_hover_cards: Вимкнути попередній перегляд профілю при наведенні setting_disable_swiping: Вимкнути рух посування setting_display_media: Показ медіа setting_display_media_default: За промовчанням diff --git a/config/locales/simple_form.zh-CN.yml b/config/locales/simple_form.zh-CN.yml index 6058cc00cff3ac..bbee8b6706297b 100644 --- a/config/locales/simple_form.zh-CN.yml +++ b/config/locales/simple_form.zh-CN.yml @@ -211,6 +211,7 @@ zh-CN: setting_default_privacy: 嘟文默认可见范围 setting_default_sensitive: 始终标记媒体为敏感内容 setting_delete_modal: 在删除嘟文前询问我 + setting_disable_hover_cards: 禁用悬停资料预览 setting_disable_swiping: 禁用滑动动作 setting_display_media: 媒体显示 setting_display_media_default: 默认 From 1dd82620712eecc18f1acd72b5a8585d37e254cf Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 15 Jul 2024 11:29:57 +0200 Subject: [PATCH 071/101] Add optional `filtered` attribute to notification entities in REST API (#31011) --- .../rest/notification_serializer.rb | 4 +++ spec/requests/api/v1/notifications_spec.rb | 26 ++++++++++++++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/app/serializers/rest/notification_serializer.rb b/app/serializers/rest/notification_serializer.rb index 966819585fcc7c..ee17af80768439 100644 --- a/app/serializers/rest/notification_serializer.rb +++ b/app/serializers/rest/notification_serializer.rb @@ -3,6 +3,8 @@ class REST::NotificationSerializer < ActiveModel::Serializer attributes :id, :type, :created_at, :group_key + attribute :filtered, if: :filtered? + belongs_to :from_account, key: :account, serializer: REST::AccountSerializer belongs_to :target_status, key: :status, if: :status_type?, serializer: REST::StatusSerializer belongs_to :report, if: :report_type?, serializer: REST::ReportSerializer @@ -32,4 +34,6 @@ def relationship_severance_event? def moderation_warning_event? object.type == :moderation_warning end + + delegate :filtered?, to: :object end diff --git a/spec/requests/api/v1/notifications_spec.rb b/spec/requests/api/v1/notifications_spec.rb index 9ff0674317bedd..c9034c17dcbd97 100644 --- a/spec/requests/api/v1/notifications_spec.rb +++ b/spec/requests/api/v1/notifications_spec.rb @@ -20,8 +20,8 @@ before do first_status = PostStatusService.new.call(user.account, text: 'Test') ReblogService.new.call(bob.account, first_status) - mentioning_status = PostStatusService.new.call(bob.account, text: 'Hello @alice') - mentioning_status.mentions.first + PostStatusService.new.call(bob.account, text: 'Hello @alice') + PostStatusService.new.call(tom.account, text: 'Hello @alice', visibility: :direct) # Filtered by default FavouriteService.new.call(bob.account, first_status) FavouriteService.new.call(tom.account, first_status) FollowService.new.call(bob.account, user.account) @@ -34,10 +34,22 @@ subject expect(response).to have_http_status(200) - expect(body_json_types).to include 'reblog' - expect(body_json_types).to include 'mention' - expect(body_json_types).to include 'favourite' - expect(body_json_types).to include 'follow' + expect(body_as_json.size).to eq 5 + expect(body_json_types).to include('reblog', 'mention', 'favourite', 'follow') + expect(body_as_json.any? { |x| x[:filtered] }).to be false + end + end + + context 'with include_filtered' do + let(:params) { { include_filtered: true } } + + it 'returns expected notification types, including filtered notifications' do + subject + + expect(response).to have_http_status(200) + expect(body_as_json.size).to eq 6 + expect(body_json_types).to include('reblog', 'mention', 'favourite', 'follow') + expect(body_as_json.any? { |x| x[:filtered] }).to be true end end @@ -96,7 +108,7 @@ def body_json_account_ids it 'returns the requested number of notifications paginated', :aggregate_failures do subject - notifications = user.account.notifications + notifications = user.account.notifications.browserable expect(body_as_json.size) .to eq(params[:limit]) From 91ef4a6fc5d8536378a2d3bc456711bcb95bd8f4 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 15 Jul 2024 06:42:18 -0400 Subject: [PATCH 072/101] Add assertion about key columns to account key generate spec (#30635) --- spec/models/account_spec.rb | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 225929ae39f9a6..dfb1f5bc61528c 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -1015,21 +1015,26 @@ def account_note_over_limit context 'when is local' do it 'generates keys' do - account = described_class.create!(domain: nil, username: Faker::Internet.user_name(separators: ['_'])) - expect(account.keypair).to be_private - expect(account.keypair).to be_public + account = described_class.create!(domain: nil, username: 'user_without_keys') + + expect(account) + .to be_private_key + .and be_public_key + expect(account.keypair) + .to be_private + .and be_public end end context 'when is remote' do it 'does not generate keys' do key = OpenSSL::PKey::RSA.new(1024).public_key - account = described_class.create!(domain: 'remote', uri: 'https://remote/actor', username: Faker::Internet.user_name(separators: ['_']), public_key: key.to_pem) + account = described_class.create!(domain: 'remote', uri: 'https://remote/actor', username: 'remote_user_with_public', public_key: key.to_pem) expect(account.keypair.params).to eq key.params end it 'normalizes domain' do - account = described_class.create!(domain: 'にゃん', uri: 'https://xn--r9j5b5b/actor', username: Faker::Internet.user_name(separators: ['_'])) + account = described_class.create!(domain: 'にゃん', uri: 'https://xn--r9j5b5b/actor', username: 'remote_user_with_idn_domain') expect(account.domain).to eq 'xn--r9j5b5b' end end From 543b59fa14a7e9b55a5a96d2cb0f38391108a561 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 15 Jul 2024 09:03:23 -0400 Subject: [PATCH 073/101] Clean up `theme_helper` style builders (#30617) --- app/helpers/theme_helper.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/helpers/theme_helper.rb b/app/helpers/theme_helper.rb index d15259851c0315..fab899a5332ebd 100644 --- a/app/helpers/theme_helper.rb +++ b/app/helpers/theme_helper.rb @@ -3,8 +3,10 @@ module ThemeHelper def theme_style_tags(theme) if theme == 'system' - stylesheet_pack_tag('mastodon-light', media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous') + - stylesheet_pack_tag('default', media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous') + ''.html_safe.tap do |tags| + tags << stylesheet_pack_tag('mastodon-light', media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous') + tags << stylesheet_pack_tag('default', media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous') + end else stylesheet_pack_tag theme, media: 'all', crossorigin: 'anonymous' end @@ -12,8 +14,10 @@ def theme_style_tags(theme) def theme_color_tags(theme) if theme == 'system' - tag.meta(name: 'theme-color', content: Themes::THEME_COLORS[:dark], media: '(prefers-color-scheme: dark)') + - tag.meta(name: 'theme-color', content: Themes::THEME_COLORS[:light], media: '(prefers-color-scheme: light)') + ''.html_safe.tap do |tags| + tags << tag.meta(name: 'theme-color', content: Themes::THEME_COLORS[:dark], media: '(prefers-color-scheme: dark)') + tags << tag.meta(name: 'theme-color', content: Themes::THEME_COLORS[:light], media: '(prefers-color-scheme: light)') + end else tag.meta name: 'theme-color', content: theme_color_for(theme) end From 0b97ec3f7b0922067c1047d621975e65bfe96ad7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 15:03:57 +0200 Subject: [PATCH 074/101] fix(deps): update dependency @babel/core to v7.24.9 (#31029) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/yarn.lock b/yarn.lock index 988c6c79cfb5cb..01645ca93b2eb2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -60,37 +60,37 @@ __metadata: linkType: hard "@babel/core@npm:^7.10.4, @babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.22.1, @babel/core@npm:^7.24.4": - version: 7.24.8 - resolution: "@babel/core@npm:7.24.8" + version: 7.24.9 + resolution: "@babel/core@npm:7.24.9" dependencies: "@ampproject/remapping": "npm:^2.2.0" "@babel/code-frame": "npm:^7.24.7" - "@babel/generator": "npm:^7.24.8" + "@babel/generator": "npm:^7.24.9" "@babel/helper-compilation-targets": "npm:^7.24.8" - "@babel/helper-module-transforms": "npm:^7.24.8" + "@babel/helper-module-transforms": "npm:^7.24.9" "@babel/helpers": "npm:^7.24.8" "@babel/parser": "npm:^7.24.8" "@babel/template": "npm:^7.24.7" "@babel/traverse": "npm:^7.24.8" - "@babel/types": "npm:^7.24.8" + "@babel/types": "npm:^7.24.9" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 10c0/5e21b40cc69746deda3fe3d6540351d9cb0d1ad5aea055b7c319db26071ff5789fd9580d1aa47b114f07631e8e2109f4e71696ca11d7c7e60d157767022c1bd2 + checksum: 10c0/e104ec6efbf099f55184933e9ab078eb5821c792ddfef3e9c6561986ec4ff103f5c11e3d7d6e5e8929e50e2c58db1cc80e5b6f14b530335b6622095ec4b4124c languageName: node linkType: hard -"@babel/generator@npm:^7.24.8, @babel/generator@npm:^7.7.2": - version: 7.24.8 - resolution: "@babel/generator@npm:7.24.8" +"@babel/generator@npm:^7.24.8, @babel/generator@npm:^7.24.9, @babel/generator@npm:^7.7.2": + version: 7.24.9 + resolution: "@babel/generator@npm:7.24.9" dependencies: - "@babel/types": "npm:^7.24.8" + "@babel/types": "npm:^7.24.9" "@jridgewell/gen-mapping": "npm:^0.3.5" "@jridgewell/trace-mapping": "npm:^0.3.25" jsesc: "npm:^2.5.1" - checksum: 10c0/e8a278e75a895f13a7b17dd79abe1e894fe82a5ed3abb127c33c14c66773d69993762521c094c6c364723f8f7375683b0d4a96097781175a29407baedf67b769 + checksum: 10c0/cd1f7edce7717462546c349e15289d1267a3ed627c6f6583fbf51e78eacacc6500ec2f0024f08f1cc7138989e575635b931acf4549f9e728017a22176a9ea6b6 languageName: node linkType: hard @@ -231,9 +231,9 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.24.7, @babel/helper-module-transforms@npm:^7.24.8": - version: 7.24.8 - resolution: "@babel/helper-module-transforms@npm:7.24.8" +"@babel/helper-module-transforms@npm:^7.24.7, @babel/helper-module-transforms@npm:^7.24.8, @babel/helper-module-transforms@npm:^7.24.9": + version: 7.24.9 + resolution: "@babel/helper-module-transforms@npm:7.24.9" dependencies: "@babel/helper-environment-visitor": "npm:^7.24.7" "@babel/helper-module-imports": "npm:^7.24.7" @@ -242,7 +242,7 @@ __metadata: "@babel/helper-validator-identifier": "npm:^7.24.7" peerDependencies: "@babel/core": ^7.0.0 - checksum: 10c0/b76496d5045af55be9de60e59e65e56a43033f01ccc746b26b7af911c358668c206b688ce70a23ab31ec04f9728f3a38e8d01073c85244115ab62f271a7fa3d1 + checksum: 10c0/e27bca43bc113731ee4f2b33a4c5bf9c7eebf4d64487b814c305cbd5feb272c29fcd3d79634ba03131ade171e5972bc7ede8dbc83ba0deb02f1e62d318c87770 languageName: node linkType: hard @@ -1540,14 +1540,14 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.0.0-beta.49, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.6, @babel/types@npm:^7.20.7, @babel/types@npm:^7.24.7, @babel/types@npm:^7.24.8, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": - version: 7.24.8 - resolution: "@babel/types@npm:7.24.8" +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.0.0-beta.49, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.6, @babel/types@npm:^7.20.7, @babel/types@npm:^7.24.7, @babel/types@npm:^7.24.8, @babel/types@npm:^7.24.9, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": + version: 7.24.9 + resolution: "@babel/types@npm:7.24.9" dependencies: "@babel/helper-string-parser": "npm:^7.24.8" "@babel/helper-validator-identifier": "npm:^7.24.7" to-fast-properties: "npm:^2.0.0" - checksum: 10c0/2d7bf561ae993e794cb052c5a81d3a6d1877da13e1e2eb2a59ae75a8fb1c965b618fb3e4abd42548f5f9a4587d3a149185a32d6c4c4ea82195da7dd86f2da0f1 + checksum: 10c0/4970b3481cab39c5c3fdb7c28c834df5c7049f3c7f43baeafe121bb05270ebf0da7c65b097abf314877f213baa591109c82204f30d66cdd46c22ece4a2f32415 languageName: node linkType: hard From 9d0bce40724eb77b7cc3917091b62e4ab159e29e Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Mon, 15 Jul 2024 15:42:47 +0200 Subject: [PATCH 075/101] Move filtered notifications bar in scrollable area (#30996) Co-authored-by: Claire --- .../mastodon/features/notifications/index.jsx | 11 ++++++++--- app/javascript/styles/mastodon/components.scss | 3 +-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/javascript/mastodon/features/notifications/index.jsx b/app/javascript/mastodon/features/notifications/index.jsx index d45f517152ea16..54883096ef50af 100644 --- a/app/javascript/mastodon/features/notifications/index.jsx +++ b/app/javascript/mastodon/features/notifications/index.jsx @@ -223,6 +223,13 @@ class Notifications extends PureComponent { let scrollContainer; + const prepend = ( + <> + {needsNotificationPermission && } + + + ); + if (signedIn) { scrollContainer = ( } + prepend={prepend} alwaysPrepend emptyMessage={emptyMessage} onLoadMore={this.handleLoadOlder} @@ -282,8 +289,6 @@ class Notifications extends PureComponent { {filterBarContainer} - - {scrollContainer} diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index da86a4caa71aac..e94ce2d8f49763 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -10192,8 +10192,7 @@ noscript { .filtered-notifications-banner { display: flex; align-items: center; - border: 1px solid var(--background-border-color); - border-top: 0; + border-bottom: 1px solid var(--background-border-color); padding: 24px 32px; gap: 16px; color: $darker-text-color; From 70969ac64c4cf2811be9aa2f8510e3d831f2533c Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Mon, 15 Jul 2024 15:42:47 +0200 Subject: [PATCH 076/101] [Glitch] Move filtered notifications bar in scrollable area Port 9d0bce40724eb77b7cc3917091b62e4ab159e29e to glitch-soc Co-authored-by: Claire Signed-off-by: Claire --- .../flavours/glitch/features/notifications/index.jsx | 11 ++++++++--- app/javascript/flavours/glitch/styles/components.scss | 3 +-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/javascript/flavours/glitch/features/notifications/index.jsx b/app/javascript/flavours/glitch/features/notifications/index.jsx index b4f1bf5dfef7b5..bc32f9a7d2d12d 100644 --- a/app/javascript/flavours/glitch/features/notifications/index.jsx +++ b/app/javascript/flavours/glitch/features/notifications/index.jsx @@ -258,6 +258,13 @@ class Notifications extends PureComponent { let scrollContainer; + const prepend = ( + <> + {needsNotificationPermission && } + + + ); + if (signedIn) { scrollContainer = ( } + prepend={prepend} alwaysPrepend emptyMessage={emptyMessage} onLoadMore={this.handleLoadOlder} @@ -356,8 +363,6 @@ class Notifications extends PureComponent { {filterBarContainer} - - {scrollContainer} diff --git a/app/javascript/flavours/glitch/styles/components.scss b/app/javascript/flavours/glitch/styles/components.scss index 533a564e99687e..06225b61e6c254 100644 --- a/app/javascript/flavours/glitch/styles/components.scss +++ b/app/javascript/flavours/glitch/styles/components.scss @@ -10746,8 +10746,7 @@ noscript { .filtered-notifications-banner { display: flex; align-items: center; - border: 1px solid var(--background-border-color); - border-top: 0; + border-bottom: 1px solid var(--background-border-color); padding: 24px 32px; gap: 16px; color: $darker-text-color; From 905921b2efaa7bda5acca1c19056a18186ec24ff Mon Sep 17 00:00:00 2001 From: Michael Stanclift Date: Tue, 16 Jul 2024 02:06:19 -0500 Subject: [PATCH 077/101] Add missing "Update Report" string in audit log (#31033) --- config/locales/en.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/locales/en.yml b/config/locales/en.yml index 20df80c272fb12..0e3d209ff33eb6 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -226,6 +226,7 @@ en: update_custom_emoji: Update Custom Emoji update_domain_block: Update Domain Block update_ip_block: Update IP rule + update_report: Update Report update_status: Update Post update_user_role: Update Role actions: From c3a2f9d99309bbe2754be9ea3f5257c434ff34b0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 14:14:25 +0200 Subject: [PATCH 078/101] chore(deps): update dependency json-schema to v4.3.1 (#31012) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 98a3571731fedd..935b28215c6fc1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -367,7 +367,7 @@ GEM json-ld-preloaded (3.3.0) json-ld (~> 3.3) rdf (~> 3.3) - json-schema (4.3.0) + json-schema (4.3.1) addressable (>= 2.8) jsonapi-renderer (0.2.2) jwt (2.7.1) From c27d194eb27aeedb16e17dfb2a8f2be7c0636851 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 15:20:34 +0200 Subject: [PATCH 079/101] fix(deps): update dependency pino to v9.3.1 (#31032) 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 01645ca93b2eb2..c5d04800619ed5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13196,8 +13196,8 @@ __metadata: linkType: hard "pino@npm:^9.0.0": - version: 9.2.0 - resolution: "pino@npm:9.2.0" + version: 9.3.1 + resolution: "pino@npm:9.3.1" dependencies: atomic-sleep: "npm:^1.0.0" fast-redact: "npm:^3.1.1" @@ -13212,7 +13212,7 @@ __metadata: thread-stream: "npm:^3.0.0" bin: pino: bin.js - checksum: 10c0/5fbd226ff7dab0961232b5aa5eca0530cdc5bb29f6bf17d929e42239293b1a587a26cc311db6abc1090c9dd57e8f7b031eae341b41d00d4a642b4f1736474c80 + checksum: 10c0/ab1e81b3e5a91852136d80a592939883eeb81442e5d3a2c070bdbdeb47c5aaa297ead246530b10eb6d5ff59445f4645d1333d342f255d9f002f73aea843e74ee languageName: node linkType: hard From fa54b6121691dfb5d64a42aa6bf07b4d67aa504f Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Tue, 16 Jul 2024 15:21:16 +0200 Subject: [PATCH 080/101] Handle missing links in Webfinger response (#31030) --- app/lib/webfinger.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/webfinger.rb b/app/lib/webfinger.rb index ae8a3b1eae0264..aeafe197024974 100644 --- a/app/lib/webfinger.rb +++ b/app/lib/webfinger.rb @@ -26,7 +26,7 @@ def link(rel, attribute) private def links - @links ||= @json['links'].index_by { |link| link['rel'] } + @links ||= @json.fetch('links', []).index_by { |link| link['rel'] } end def validate_response! From aa88aca0ad89f24fead6a1c6dbfad9def94bd47e Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Tue, 16 Jul 2024 15:23:08 +0200 Subject: [PATCH 081/101] Add optional OAuth application to reports (#30539) --- app/controllers/api/v1/reports_controller.rb | 2 +- app/models/report.rb | 2 ++ app/services/report_service.rb | 4 +++- app/views/admin/reports/_header_details.html.haml | 6 ++++++ config/locales/en.yml | 1 + .../20240713171841_add_application_to_reports.rb | 8 ++++++++ ...13171909_validate_add_application_to_reports.rb | 7 +++++++ db/schema.rb | 4 +++- spec/lib/activitypub/activity/flag_spec.rb | 1 + spec/requests/api/v1/reports_spec.rb | 1 + spec/services/report_service_spec.rb | 14 ++++++++++++++ 11 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20240713171841_add_application_to_reports.rb create mode 100644 db/migrate/20240713171909_validate_add_application_to_reports.rb diff --git a/app/controllers/api/v1/reports_controller.rb b/app/controllers/api/v1/reports_controller.rb index 300c9faa3f0ee9..72f358bb5bcd95 100644 --- a/app/controllers/api/v1/reports_controller.rb +++ b/app/controllers/api/v1/reports_controller.rb @@ -10,7 +10,7 @@ def create @report = ReportService.new.call( current_account, reported_account, - report_params + report_params.merge(application: doorkeeper_token.application) ) render json: @report, serializer: REST::ReportSerializer diff --git a/app/models/report.rb b/app/models/report.rb index 3df5a20e1819c1..17c1503436f0ff 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -18,6 +18,7 @@ # category :integer default("other"), not null # action_taken_at :datetime # rule_ids :bigint(8) is an Array +# application_id :bigint(8) # class Report < ApplicationRecord @@ -31,6 +32,7 @@ class Report < ApplicationRecord rate_limit by: :account, family: :reports belongs_to :account + belongs_to :application, class_name: 'Doorkeeper::Application', optional: true with_options class_name: 'Account' do belongs_to :target_account diff --git a/app/services/report_service.rb b/app/services/report_service.rb index dea6df7b0a1b1b..c95e216c790162 100644 --- a/app/services/report_service.rb +++ b/app/services/report_service.rb @@ -10,6 +10,7 @@ def call(source_account, target_account, options = {}) @comment = options.delete(:comment).presence || '' @category = options[:rule_ids].present? ? 'violation' : (options.delete(:category).presence || 'other') @rule_ids = options.delete(:rule_ids).presence + @application = options.delete(:application).presence @options = options raise ActiveRecord::RecordNotFound if @target_account.unavailable? @@ -35,7 +36,8 @@ def create_report! uri: @options[:uri], forwarded: forward_to_origin?, category: @category, - rule_ids: @rule_ids + rule_ids: @rule_ids, + application: @application ) end diff --git a/app/views/admin/reports/_header_details.html.haml b/app/views/admin/reports/_header_details.html.haml index 45790b9cd50d75..434231f7333626 100644 --- a/app/views/admin/reports/_header_details.html.haml +++ b/app/views/admin/reports/_header_details.html.haml @@ -14,6 +14,12 @@ = admin_account_link_to report.account - else = report.account.domain + - if report.application.present? + .report-header__details__item + .report-header__details__item__header + %strong= t('admin.reports.reported_with_application') + .report-header__details__item__content + = report.application.name .report-header__details__item .report-header__details__item__header %strong= t('admin.reports.status') diff --git a/config/locales/en.yml b/config/locales/en.yml index 0e3d209ff33eb6..cccb1721634353 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -639,6 +639,7 @@ en: report: 'Report #%{id}' reported_account: Reported account reported_by: Reported by + reported_with_application: Reported with application resolved: Resolved resolved_msg: Report successfully resolved! skip_to_actions: Skip to actions diff --git a/db/migrate/20240713171841_add_application_to_reports.rb b/db/migrate/20240713171841_add_application_to_reports.rb new file mode 100644 index 00000000000000..42ba14e9b260a0 --- /dev/null +++ b/db/migrate/20240713171841_add_application_to_reports.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class AddApplicationToReports < ActiveRecord::Migration[7.1] + def change + add_column :reports, :application_id, :bigint, null: true + add_foreign_key :reports, :oauth_applications, column: :application_id, on_delete: :nullify, validate: false + end +end diff --git a/db/migrate/20240713171909_validate_add_application_to_reports.rb b/db/migrate/20240713171909_validate_add_application_to_reports.rb new file mode 100644 index 00000000000000..34ae690a3908e3 --- /dev/null +++ b/db/migrate/20240713171909_validate_add_application_to_reports.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class ValidateAddApplicationToReports < ActiveRecord::Migration[7.1] + def change + validate_foreign_key :reports, :oauth_applications + end +end diff --git a/db/schema.rb b/db/schema.rb index 87c49d74152358..66da12f71394b2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_07_12_064044) do +ActiveRecord::Schema[7.1].define(version: 2024_07_13_171909) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -929,6 +929,7 @@ t.integer "category", default: 0, null: false t.datetime "action_taken_at", precision: nil t.bigint "rule_ids", array: true + t.bigint "application_id" t.index ["account_id"], name: "index_reports_on_account_id" t.index ["action_taken_by_account_id"], name: "index_reports_on_action_taken_by_account_id", where: "(action_taken_by_account_id IS NOT NULL)" t.index ["assigned_account_id"], name: "index_reports_on_assigned_account_id", where: "(assigned_account_id IS NOT NULL)" @@ -1361,6 +1362,7 @@ add_foreign_key "reports", "accounts", column: "assigned_account_id", on_delete: :nullify add_foreign_key "reports", "accounts", column: "target_account_id", name: "fk_eb37af34f0", on_delete: :cascade add_foreign_key "reports", "accounts", name: "fk_4b81f7522c", on_delete: :cascade + add_foreign_key "reports", "oauth_applications", column: "application_id", on_delete: :nullify add_foreign_key "scheduled_statuses", "accounts", on_delete: :cascade add_foreign_key "session_activations", "oauth_access_tokens", column: "access_token_id", name: "fk_957e5bda89", on_delete: :cascade add_foreign_key "session_activations", "users", name: "fk_e5fda67334", on_delete: :cascade diff --git a/spec/lib/activitypub/activity/flag_spec.rb b/spec/lib/activitypub/activity/flag_spec.rb index 426cd97df9acf3..be1bd42a14a810 100644 --- a/spec/lib/activitypub/activity/flag_spec.rb +++ b/spec/lib/activitypub/activity/flag_spec.rb @@ -36,6 +36,7 @@ expect(report).to_not be_nil expect(report.comment).to eq 'Boo!!' expect(report.status_ids).to eq [status.id] + expect(report.application).to be_nil end end diff --git a/spec/requests/api/v1/reports_spec.rb b/spec/requests/api/v1/reports_spec.rb index 491c6263d3397e..a72d9bbcd85bbb 100644 --- a/spec/requests/api/v1/reports_spec.rb +++ b/spec/requests/api/v1/reports_spec.rb @@ -47,6 +47,7 @@ expect(target_account.targeted_reports).to_not be_empty expect(target_account.targeted_reports.first.comment).to eq 'reasons' + expect(target_account.targeted_reports.first.application).to eq token.application expect(emails.size) .to eq(1) diff --git a/spec/services/report_service_spec.rb b/spec/services/report_service_spec.rb index bf1f937191b74c..6518c5c27ad3a0 100644 --- a/spec/services/report_service_spec.rb +++ b/spec/services/report_service_spec.rb @@ -23,6 +23,11 @@ stub_request(:post, 'http://example.com/inbox').to_return(status: 200) end + it 'does not have an application' do + report = subject.call(source_account, remote_account) + expect(report.application).to be_nil + end + context 'when forward is true', :inline_jobs do let(:forward) { true } @@ -96,6 +101,15 @@ end end + context 'when passed an application' do + let(:application) { Fabricate(:application) } + + it 'has an application' do + report = subject.call(source_account, target_account, application: application) + expect(report.application).to eq application + end + end + context 'when the reported status is a DM' do subject do -> { described_class.new.call(source_account, target_account, status_ids: [status.id]) } From a609940ec9a370a18789ad87532a098fe6e564c9 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Tue, 16 Jul 2024 15:23:26 +0200 Subject: [PATCH 082/101] Fix test description in hashtag_bar (#30827) --- app/javascript/mastodon/components/__tests__/hashtag_bar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx b/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx index b7225fc92e01e4..f86c1a2a6bc5b5 100644 --- a/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx +++ b/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx @@ -165,7 +165,7 @@ describe('computeHashtagBarForStatus', () => { ); }); - it('puts the hashtags in the bar if a status content has hashtags in the only line and has a media', () => { + it('does not put the hashtags in the bar if a status content has hashtags in the only line and has a media', () => { const status = createStatus( '

This is my content! #hashtag

', ['hashtag'], From 66430cb25c824cfad0394216d8e7a523533bcf4b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 17:24:56 +0200 Subject: [PATCH 083/101] New Crowdin Translations (automated) (#31038) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/cy.json | 29 ++++++++-------- app/javascript/mastodon/locales/gl.json | 4 +-- app/javascript/mastodon/locales/kab.json | 2 +- app/javascript/mastodon/locales/zh-TW.json | 10 +++--- config/locales/cy.yml | 9 ++--- config/locales/devise.cy.yml | 2 +- config/locales/devise.zh-TW.yml | 4 +-- config/locales/doorkeeper.cy.yml | 8 +++-- config/locales/simple_form.ca.yml | 2 ++ config/locales/simple_form.cy.yml | 2 ++ config/locales/simple_form.da.yml | 2 ++ config/locales/simple_form.de.yml | 2 ++ config/locales/simple_form.eo.yml | 1 + config/locales/simple_form.es-AR.yml | 2 ++ config/locales/simple_form.es-MX.yml | 2 ++ config/locales/simple_form.es.yml | 2 ++ config/locales/simple_form.fi.yml | 2 ++ config/locales/simple_form.fo.yml | 2 ++ config/locales/simple_form.gl.yml | 2 ++ config/locales/simple_form.he.yml | 2 ++ config/locales/simple_form.hu.yml | 3 ++ config/locales/simple_form.is.yml | 2 ++ config/locales/simple_form.it.yml | 2 ++ config/locales/simple_form.ko.yml | 2 ++ config/locales/simple_form.lt.yml | 3 ++ config/locales/simple_form.nl.yml | 2 ++ config/locales/simple_form.pl.yml | 2 ++ config/locales/simple_form.pt-PT.yml | 2 ++ config/locales/simple_form.sl.yml | 2 ++ config/locales/simple_form.sq.yml | 2 ++ config/locales/simple_form.sr-Latn.yml | 2 ++ config/locales/simple_form.sr.yml | 2 ++ config/locales/simple_form.sv.yml | 2 ++ config/locales/simple_form.tr.yml | 2 ++ config/locales/simple_form.uk.yml | 2 ++ config/locales/simple_form.zh-CN.yml | 2 ++ config/locales/simple_form.zh-TW.yml | 18 +++++----- config/locales/zh-TW.yml | 40 +++++++++++----------- 38 files changed, 123 insertions(+), 60 deletions(-) diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json index 1c7e61832c1f29..9a3fab6155a399 100644 --- a/app/javascript/mastodon/locales/cy.json +++ b/app/javascript/mastodon/locales/cy.json @@ -48,8 +48,8 @@ "account.mention": "Crybwyll @{name}", "account.moved_to": "Mae {name} wedi nodi fod eu cyfrif newydd yn:", "account.mute": "Tewi @{name}", - "account.mute_notifications_short": "Distewi hysbysiadau", - "account.mute_short": "Tewi", + "account.mute_notifications_short": "Diffodd hysbysiadau", + "account.mute_short": "Anwybyddu", "account.muted": "Wedi anwybyddu", "account.mutual": "Cydgydnabod", "account.no_bio": "Dim disgrifiad wedi'i gynnig.", @@ -92,7 +92,7 @@ "block_modal.they_cant_mention": "Nid ydynt yn gallu eich crybwyll na'ch dilyn.", "block_modal.they_cant_see_posts": "Nid ydynt yn gallu gweld eich postiadau ac ni fyddwch yn gweld eu rhai hwy.", "block_modal.they_will_know": "Gallant weld eu bod wedi'u rhwystro.", - "block_modal.title": "Rhwystro defnyddiwr?", + "block_modal.title": "Blocio defnyddiwr?", "block_modal.you_wont_see_mentions": "Ni welwch bostiadau sy'n sôn amdanynt.", "boost_modal.combo": "Mae modd pwyso {combo} er mwyn hepgor hyn tro nesa", "bundle_column_error.copy_stacktrace": "Copïo'r adroddiad gwall", @@ -164,7 +164,7 @@ "compose_form.spoiler.marked": "Dileu rhybudd cynnwys", "compose_form.spoiler.unmarked": "Ychwanegu rhybudd cynnwys", "compose_form.spoiler_placeholder": "Rhybudd cynnwys (dewisol)", - "confirmation_modal.cancel": "Diddymu", + "confirmation_modal.cancel": "Canslo", "confirmations.block.confirm": "Blocio", "confirmations.cancel_follow_request.confirm": "Tynnu'r cais yn ôl", "confirmations.cancel_follow_request.message": "Ydych chi'n siŵr eich bod am dynnu'ch cais i ddilyn {name} yn ôl?", @@ -174,7 +174,7 @@ "confirmations.delete_list.message": "Ydych chi'n siŵr eich bod eisiau dileu'r rhestr hwn am byth?", "confirmations.discard_edit_media.confirm": "Dileu", "confirmations.discard_edit_media.message": "Mae gennych newidiadau heb eu cadw i'r disgrifiad cyfryngau neu'r rhagolwg - eu dileu beth bynnag?", - "confirmations.domain_block.confirm": "Rhwystro gweinydd", + "confirmations.domain_block.confirm": "Blocio gweinydd", "confirmations.domain_block.message": "Ydych chi wir, wir eisiau blocio'r holl {domain}? Fel arfer, mae blocio neu dewi pobl penodol yn broses mwy effeithiol. Fyddwch chi ddim yn gweld cynnwys o'r parth hwnnw mewn ffrydiau cyhoeddus neu yn eich hysbysiadau. Bydd eich dilynwyr o'r parth hwnnw yn cael eu ddileu.", "confirmations.edit.confirm": "Golygu", "confirmations.edit.message": "Bydd golygu nawr yn trosysgrifennu'r neges rydych yn ei ysgrifennu ar hyn o bryd. Ydych chi'n siŵr eich bod eisiau gwneud hyn?", @@ -201,17 +201,17 @@ "disabled_account_banner.account_settings": "Gosodiadau'r cyfrif", "disabled_account_banner.text": "Mae eich cyfrif {disabledAccount} wedi ei analluogi ar hyn o bryd.", "dismissable_banner.community_timeline": "Dyma'r postiadau cyhoeddus diweddaraf gan bobl sydd â chyfrifon ar {domain}.", - "dismissable_banner.dismiss": "Diddymu", + "dismissable_banner.dismiss": "Cau", "dismissable_banner.explore_links": "Dyma straeon newyddion sy’n cael eu rhannu fwyaf ar y we gymdeithasol heddiw. Mae'r straeon newyddion diweddaraf sy'n cael eu postio gan fwy o unigolion gwahanol yn cael eu graddio'n uwch.", "dismissable_banner.explore_statuses": "Mae'r rhain yn bostiadau o bob rhan o'r we gymdeithasol sydd ar gynnydd heddiw. Mae postiadau mwy diweddar sydd â mwy o hybiau a ffefrynu'n cael eu graddio'n uwch.", "dismissable_banner.explore_tags": "Mae'r rhain yn hashnodau sydd ar gynnydd ar y we gymdeithasol heddiw. Mae hashnodau sy'n cael eu defnyddio gan fwy o unigolion gwahanol yn cael eu graddio'n uwch.", "dismissable_banner.public_timeline": "Dyma'r postiadau cyhoeddus diweddaraf gan bobl ar y we gymdeithasol y mae pobl ar {domain} yn eu dilyn.", - "domain_block_modal.block": "Rhwystro gweinydd", - "domain_block_modal.block_account_instead": "Rhwystro @{name} yn lle hynny", + "domain_block_modal.block": "Blocio gweinydd", + "domain_block_modal.block_account_instead": "Blocio @{name} yn ei le", "domain_block_modal.they_can_interact_with_old_posts": "Gall pobl o'r gweinydd hwn ryngweithio â'ch hen bostiadau.", "domain_block_modal.they_cant_follow": "Ni all neb o'r gweinydd hwn eich dilyn.", - "domain_block_modal.they_wont_know": "Fyddan nhw ddim yn gwybod eu bod wedi cael eu rhwystro.", - "domain_block_modal.title": "Rhwystro parth?", + "domain_block_modal.they_wont_know": "Fyddan nhw ddim yn gwybod eu bod wedi cael eu blocio.", + "domain_block_modal.title": "Blocio parth?", "domain_block_modal.you_will_lose_followers": "Bydd eich holl ddilynwyr o'r gweinydd hwn yn cael eu tynnu.", "domain_block_modal.you_wont_see_posts": "Fyddwch chi ddim yn gweld postiadau na hysbysiadau gan ddefnyddwyr ar y gweinydd hwn.", "domain_pill.activitypub_lets_connect": "Mae'n caniatáu ichi gysylltu a rhyngweithio â phobl nid yn unig ar Mastodon, ond ar draws gwahanol apiau cymdeithasol hefyd.", @@ -252,7 +252,7 @@ "empty_column.bookmarked_statuses": "Nid oes gennych unrhyw bostiad wedi'u cadw fel llyfrnodau eto. Pan fyddwch yn gosod nod tudalen i un, mi fydd yn ymddangos yma.", "empty_column.community": "Mae'r ffrwd lleol yn wag. Beth am ysgrifennu rhywbeth cyhoeddus!", "empty_column.direct": "Nid oes gennych unrhyw grybwylliadau preifat eto. Pan fyddwch chi'n anfon neu'n derbyn un, bydd yn ymddangos yma.", - "empty_column.domain_blocks": "Nid oes yna unrhyw barthau cuddiedig eto.", + "empty_column.domain_blocks": "Nid oes unrhyw barthau wedi'u blocio eto.", "empty_column.explore_statuses": "Does dim yn trendio ar hyn o bryd. Dewch nôl nes ymlaen!", "empty_column.favourited_statuses": "Nid oes gennych unrhyw hoff bostiadau eto. Pan byddwch yn hoffi un, bydd yn ymddangos yma.", "empty_column.favourites": "Nid oes unrhyw un wedi hoffi'r postiad hwn eto. Pan fydd rhywun yn gwneud hynny, byddan nhw'n ymddangos yma.", @@ -411,6 +411,7 @@ "limited_account_hint.action": "Dangos y proffil beth bynnag", "limited_account_hint.title": "Mae'r proffil hwn wedi cael ei guddio gan gymedrolwyr {domain}.", "link_preview.author": "Gan {name}", + "link_preview.more_from_author": "Mwy gan {name}", "lists.account.add": "Ychwanegu at restr", "lists.account.remove": "Tynnu o'r rhestr", "lists.delete": "Dileu rhestr", @@ -484,9 +485,9 @@ "notification.reblog": "Hybodd {name} eich post", "notification.relationships_severance_event": "Wedi colli cysylltiad â {name}", "notification.relationships_severance_event.account_suspension": "Mae gweinyddwr o {from} wedi atal {target}, sy'n golygu na allwch dderbyn diweddariadau ganddynt mwyach na rhyngweithio â nhw.", - "notification.relationships_severance_event.domain_block": "Mae gweinyddwr o {from} wedi rhwystro {target}, gan gynnwys {followersCount} o'ch dilynwyr a {followingCount, plural, one {# cyfrif} other {# cyfrif}} arall rydych chi'n ei ddilyn.", + "notification.relationships_severance_event.domain_block": "Mae gweinyddwr o {from} wedi blocio {target}, gan gynnwys {followersCount} o'ch dilynwyr a {followingCount, plural, one {# cyfrif} other {# cyfrif}} arall rydych chi'n ei ddilyn.", "notification.relationships_severance_event.learn_more": "Dysgu mwy", - "notification.relationships_severance_event.user_domain_block": "Rydych wedi rhwystro {target}, gan ddileu {followersCount} o'ch dilynwyr a {followingCount, plural, one {# cyfrif} other {#cyfrifon}} arall rydych yn ei ddilyn.", + "notification.relationships_severance_event.user_domain_block": "Rydych wedi blocio {target}, gan ddileu {followersCount} o'ch dilynwyr a {followingCount, plural, one {# cyfrif} other {#cyfrifon}} arall rydych yn ei ddilyn.", "notification.status": "{name} newydd ei bostio", "notification.update": "Golygodd {name} bostiad", "notification_requests.accept": "Derbyn", @@ -803,7 +804,7 @@ "video.expand": "Ymestyn fideo", "video.fullscreen": "Sgrin llawn", "video.hide": "Cuddio fideo", - "video.mute": "Tewi sain", + "video.mute": "Diffodd sain", "video.pause": "Oedi", "video.play": "Chwarae", "video.unmute": "Dad-dewi sain" diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 03287c7e5224bc..c1e0f97f804764 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -92,7 +92,7 @@ "block_modal.remote_users_caveat": "Ímoslle pedir ao servidor {domain} que respecte a túa decisión. Emporiso, non hai garantía de que atenda a petición xa que os servidores xestionan os bloqueos de formas diferentes. As publicacións públicas poderían aínda ser visibles para usuarias que non iniciaron sesión.", "block_modal.show_less": "Mostrar menos", "block_modal.show_more": "Mostrar máis", - "block_modal.they_cant_mention": "Non te poden seguir nin mencionar.", + "block_modal.they_cant_mention": "Non te pode seguir nin mencionar.", "block_modal.they_cant_see_posts": "Non pode ver as túas publicacións nin ti as de ela.", "block_modal.they_will_know": "Pode ver que a bloqueaches.", "block_modal.title": "Bloquear usuaria?", @@ -216,7 +216,7 @@ "domain_block_modal.they_wont_know": "Non saberá que a bloqueaches.", "domain_block_modal.title": "Bloquear dominio?", "domain_block_modal.you_will_lose_followers": "Vanse eliminar todas as túas seguidoras deste servidor.", - "domain_block_modal.you_wont_see_posts": "Non verás publicacións ou notificación das usuarias neste servidor.", + "domain_block_modal.you_wont_see_posts": "Non verás publicacións ou notificacións das usuarias deste servidor.", "domain_pill.activitypub_lets_connect": "Permíteche conectar e interactuar con persoas non só de Mastodon, se non tamén con outras apps sociais.", "domain_pill.activitypub_like_language": "ActivityPub é algo así como o idioma que Mastodon fala con outras redes sociais.", "domain_pill.server": "Servidor", diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json index 868edbc8c0fe4c..a5c3280712a97f 100644 --- a/app/javascript/mastodon/locales/kab.json +++ b/app/javascript/mastodon/locales/kab.json @@ -580,7 +580,7 @@ "status.mute_conversation": "Sgugem adiwenni", "status.open": "Semɣeṛ tasuffeɣt-ayi", "status.pin": "Senteḍ-itt deg umaɣnu", - "status.pinned": "Tijewwiqin yettwasentḍen", + "status.pinned": "Tisuffaɣ yettwasentḍen", "status.read_more": "Issin ugar", "status.reblog": "Bḍu", "status.reblogged_by": "Yebḍa-tt {name}", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 04469a971d5cbf..b9a5fc071468ca 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -113,7 +113,7 @@ "closed_registrations.other_server_instructions": "因為 Mastodon 是去中心化的,所以您也能於其他伺服器上建立帳號,並仍然與這個伺服器互動。", "closed_registrations_modal.description": "目前無法於 {domain} 建立新帳號,但也請別忘了,您並不一定需要有 {domain} 伺服器的帳號,也能使用 Mastodon。", "closed_registrations_modal.find_another_server": "尋找另一個伺服器", - "closed_registrations_modal.preamble": "Mastodon 是去中心化的,所以無論您於哪個伺服器新增帳號,都可以與此伺服器上的任何人跟隨及互動。您甚至能自行架設一個自己的伺服器!", + "closed_registrations_modal.preamble": "Mastodon 是去中心化的,所以無論您於哪個伺服器新增帳號,都可以與此伺服器上的任何人跟隨及互動。您甚至能自行架設自己的伺服器!", "closed_registrations_modal.title": "註冊 Mastodon", "column.about": "關於", "column.blocks": "已封鎖的使用者", @@ -271,7 +271,7 @@ "empty_column.public": "這裡什麼都沒有!嘗試寫些公開的嘟文,或者跟隨其他伺服器的使用者後,就會有嘟文出現了", "error.unexpected_crash.explanation": "由於發生系統故障或瀏覽器相容性問題,無法正常顯示此頁面。", "error.unexpected_crash.explanation_addons": "此頁面無法被正常顯示,這可能是由瀏覽器附加元件或網頁自動翻譯工具造成的。", - "error.unexpected_crash.next_steps": "請嘗試重新整理頁面。如果狀況沒有改善,您可以使用不同的瀏覽器或應用程式來檢視來使用 Mastodon。", + "error.unexpected_crash.next_steps": "請嘗試重新整理頁面。如果狀況沒有改善,您可以使用不同的瀏覽器或應用程式以檢視來使用 Mastodon。", "error.unexpected_crash.next_steps_addons": "請嘗試關閉它們然後重新整理頁面。如果狀況沒有改善,您可以使用不同的瀏覽器或應用程式來檢視來使用 Mastodon。", "errors.unexpected_crash.copy_stacktrace": "複製 stacktrace 到剪貼簿", "errors.unexpected_crash.report_issue": "回報問題", @@ -356,7 +356,7 @@ "home.show_announcements": "顯示公告", "interaction_modal.description.favourite": "若於 Mastodon 上有個帳號,您可以將此嘟文加入最愛使作者知道您欣賞它且將它儲存下來。", "interaction_modal.description.follow": "若於 Mastodon 上有個帳號,您可以跟隨 {name} 以於首頁時間軸接收他們的嘟文。", - "interaction_modal.description.reblog": "若於 Mastodon 上有個帳號,您可以轉嘟此嘟文以分享給您的跟隨者們。", + "interaction_modal.description.reblog": "若於 Mastodon 上有個帳號,您可以轉嘟此嘟文以向您的跟隨者們分享。", "interaction_modal.description.reply": "若於 Mastodon 上有個帳號,您可以回覆此嘟文。", "interaction_modal.login.action": "返回首頁", "interaction_modal.login.prompt": "您帳號所屬伺服器之網域,例如:mastodon.social", @@ -552,7 +552,7 @@ "onboarding.follows.lead": "您的首頁時間軸是 Mastodon 的核心體驗。若您跟隨更多人,它將會變得更活躍有趣。這些個人檔案也許是個好起點,您可以隨時取消跟隨他們!", "onboarding.follows.title": "客製化您的首頁時間軸", "onboarding.profile.discoverable": "使我的個人檔案可以被找到", - "onboarding.profile.discoverable_hint": "當您於 Mastodon 上選擇加入可發現性時,您的嘟文可能會顯示於搜尋結果與趨勢中。您的個人檔案可能會被推薦給與您志趣相投的人。", + "onboarding.profile.discoverable_hint": "當您於 Mastodon 上選擇加入可發現性時,您的嘟文可能會顯示於搜尋結果與趨勢中。您的個人檔案可能會被推薦至與您志趣相投的人。", "onboarding.profile.display_name": "顯示名稱", "onboarding.profile.display_name_hint": "完整名稱或暱稱...", "onboarding.profile.lead": "您隨時可以稍候於設定中完成此操作,將有更多自訂選項可使用。", @@ -797,7 +797,7 @@ "upload_modal.applying": "正在套用...", "upload_modal.choose_image": "選擇圖片", "upload_modal.description_placeholder": "我能吞下玻璃而不傷身體", - "upload_modal.detect_text": "從圖片中偵測文字", + "upload_modal.detect_text": "自圖片中偵測文字", "upload_modal.edit_media": "編輯媒體", "upload_modal.hint": "於預覽中點擊或拖曳圓圈以選擇將於所有縮圖中顯示的焦點。", "upload_modal.preparing_ocr": "準備 OCR 中……", diff --git a/config/locales/cy.yml b/config/locales/cy.yml index 35ed5ade8aa913..cd48b3e3b0ec2f 100644 --- a/config/locales/cy.yml +++ b/config/locales/cy.yml @@ -305,6 +305,7 @@ cy: filter_by_action: Hidlo yn ôl gweithred filter_by_user: Hidlo yn ôl defnyddiwr title: Cofnod archwilio + unavailable_instance: "(nid yw enw'r parth ar gael)" announcements: destroyed_msg: Cyhoeddiad wedi'i ddileu'n llwyddiannus! edit: @@ -1436,7 +1437,7 @@ cy: domain_blocking_html: Rydych ar fin rhwystro hyd at %{total_items} parth o %{filename} . following_html: Rydych ar fin dilyn hyd at %{total_items} cyfrif gan %{filename} . lists_html: Rydych ar fin ychwanegu hyd at %{total_items} o gyfrifon o %{filename} at eich rhestrau . Bydd rhestrau newydd yn cael eu creu os nad oes rhestr i ychwanegu ati. - muting_html: Rydych ar fin tewi hyd at %{total_items} cyfrif o %{filename} . + muting_html: Rydych ar fin anwybyddu hyd at %{total_items} cyfrif o %{filename}. preface: Gallwch fewnforio data rydych chi wedi'i allforio o weinydd arall, fel rhestr o'r bobl rydych chi'n eu dilyn neu'n eu blocio. recent_imports: Mewnforion diweddar states: @@ -1529,9 +1530,9 @@ cy: too_many: Methu atodi mwy na 4 ffeil migrations: acct: Symudwyd i - cancel: Diddymu ailgyfeirio - cancel_explanation: Bydd diddymu'r ailgyfeiriad yn ail agor eich cyfrif cyfredol, ond ni fydd yn dod â dilynwyr sydd wedi'u symud i'r cyfrif hwnnw yn ôl. - cancelled_msg: Wedi diddymu'r ailgyfeiriad yn llwyddiannus. + cancel: Canslo ailgyfeirio + cancel_explanation: Bydd canslo'r ailgyfeiriad yn ail agor eich cyfrif cyfredol, ond ni fydd yn dod â dilynwyr sydd wedi'u symud i'r cyfrif hwnnw yn ôl. + cancelled_msg: Wedi canslo'r ailgyfeiriad yn llwyddiannus. errors: already_moved: yw'r un cyfrif rydych chi wedi symud iddo eisoes missing_also_known_as: nid yw'n arallenw o'r cyfrif hwn diff --git a/config/locales/devise.cy.yml b/config/locales/devise.cy.yml index 523ae70b049c4a..b41e12f85d69fd 100644 --- a/config/locales/devise.cy.yml +++ b/config/locales/devise.cy.yml @@ -93,7 +93,7 @@ cy: updated: Mae eich cyfrinair wedi'i newid yn llwyddiannus. Rydych chi bellach wedi mewngofnodi. updated_not_active: Mae eich cyfrinair wedi ei newid yn llwyddiannus. registrations: - destroyed: Hwyl fawr! Mae eich cyfrif wedi ei ddiddymu'n llwyddiannus. Gobeithiwn eich gweld chi eto'n fuan. + destroyed: Hwyl fawr! Mae eich cyfrif wedi ei chanslo'n llwyddiannus. Gobeithiwn eich gweld chi eto'n fuan. signed_up: Croeso! Rydych wedi cofrestru'n llwyddiannus. signed_up_but_inactive: Yr ydych wedi cofrestru'n llwyddiannus. Fodd bynnag, ni allwn eich mewngofnodi achos nid yw eich cyfrif wedi ei hagor eto. signed_up_but_locked: Rydych chi wedi cofrestru'n llwyddiannus. Fodd bynnag, ni allem eich mewngofnodi oherwydd bod eich cyfrif wedi'i gloi. diff --git a/config/locales/devise.zh-TW.yml b/config/locales/devise.zh-TW.yml index 7ead831e4f75aa..3e3af9f323997c 100644 --- a/config/locales/devise.zh-TW.yml +++ b/config/locales/devise.zh-TW.yml @@ -75,7 +75,7 @@ zh-TW: title: 您的一支安全密鑰已經被移除 webauthn_disabled: explanation: 您的帳號已停用安全金鑰身份驗證。 - extra: 現在僅可使用配對的 TOTP 應用程式產生之 token 登入。 + extra: 目前僅可使用配對的 TOTP 應用程式產生之 token 登入。 subject: Mastodon:安全密鑰認證方式已停用 title: 已停用安全密鑰 webauthn_enabled: @@ -90,7 +90,7 @@ zh-TW: no_token: 您必須透過密碼重設信件才能存取此頁面。若確實如此,請確定輸入的網址是完整的。 send_instructions: 若電子郵件地址存在於我們的資料庫,幾分鐘後您將於信箱中收到密碼復原連結。若未收到請檢查垃圾郵件資料夾。 send_paranoid_instructions: 若電子郵件地址存在於我們的資料庫,幾分鐘後您將於信箱中收到密碼復原連結。若未收到請檢查垃圾郵件資料夾。 - updated: 您的密碼已成功變更,現在已經登入。 + updated: 您的密碼已成功變更,您現在已經成功登入。 updated_not_active: 您的密碼已成功變更。 registrations: destroyed: 再見!您的帳號已成功取消,期待再相逢。 diff --git a/config/locales/doorkeeper.cy.yml b/config/locales/doorkeeper.cy.yml index e79aa0359f67b8..e023bc060312c8 100644 --- a/config/locales/doorkeeper.cy.yml +++ b/config/locales/doorkeeper.cy.yml @@ -20,7 +20,7 @@ cy: applications: buttons: authorize: Awdurdodi - cancel: Diddymu + cancel: Canslo destroy: Dinistrio edit: Golygu submit: Cyflwyno @@ -72,7 +72,7 @@ cy: revoke: Ydych chi'n siŵr? index: authorized_at: Wedi'i awdurdodi ar %{date} - description_html: Mae'r rhain yn raglenni sy'n gallu cael mynediad i'ch cyfrif gan ddefnyddio'r API. Os oes yna rhaglenni nad ydych chi'n eu hadnabod yma, neu os yw rhaglen yn camymddwyn, gallwch chi ddiddymu ei fynediad. + description_html: Mae'r rhain yn raglenni sy'n gallu cael mynediad i'ch cyfrif gan ddefnyddio'r API. Os oes yna rhaglenni nad ydych chi'n eu hadnabod yma, neu os yw rhaglen yn camymddwyn, gallwch chi ddirymu ei fynediad. last_used_at: Defnyddiwyd ddiwethaf ar %{date} never_used: Erioed wedi'i ddefnyddio scopes: Caniatâd @@ -83,7 +83,7 @@ cy: access_denied: Mae perchennog yr adnodd neu'r gweinydd awdurdodi wedi atal y cais. credential_flow_not_configured: Llif meini prawf cyfrinair perchennog yr adnodd wedi methu achos fod Doorkeeper.configure.resource_owner_from_credentials heb ei ffurfweddu. invalid_client: Methodd dilysu cleient oherwydd cleient anhysbys, dim dilysiad cleient wedi'i gynnwys, neu ddull dilysu heb ei gefnogi. - invalid_grant: Mae'r grant awdurdodi ar yr amod yn annilys, wedi dod i ben, wedi'i ddiddymu, nid yw'n cyfateb i'r URI ailgyfeirio a ddefnyddiwyd yn y cais am awdurdodiad, neu wedi'i roi i gleient arall. + invalid_grant: Mae'r grant awdurdodi ar yr amod yn annilys, wedi dod i ben, wedi'i ddirymu, nid yw'n cyfateb i'r URI ailgyfeirio a ddefnyddiwyd yn y cais am awdurdodiad, neu wedi'i roi i gleient arall. invalid_redirect_uri: Nid yw'r uri ailgyfeirio a gynhwysir yn ddilys. invalid_request: missing_param: 'Paramedr gofynnol ar goll: %{value}.' @@ -135,6 +135,7 @@ cy: media: Atodiadau cyfryngau mutes: Anwybyddiadau notifications: Hysbysiadau + profile: Eich proffil Mastodon push: Hysbysiadau gwthiadwy reports: Adroddiadau search: Chwilio @@ -165,6 +166,7 @@ cy: admin:write:reports: cyflawni camau cymedroli ar adroddiadau crypto: defnyddio amgryptio ben i ben follow: addasu perthnasau cyfrif + profile: darllen dim ond gwybodaeth proffil eich cyfrif push: derbyn eich hysbysiadau gwthiadwy read: darllen holl ddata eich cyfrif read:accounts: gweld gwybodaeth y cyfrif diff --git a/config/locales/simple_form.ca.yml b/config/locales/simple_form.ca.yml index 4bb906abdbd897..2e0199d7fd5b06 100644 --- a/config/locales/simple_form.ca.yml +++ b/config/locales/simple_form.ca.yml @@ -243,11 +243,13 @@ ca: warn: Oculta amb un avís form_admin_settings: activity_api_enabled: Publica a l'API estadístiques agregades sobre l'activitat dels usuaris + app_icon: Icona de l'aplicació backups_retention_period: Període de retenció del arxiu d'usuari bootstrap_timeline_accounts: Recomana sempre aquests comptes als nous usuaris closed_registrations_message: Missatge personalitzat quan el registre no és accessible content_cache_retention_period: Període de retenció del contingut remot custom_css: CSS personalitzat + favicon: Icona de preferits mascot: Mascota personalitzada (llegat) media_cache_retention_period: Període de retenció del cau multimèdia peers_api_enabled: Publica a l'API la llista de servidors descoberts diff --git a/config/locales/simple_form.cy.yml b/config/locales/simple_form.cy.yml index 5e8fd85293f75a..cd5bc1add788b8 100644 --- a/config/locales/simple_form.cy.yml +++ b/config/locales/simple_form.cy.yml @@ -242,11 +242,13 @@ cy: warn: Cuddio â rhybudd form_admin_settings: activity_api_enabled: Cyhoeddi ystadegau cyfanredol am weithgarwch defnyddwyr yn yr API + app_icon: Eicon ap backups_retention_period: Cyfnod cadw archif defnyddwyr bootstrap_timeline_accounts: Argymhellwch y cyfrifon hyn i ddefnyddwyr newydd bob amser closed_registrations_message: Neges bersonol pan nad yw cofrestriadau ar gael content_cache_retention_period: Cyfnod cadw cynnwys o bell custom_css: CSS cyfaddas + favicon: Favicon mascot: Mascot cyfaddas (hen) media_cache_retention_period: Cyfnod cadw storfa cyfryngau peers_api_enabled: Cyhoeddi rhestr o weinyddion a ddarganfuwyd yn yr API diff --git a/config/locales/simple_form.da.yml b/config/locales/simple_form.da.yml index 73a32cf2fdee2d..9e949d16839f7d 100644 --- a/config/locales/simple_form.da.yml +++ b/config/locales/simple_form.da.yml @@ -243,11 +243,13 @@ da: warn: Skjul bag en advarsel form_admin_settings: activity_api_enabled: Offentliggør samlede statistikker vedr. brugeraktivitet i API'en + app_icon: App-ikon backups_retention_period: Brugerarkivs opbevaringsperiode bootstrap_timeline_accounts: Anbefal altid disse konti til nye brugere closed_registrations_message: Tilpasset besked, når tilmelding er utilgængelig content_cache_retention_period: Opbevaringsperiode for eksternt indhold custom_css: Tilpasset CSS + favicon: Favikon mascot: Tilpasset maskot (ældre funktion) media_cache_retention_period: Media-cache opbevaringsperiode peers_api_enabled: Udgiv liste over fundne server i API'en diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml index 5711d2a12a4993..332d9513952126 100644 --- a/config/locales/simple_form.de.yml +++ b/config/locales/simple_form.de.yml @@ -243,11 +243,13 @@ de: warn: Mit einer Inhaltswarnung ausblenden form_admin_settings: activity_api_enabled: Aggregierte Nutzungsdaten über die API veröffentlichen + app_icon: App-Symbol backups_retention_period: Aufbewahrungsfrist für Archive bootstrap_timeline_accounts: Neuen Nutzern immer diese Konten empfehlen closed_registrations_message: Nachricht, falls Registrierungen deaktiviert sind content_cache_retention_period: Aufbewahrungsfrist für externe Inhalte custom_css: Eigenes CSS + favicon: Favicon mascot: Benutzerdefiniertes Maskottchen (Legacy) media_cache_retention_period: Aufbewahrungsfrist für Medien im Cache peers_api_enabled: Die entdeckten Server im Fediverse über die API veröffentlichen diff --git a/config/locales/simple_form.eo.yml b/config/locales/simple_form.eo.yml index 40535bf96bc12e..021be93b5d74cb 100644 --- a/config/locales/simple_form.eo.yml +++ b/config/locales/simple_form.eo.yml @@ -235,6 +235,7 @@ eo: warn: Kaŝi malantaŭ averto form_admin_settings: activity_api_enabled: Publikigi entutajn statistikojn pri uzantagado en la API + app_icon: Apbildo backups_retention_period: Uzantoarkivretendauro bootstrap_timeline_accounts: Ĉiam rekomendi ĉi tiujn kontojn al novaj uzantoj closed_registrations_message: Kutima mesaĝo kiam registroj ne estas disponeblaj diff --git a/config/locales/simple_form.es-AR.yml b/config/locales/simple_form.es-AR.yml index 1d1ac876894f94..71d9726b079423 100644 --- a/config/locales/simple_form.es-AR.yml +++ b/config/locales/simple_form.es-AR.yml @@ -243,11 +243,13 @@ es-AR: warn: Ocultar con una advertencia form_admin_settings: activity_api_enabled: Publicar estadísticas agregadas sobre la actividad de la cuenta en la API + app_icon: Ícono de la aplicación backups_retention_period: Período de retención del archivo historial del usuario bootstrap_timeline_accounts: Siempre recomendar estas cuentas a usuarios nuevos closed_registrations_message: Mensaje personalizado cuando los registros no están disponibles content_cache_retention_period: Período de retención de contenido remoto custom_css: CSS personalizado + favicon: Favicón mascot: Mascota personalizada (legado) media_cache_retention_period: Período de retención de la caché de medios peers_api_enabled: Publicar lista de servidores descubiertos en la API diff --git a/config/locales/simple_form.es-MX.yml b/config/locales/simple_form.es-MX.yml index 19a1c9d0c095f9..730e9b265b335a 100644 --- a/config/locales/simple_form.es-MX.yml +++ b/config/locales/simple_form.es-MX.yml @@ -243,11 +243,13 @@ es-MX: warn: Ocultar con una advertencia form_admin_settings: activity_api_enabled: Publicar estadísticas locales acerca de la actividad de usuario en la API + app_icon: Icono de la app backups_retention_period: Período de retención del archivo de usuario bootstrap_timeline_accounts: Recomendar siempre estas cuentas a nuevos usuarios closed_registrations_message: Mensaje personalizado cuando los registros no están disponibles content_cache_retention_period: Período de retención de contenido remoto custom_css: CSS personalizado + favicon: Favicon mascot: Mascota personalizada (legado) media_cache_retention_period: Período de retención de caché multimedia peers_api_enabled: Publicar lista de servidores descubiertos en la API diff --git a/config/locales/simple_form.es.yml b/config/locales/simple_form.es.yml index cc71e192665609..f2d91349adad6b 100644 --- a/config/locales/simple_form.es.yml +++ b/config/locales/simple_form.es.yml @@ -243,11 +243,13 @@ es: warn: Ocultar con una advertencia form_admin_settings: activity_api_enabled: Publicar estadísticas agregadas sobre la actividad del usuario con la API + app_icon: Icono de la app backups_retention_period: Período de retención del archivo de usuario bootstrap_timeline_accounts: Recomendar siempre estas cuentas a nuevos usuarios closed_registrations_message: Mensaje personalizado cuando los registros no están disponibles content_cache_retention_period: Período de retención de contenido remoto custom_css: CSS personalizado + favicon: Favicon mascot: Mascota personalizada (legado) media_cache_retention_period: Período de retención de caché multimedia peers_api_enabled: Publicar lista de servidores descubiertos en la API diff --git a/config/locales/simple_form.fi.yml b/config/locales/simple_form.fi.yml index 214308ba5cc6e6..de099a21be2e1a 100644 --- a/config/locales/simple_form.fi.yml +++ b/config/locales/simple_form.fi.yml @@ -243,11 +243,13 @@ fi: warn: Piilota varoittaen form_admin_settings: activity_api_enabled: Julkaise yhteenlasketut tilastot käyttäjätoiminnasta ohjelmointirajapinnassa + app_icon: Sovelluskuvake backups_retention_period: Käyttäjän arkiston säilytysaika bootstrap_timeline_accounts: Suosittele aina näitä tilejä uusille käyttäjille closed_registrations_message: Mukautettu viesti, kun rekisteröityminen ei ole saatavilla content_cache_retention_period: Etäsisällön säilytysaika custom_css: Mukautettu CSS + favicon: Suosikkikuvake mascot: Mukautettu maskotti (vanhentunut ominaisuus) media_cache_retention_period: Mediasisältövälimuistin säilytysaika peers_api_enabled: Julkaise löydettyjen palvelinten luettelo ohjelmointirajapinnassa diff --git a/config/locales/simple_form.fo.yml b/config/locales/simple_form.fo.yml index 8f0b51719fceed..b334d4f034ac5f 100644 --- a/config/locales/simple_form.fo.yml +++ b/config/locales/simple_form.fo.yml @@ -243,11 +243,13 @@ fo: warn: Fjal við eini ávaring form_admin_settings: activity_api_enabled: Útgev samantald hagtøl um brúkaravirksemi í API'num + app_icon: App ikon backups_retention_period: Hvussu leingi verða brúkarasøvn goymd bootstrap_timeline_accounts: Mæl altíð nýggjum brúkarum at fylgja hesar kontur closed_registrations_message: Serskild boð, tá tað ikki er møguligt at tilmelda seg content_cache_retention_period: Tíðarskeið fyri varðveiðslu av fjartilfari custom_css: Serskilt CSS + favicon: Favikon mascot: Serskildur maskottur (arvur) media_cache_retention_period: Tíðarskeið, har miðlagoymslur verða varðveittar peers_api_enabled: Kunnger lista við uppdagaðum ambætarum í API'num diff --git a/config/locales/simple_form.gl.yml b/config/locales/simple_form.gl.yml index 0653d7620430ec..57bf31b42d2851 100644 --- a/config/locales/simple_form.gl.yml +++ b/config/locales/simple_form.gl.yml @@ -243,11 +243,13 @@ gl: warn: Agochar tras un aviso form_admin_settings: activity_api_enabled: Publicar na API estatísticas agregadas acerca da actividade das usuarias + app_icon: Icona da app backups_retention_period: Período de retención do arquivo da usuaria bootstrap_timeline_accounts: Recomendar sempre estas contas ás novas usuarias closed_registrations_message: Mensaxe personalizada para cando o rexistro está pechado content_cache_retention_period: Período de retención de contido remoto custom_css: CSS personalizado + favicon: Favicon mascot: Mascota propia (herdado) media_cache_retention_period: Período de retención da caché multimedia peers_api_enabled: Publicar na API unha lista dos servidores descubertos diff --git a/config/locales/simple_form.he.yml b/config/locales/simple_form.he.yml index 1331d2f62bc157..f79ddb9b34e832 100644 --- a/config/locales/simple_form.he.yml +++ b/config/locales/simple_form.he.yml @@ -243,11 +243,13 @@ he: warn: הסתרה עם אזהרה form_admin_settings: activity_api_enabled: פרסום סטטיסטיקות מקובצות עבור פעילות משתמשים באמצעות מנשק התוכנה + app_icon: אייקון יישומון backups_retention_period: תקופת השמירה של ארכיון המשתמש bootstrap_timeline_accounts: המלצה על חשבונות אלה למשתמשים חדשים closed_registrations_message: הודעה מיוחדת כשההרשמה לא מאופשרת content_cache_retention_period: תקופת השמירה על תוכן חיצוני custom_css: CSS בהתאמה אישית + favicon: סמל מועדפים (Favicon) mascot: סמל השרת (ישן) media_cache_retention_period: תקופת שמירת מטמון מדיה peers_api_enabled: פרסם רשימה של שרתים שנתגלו באמצעות ה-API diff --git a/config/locales/simple_form.hu.yml b/config/locales/simple_form.hu.yml index 6ccca2bc847928..518a82ec163a99 100644 --- a/config/locales/simple_form.hu.yml +++ b/config/locales/simple_form.hu.yml @@ -211,6 +211,7 @@ hu: setting_default_privacy: Bejegyzések láthatósága setting_default_sensitive: Minden médiafájl megjelölése kényesként setting_delete_modal: Megerősítés kérése bejegyzés törlése előtt + setting_disable_hover_cards: Profilelőnézet letiltása föléhúzás esetén setting_disable_swiping: Elhúzás művelet kikapcsolása setting_display_media: Média megjelenítése setting_display_media_default: Alapértelmezés @@ -242,11 +243,13 @@ hu: warn: Elrejtés figyelmeztetéssel form_admin_settings: activity_api_enabled: Összesített statisztikák közzététele az API-ban a felhasználók aktivitásáról + app_icon: Alkalmazásikon backups_retention_period: Felhasználói archívum megtartási időszaka bootstrap_timeline_accounts: Mindig javasoljuk ezeket a fiókokat az új felhasználók számára closed_registrations_message: A feliratkozáskor megjelenő egyéni üzenet nem érhető el content_cache_retention_period: Távoli tartalmak megtartási időszaka custom_css: Egyéni CSS + favicon: Könyvjelzőikon mascot: Egyéni kabala (örökölt) media_cache_retention_period: Média-gyorsítótár megtartási időszaka peers_api_enabled: Felfedezett kiszolgálók listájának közzététele az API-ban diff --git a/config/locales/simple_form.is.yml b/config/locales/simple_form.is.yml index cc267db8d5629e..9e28ef9be695b9 100644 --- a/config/locales/simple_form.is.yml +++ b/config/locales/simple_form.is.yml @@ -243,11 +243,13 @@ is: warn: Fela með aðvörun form_admin_settings: activity_api_enabled: Birta samantektartölfræði um virkni notanda í API-kerfisviðmótinu + app_icon: Táknmynd forrits backups_retention_period: Tímalengd sem safni notandans er haldið eftir bootstrap_timeline_accounts: Alltaf mæla með þessum notendaaðgöngum fyrir nýja notendur closed_registrations_message: Sérsniðin skilaboð þegar ekki er hægt að nýskrá content_cache_retention_period: Tímabil sem á að geyma fjartengt efni custom_css: Sérsniðið CSS + favicon: Auðkennismynd mascot: Sérsniðið gæludýr (eldra) media_cache_retention_period: Tímalengd sem myndefni haldið peers_api_enabled: Birta lista yfir uppgötvaða netþjóna í API-kerfisviðmótinu diff --git a/config/locales/simple_form.it.yml b/config/locales/simple_form.it.yml index 540812faa9958f..eddc86b4e11ad8 100644 --- a/config/locales/simple_form.it.yml +++ b/config/locales/simple_form.it.yml @@ -243,11 +243,13 @@ it: warn: Nascondi con avviso form_admin_settings: activity_api_enabled: Pubblica le statistiche aggregate sull'attività degli utenti nell'API + app_icon: Icona app backups_retention_period: Periodo di conservazione dell'archivio utente bootstrap_timeline_accounts: Consiglia sempre questi account ai nuovi utenti closed_registrations_message: Messaggio personalizzato quando le iscrizioni non sono disponibili content_cache_retention_period: Periodo di ritenzione del contenuto remoto custom_css: Personalizza CSS + favicon: Favicon mascot: Personalizza mascotte (legacy) media_cache_retention_period: Periodo di conservazione della cache multimediale peers_api_enabled: Pubblica l'elenco dei server scoperti nell'API diff --git a/config/locales/simple_form.ko.yml b/config/locales/simple_form.ko.yml index 1107e4cba5b736..b94b4b3d40425b 100644 --- a/config/locales/simple_form.ko.yml +++ b/config/locales/simple_form.ko.yml @@ -243,11 +243,13 @@ ko: warn: 경고와 함께 숨기기 form_admin_settings: activity_api_enabled: API에 유저 활동에 대한 통계 발행 + app_icon: 앱 아이콘 backups_retention_period: 사용자 아카이브 유지 기한 bootstrap_timeline_accounts: 새로운 사용자들에게 추천할 계정들 closed_registrations_message: 가입이 불가능 할 때의 사용자 지정 메시지 content_cache_retention_period: 리모트 콘텐츠 보유 기간 custom_css: 사용자 정의 CSS + favicon: 파비콘 mascot: 사용자 정의 마스코트 (legacy) media_cache_retention_period: 미디어 캐시 유지 기한 peers_api_enabled: API에 발견 된 서버들의 목록 발행 diff --git a/config/locales/simple_form.lt.yml b/config/locales/simple_form.lt.yml index feec37ae003676..b31803409b3976 100644 --- a/config/locales/simple_form.lt.yml +++ b/config/locales/simple_form.lt.yml @@ -137,6 +137,7 @@ lt: setting_default_privacy: Skelbimo privatumas setting_default_sensitive: Visada žymėti mediją kaip jautrią setting_delete_modal: Rodyti patvirtinimo dialogą prieš ištrinant įrašą + setting_disable_hover_cards: Išjungti profilio peržiūrą užvedus setting_disable_swiping: Išjungti perbraukimo judėjimus setting_display_media: Medijos rodymas setting_display_media_hide_all: Slėpti viską @@ -164,9 +165,11 @@ lt: warn: Slėpti su įspėjimu form_admin_settings: activity_api_enabled: Skelbti suvestinį statistiką apie naudotojų veiklą per API + app_icon: Programėlės piktograma bootstrap_timeline_accounts: Visada rekomenduoti šias paskyras naujiems naudotojams content_cache_retention_period: Nuotolinio turinio saugojimo laikotarpis custom_css: Pasirinktinis CSS + favicon: Svetainės piktograma mascot: Pasirinktinis talismanas (pasenęs) registrations_mode: Kas gali užsiregistruoti require_invite_text: Reikalauti priežasties prisijungti diff --git a/config/locales/simple_form.nl.yml b/config/locales/simple_form.nl.yml index 5110e1e6d09b9a..56331934064401 100644 --- a/config/locales/simple_form.nl.yml +++ b/config/locales/simple_form.nl.yml @@ -243,11 +243,13 @@ nl: warn: Met een waarschuwing verbergen form_admin_settings: activity_api_enabled: Statistieken over gebruikersactiviteit via de API publiceren + app_icon: App-pictogram backups_retention_period: Bewaartermijn gebruikersarchief bootstrap_timeline_accounts: Accounts die altijd aan nieuwe gebruikers worden aanbevolen closed_registrations_message: Aangepast bericht wanneer registratie is uitgeschakeld content_cache_retention_period: Bewaartermijn voor externe inhoud custom_css: Aangepaste CSS + favicon: Favicon mascot: Aangepaste mascotte (legacy) media_cache_retention_period: Bewaartermijn mediacache peers_api_enabled: Lijst van bekende servers via de API publiceren diff --git a/config/locales/simple_form.pl.yml b/config/locales/simple_form.pl.yml index b8de10de26a336..39e1814195f877 100644 --- a/config/locales/simple_form.pl.yml +++ b/config/locales/simple_form.pl.yml @@ -243,11 +243,13 @@ pl: warn: Ukryj z ostrzeżeniem form_admin_settings: activity_api_enabled: Publikuj zagregowane statystyki dotyczące aktywności użytkownika w API + app_icon: Ikona aplikacji backups_retention_period: Okres przechowywania archiwum użytkownika bootstrap_timeline_accounts: Zawsze rekomenduj te konta nowym użytkownikom closed_registrations_message: Niestandardowa wiadomość, gdy rejestracje nie są dostępne content_cache_retention_period: Okres zachowywania zdalnych treści custom_css: Niestandardowy CSS + favicon: Favicon mascot: Własna ikona media_cache_retention_period: Okres przechowywania pamięci podręcznej peers_api_enabled: Opublikuj listę odkrytych serwerów w API diff --git a/config/locales/simple_form.pt-PT.yml b/config/locales/simple_form.pt-PT.yml index f993687b91bee4..ef7dfd00a80fa2 100644 --- a/config/locales/simple_form.pt-PT.yml +++ b/config/locales/simple_form.pt-PT.yml @@ -243,11 +243,13 @@ pt-PT: warn: Ocultar com um aviso form_admin_settings: activity_api_enabled: Publicar estatísticas agregadas sobre a atividade dos utilizadores na API + app_icon: Ícone da aplicação backups_retention_period: Período de retenção de arquivos de utilizador bootstrap_timeline_accounts: Recomendar sempre estas contas para novos utilizadores closed_registrations_message: Mensagem personalizada quando as inscrições não estiverem disponíveis content_cache_retention_period: Período de retenção de conteúdos remotos custom_css: CSS personalizado + favicon: Ícone de favoritos mascot: Mascote personalizada (legado) media_cache_retention_period: Período de retenção de ficheiros de media em cache peers_api_enabled: Publicar lista de servidores descobertos na API diff --git a/config/locales/simple_form.sl.yml b/config/locales/simple_form.sl.yml index 14551774fe0b29..a937f17206e465 100644 --- a/config/locales/simple_form.sl.yml +++ b/config/locales/simple_form.sl.yml @@ -243,11 +243,13 @@ sl: warn: Skrij z opozorilom form_admin_settings: activity_api_enabled: Objavi združeno statistiko o dejavnosti uporabnikov v API-ju + app_icon: Ikona programa backups_retention_period: Obdobje hrambe arhivov uporabnikov bootstrap_timeline_accounts: Vedno priporočaj te račune novim uporabnikom closed_registrations_message: Sporočilo po meri, ko registracije niso na voljo content_cache_retention_period: Obdobje hranjenja vsebine z ostalih strežnikov custom_css: CSS po meri + favicon: Ikona spletne strani mascot: Maskota po meri (opuščeno) media_cache_retention_period: Obdobje hrambe predpomnilnika predstavnosti peers_api_enabled: Objavi seznam odkritih strežnikov v API-ju diff --git a/config/locales/simple_form.sq.yml b/config/locales/simple_form.sq.yml index 6fedf315724746..4236d7dce2673b 100644 --- a/config/locales/simple_form.sq.yml +++ b/config/locales/simple_form.sq.yml @@ -243,11 +243,13 @@ sq: warn: Fshihe me një sinjalizim form_admin_settings: activity_api_enabled: Publikoni te API statistika përmbledhëse rreth veprimtarisë së përdoruesve + app_icon: Ikonë aplikacioni backups_retention_period: Periudhë mbajtjeje arkivash përdoruesish bootstrap_timeline_accounts: Rekomandoju përherë këto llogari përdoruesve të rinj closed_registrations_message: Mesazh vetjak për pamundësi regjistrimesh të reja content_cache_retention_period: Periudhë mbajtjeje lënde të largët custom_css: CSS Vetjake + favicon: Favikonë mascot: Simbol vetjak (e dikurshme) media_cache_retention_period: Periudhë mbajtjeje lënde media peers_api_enabled: Publiko te API listë shërbyesish të zbuluar diff --git a/config/locales/simple_form.sr-Latn.yml b/config/locales/simple_form.sr-Latn.yml index 22c07aa7c65938..40b710b078e690 100644 --- a/config/locales/simple_form.sr-Latn.yml +++ b/config/locales/simple_form.sr-Latn.yml @@ -243,11 +243,13 @@ sr-Latn: warn: Sakrij uz upozorenje form_admin_settings: activity_api_enabled: Objavi prikupljenu statistiku o korisničkoj aktivnosti u API + app_icon: Ikonica aplikacije backups_retention_period: Period čuvanja korisničke arhive bootstrap_timeline_accounts: Uvek preporuči ove naloge novim korisnicima closed_registrations_message: Prilagođena poruka kada prijave nisu moguće content_cache_retention_period: Period zadržavanja udaljenog sadržaja custom_css: Prilagođeni CSS + favicon: Favikon mascot: Prilagođena maskota (nasleđe) media_cache_retention_period: Period čuvanja keša medija peers_api_enabled: Objavite listu otkrivenih servera u API diff --git a/config/locales/simple_form.sr.yml b/config/locales/simple_form.sr.yml index 10c1796637e999..c0a9ac2d8963c6 100644 --- a/config/locales/simple_form.sr.yml +++ b/config/locales/simple_form.sr.yml @@ -243,11 +243,13 @@ sr: warn: Сакриј уз упозорење form_admin_settings: activity_api_enabled: Објави прикупљену статистику о корисничкој активности у API + app_icon: Иконица апликације backups_retention_period: Период чувања корисничке архиве bootstrap_timeline_accounts: Увек препоручи ове налоге новим корисницима closed_registrations_message: Прилагођена порука када пријаве нису могуће content_cache_retention_period: Период задржавања удаљеног садржаја custom_css: Прилагођени CSS + favicon: Фавикон mascot: Прилагођена маскота (наслеђе) media_cache_retention_period: Период чувања кеша медија peers_api_enabled: Објавите листу откривених сервера у API diff --git a/config/locales/simple_form.sv.yml b/config/locales/simple_form.sv.yml index 11d142a2bd6d73..2281e4b4abe109 100644 --- a/config/locales/simple_form.sv.yml +++ b/config/locales/simple_form.sv.yml @@ -242,11 +242,13 @@ sv: warn: Dölj med en varning form_admin_settings: activity_api_enabled: Publicera aggregerad statistik om användaraktivitet i API:et + app_icon: Appikon backups_retention_period: Lagringsperiod för användararkivet bootstrap_timeline_accounts: Rekommendera alltid dessa konton till nya användare closed_registrations_message: Anpassat meddelande när nyregistreringar inte är tillgängliga content_cache_retention_period: Förvaringsperiod för fjärrinnehåll custom_css: Anpassad CSS + favicon: Favicon mascot: Anpassad maskot (tekniskt arv) media_cache_retention_period: Tid för bibehållande av mediecache peers_api_enabled: Publicera lista över upptäckta servrar i API:et diff --git a/config/locales/simple_form.tr.yml b/config/locales/simple_form.tr.yml index b1e90122eca499..f098bfd3ca6d32 100644 --- a/config/locales/simple_form.tr.yml +++ b/config/locales/simple_form.tr.yml @@ -243,11 +243,13 @@ tr: warn: Uyarıyla gizle form_admin_settings: activity_api_enabled: API'deki kullanıcı etkinliği hakkında toplu istatistikler yayınlayın + app_icon: Uygulama simgesi backups_retention_period: Kullanıcı arşivi saklama süresi bootstrap_timeline_accounts: Bu hesapları yeni kullanıcılara her zaman öner closed_registrations_message: Kayıt olma mevcut değilken gösterilen özel ileti content_cache_retention_period: Uzak içerik saklama süresi custom_css: Özel CSS + favicon: Yer imi simgesi mascot: Özel maskot (eski) media_cache_retention_period: Medya önbelleği saklama süresi peers_api_enabled: API'de keşfedilen sunucuların listesini yayınla diff --git a/config/locales/simple_form.uk.yml b/config/locales/simple_form.uk.yml index 332f2c91dc9daf..2f494534ea63b9 100644 --- a/config/locales/simple_form.uk.yml +++ b/config/locales/simple_form.uk.yml @@ -243,11 +243,13 @@ uk: warn: Сховати за попередженням form_admin_settings: activity_api_enabled: Публікація агрегованої статистики про активність користувачів + app_icon: Значок застосунку backups_retention_period: Період утримання архіву користувача bootstrap_timeline_accounts: Завжди рекомендувати новим користувачам ці облікові записи closed_registrations_message: Показуване повідомлення, якщо реєстрація недоступна content_cache_retention_period: Період збереження віддаленого вмісту custom_css: Користувацький CSS + favicon: Значок сайту mascot: Користувацький символ (застарілий) media_cache_retention_period: Період збереження кешу медіа peers_api_enabled: Опублікувати список знайдених серверів у API diff --git a/config/locales/simple_form.zh-CN.yml b/config/locales/simple_form.zh-CN.yml index bbee8b6706297b..ab440e04f43c7f 100644 --- a/config/locales/simple_form.zh-CN.yml +++ b/config/locales/simple_form.zh-CN.yml @@ -243,11 +243,13 @@ zh-CN: warn: 隐藏时显示警告信息 form_admin_settings: activity_api_enabled: 在 API 中发布有关用户活动的汇总统计数据 + app_icon: 应用图标 backups_retention_period: 用户存档保留期 bootstrap_timeline_accounts: 推荐新用户关注以下账号 closed_registrations_message: 在关闭注册时显示的自定义消息 content_cache_retention_period: 外站内容保留期 custom_css: 自定义 CSS + favicon: Favicon mascot: 自定义吉祥物(旧) media_cache_retention_period: 媒体缓存保留期 peers_api_enabled: 在API中公开的已知实例的服务器的列表 diff --git a/config/locales/simple_form.zh-TW.yml b/config/locales/simple_form.zh-TW.yml index 7c460a25f58e24..15ccaf8a17782d 100644 --- a/config/locales/simple_form.zh-TW.yml +++ b/config/locales/simple_form.zh-TW.yml @@ -24,7 +24,7 @@ zh-TW: type_html: 設定要使用 %{acct} 做的事 types: disable: 禁止該使用者使用他們的帳號,但是不刪除或隱藏他們的內容。 - none: 使用這個寄送警告給該使用者,而不進行其他動作。 + none: 使用這個寄送警告至該使用者,而不進行其他動作。 sensitive: 強制標記此使用者所有多媒體附加檔案為敏感內容。 silence: 禁止該使用者發公開嘟文,從無跟隨他們的帳號中隱藏嘟文與通知。關閉所有對此帳號之檢舉報告。 suspend: 禁止所有對該帳號任何互動,並且刪除其內容。三十天內可以撤銷此動作。關閉所有對此帳號之檢舉報告。 @@ -34,7 +34,7 @@ zh-TW: ends_at: 可選的。公告會於該時間點自動取消發布 scheduled_at: 空白則立即發布公告 starts_at: 可選的。使公告於特定時間範圍內顯示 - text: 您可以使用嘟文語法,但請小心別讓公告太鴨霸而佔據使用者的整個版面。 + text: 您可以使用嘟文語法,但請小心別使公告太鴨霸而佔據使用者的整個版面。 appeal: text: 您只能對警示提出一次申訴 defaults: @@ -47,7 +47,7 @@ zh-TW: digest: 僅於您長時間未登入且於未登入期間收到私訊時傳送 email: 您將收到一封確認電子郵件 header: 支援 WEBP、PNG、GIF 或 JPG 圖片格式,檔案最大為 %{size}。將等比例縮減至 %{dimensions} 像素 - inbox_url: 從您想要使用的中繼首頁複製網址 + inbox_url: 自您想要使用的中繼首頁複製網址 irreversible: 已過濾的嘟文將會不可逆地消失,即便之後移除過濾器也一樣 locale: 使用者介面、電子郵件與推播通知的語言 password: 使用至少 8 個字元 @@ -92,7 +92,7 @@ zh-TW: site_contact_email: 其他人如何聯繫您關於法律或支援之諮詢。 site_contact_username: 其他人如何於 Mastodon 上聯繫您。 site_extended_description: 任何其他可能對訪客或使用者有用的額外資訊。可由 Markdown 語法撰寫。 - site_short_description: 一段有助於辨別您伺服器的簡短說明。例如:誰運行該伺服器、該伺服器是提供給哪些人群? + site_short_description: 一段有助於辨別您伺服器的簡短說明。例如:誰運行該伺服器、該伺服器是提供至哪些人群? site_terms: 使用您自己的隱私權政策,或者保留空白以使用預設值。可由 Markdown 語法撰寫。 site_title: 除了網域外,其他人該如何指稱您的伺服器。 status_page_url: 當服務中斷時,可以提供使用者了解伺服器資訊頁面之 URL @@ -101,11 +101,11 @@ zh-TW: timeline_preview: 未登入之訪客能夠瀏覽此伺服器上最新的公開嘟文。 trendable_by_default: 跳過手動審核熱門內容。仍能於登上熱門趨勢後移除個別內容。 trends: 熱門趨勢將顯示於您伺服器上正在吸引大量注意力的嘟文、主題標籤、或者新聞。 - trends_as_landing_page: 顯示熱門趨勢內容給未登入使用者及訪客而不是關於此伺服器之描述。需要啟用熱門趨勢。 + trends_as_landing_page: 顯示熱門趨勢內容至未登入使用者及訪客而不是關於此伺服器之描述。需要啟用熱門趨勢。 form_challenge: current_password: 您正要進入安全區域 imports: - data: 從其他 Mastodon 伺服器匯出的 CSV 檔案 + data: 自其他 Mastodon 伺服器匯出的 CSV 檔案 invite_request: text: 這會協助我們審核您的申請 ip_block: @@ -133,7 +133,7 @@ zh-TW: role: 角色控制使用者有哪些權限 user_role: color: 於整個使用者介面中用於角色的顏色,十六進位格式的 RGB - highlighted: 這會使角色公開可見 + highlighted: 這將使角色公開可見 name: 角色的公開名稱,如果角色設定為顯示為徽章 permissions_as_keys: 有此角色的使用者將有權存取... position: 某些情況下,衝突的解決方式由更高階的角色決定。某些動作只能由優先程度較低的角色執行 @@ -243,11 +243,13 @@ zh-TW: warn: 隱藏於警告之後 form_admin_settings: activity_api_enabled: 於 API 中公開使用者活躍度的統計數據 + app_icon: 應用程式圖示 backups_retention_period: 使用者封存資料保留期間 - bootstrap_timeline_accounts: 永遠推薦這些帳號給新使用者 + bootstrap_timeline_accounts: 永遠推薦這些帳號至新使用者 closed_registrations_message: 當註冊關閉時的客製化訊息 content_cache_retention_period: 遠端內容保留期限 custom_css: 自訂 CSS + favicon: 網站圖示 (Favicon) mascot: 自訂吉祥物 (legacy) media_cache_retention_period: 多媒體快取資料保留期間 peers_api_enabled: 於 API 中公開已知伺服器的列表 diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 1317d5f70740a5..3a548c226557e9 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -227,7 +227,7 @@ zh-TW: update_user_role: 更新角色 actions: approve_appeal_html: "%{name} 已批准來自 %{target} 的審核決定申訴" - approve_user_html: "%{name} 已批准從 %{target} 而來的註冊" + approve_user_html: "%{name} 已批准自 %{target} 而來的註冊" assigned_to_self_report_html: "%{name} 將報告 %{target} 指派給自己" change_email_user_html: "%{name} 已變更使用者 %{target} 的電子郵件地址" change_role_user_html: "%{name} 已變更 %{target} 的角色" @@ -264,10 +264,10 @@ zh-TW: memorialize_account_html: "%{name} 將 %{target} 設定為追悼帳號" promote_user_html: "%{name} 對使用者 %{target} 已進行晉級操作" reject_appeal_html: "%{name} 已回絕來自 %{target} 的審核決定申訴" - reject_user_html: "%{name} 已回絕從 %{target} 而來的註冊" + reject_user_html: "%{name} 已回絕自 %{target} 而來的註冊" remove_avatar_user_html: "%{name} 已移除 %{target} 的大頭貼" reopen_report_html: "%{name} 重新開啟 %{target} 的檢舉" - resend_user_html: "%{name} 已重新發送驗證信給 %{target}" + resend_user_html: "%{name} 已重新發送驗證信至 %{target}" reset_password_user_html: "%{name} 已重新設定使用者 %{target} 的密碼" resolve_report_html: "%{name} 已處理 %{target} 的檢舉" sensitive_account_html: "%{name} 將 %{target} 的媒體檔案標記為敏感內容" @@ -383,7 +383,7 @@ zh-TW: confirm: 停權 permanent_action: 撤銷停權將不會回復任何資料或跟隨關係 preamble_html: 您將要停權 %{domain} 及其子網域。 - remove_all_data: 此操作將從您伺服器移除所有來自此網域帳號之內容、媒體、個人檔案資料。 + remove_all_data: 此操作將自您伺服器移除所有來自此網域帳號之內容、媒體、個人檔案資料。 stop_communication: 您伺服器將停止與這些伺服器交流。 title: 確認對 %{domain} 網域之封鎖 undo_relationships: 此操作將撤銷任何您伺服器與此伺服器帳號間之跟隨關係。 @@ -555,11 +555,11 @@ zh-TW: relays: add_new: 新增中繼站 delete: 刪除 - description_html: "聯邦中繼站 是種中繼伺服器,會於訂閱並推送至此中繼站的伺服器之間交換大量的公開嘟文。中繼站也能協助小型或中型伺服器從聯邦宇宙中探索內容,而無須本地使用者手動跟隨遠端伺服器的其他使用者。" + description_html: "聯邦中繼站 是種中繼伺服器,會於訂閱並推送至此中繼站的伺服器之間交換大量的公開嘟文。中繼站也能協助小型或中型伺服器自聯邦宇宙中探索內容,而無須本地使用者手動跟隨遠端伺服器的其他使用者。" disable: 停用 disabled: 停用 enable: 啟用 - enable_hint: 啟用後,您的伺服器將訂閱該中繼的所有公開文章,並將會此伺服器的公開文章發送給它。 + enable_hint: 啟用後,您的伺服器將訂閱該中繼的所有公開文章,並將此伺服器的公開文章向它發送。 enabled: 已啟用 inbox_url: 中繼 URL pending: 等待中繼站審核 @@ -648,7 +648,7 @@ zh-TW: delete_data_html: 於即日起 30 天後刪除 @%{acct}之個人檔案與內容,除非他們於期限前被解除停權 preview_preamble_html: "@%{acct} 將收到關於以下內容之警告:" record_strike_html: 紀錄關於 @%{acct}之警示有助於您升級對此帳號未來違規處理 - send_email_html: 寄一封警告 e-mail 給 @%{acct} + send_email_html: 寄一封警告 e-mail 至 @%{acct} warning_placeholder: 選填之其他站務動作理由。 target_origin: 檢舉帳號之來源 title: 檢舉 @@ -671,7 +671,7 @@ zh-TW: description_html: 透過使用者角色,您可以自訂您的使用者可以存取 Mastodon 的哪些功能與區域。 edit: 編輯「%{name}」角色 everyone: 預設權限 - everyone_full_description_html: 這是會影響所有使用者基本角色,即使是那些沒有被分配角色的使用者也一樣。其他所有的角色都從它繼承權限。 + everyone_full_description_html: 這是會影響所有使用者基本角色,即使是那些沒有被分配角色的使用者也一樣。其他所有的角色都自它繼承權限。 permissions_count: other: "%{count} 個權限" privileges: @@ -757,8 +757,8 @@ zh-TW: title: 探索 trends: 熱門趨勢 domain_blocks: - all: 給任何人 - disabled: 給沒有人 + all: 至任何人 + disabled: 至沒有人 users: 套用至所有登入的本站使用者 registrations: moderation_recommandation: 對所有人開放註冊之前,請確保您有人手充足且反應靈敏的管理員團隊! @@ -797,7 +797,7 @@ zh-TW: back_to_account: 返回帳號資訊頁面 back_to_report: 回到檢舉報告頁面 batch: - remove_from_report: 從檢舉報告中移除 + remove_from_report: 自檢舉報告中移除 report: 檢舉報告 deleted: 已刪除 favourites: 最愛 @@ -1055,7 +1055,7 @@ zh-TW: dont_have_your_security_key: 找不到您的安全金鑰? forgot_password: 忘記密碼? invalid_reset_password_token: 密碼重設 token 無效或已過期。請重新設定密碼。 - link_to_otp: 請從您手機輸入兩階段驗證 (2FA) 或備用驗證碼 + link_to_otp: 請自您手機輸入兩階段驗證 (2FA) 或備用驗證碼 link_to_webauth: 使用您的安全金鑰 log_in_with: 登入,使用 login: 登入 @@ -1138,7 +1138,7 @@ zh-TW: x_seconds: "%{count}秒" deletes: challenge_not_passed: 您所輸入的資料不正確 - confirm_password: 輸入您現在的密碼以驗證身份 + confirm_password: 輸入您目前的密碼以驗證身份 confirm_username: 請輸入您的使用者名稱以作確認 proceed: 刪除帳號 success_msg: 您的帳號已經成功刪除 @@ -1170,7 +1170,7 @@ zh-TW: recipient: 此致 reject_appeal: 駁回申訴 status: '嘟文編號 #%{id}' - status_removed: 嘟文已從系統中移除 + status_removed: 嘟文已自系統中移除 title: "%{action} 來自 %{date}" title_actions: delete_statuses: 嘟文移除 @@ -1238,10 +1238,10 @@ zh-TW: add_keyword: 新增關鍵字 keywords: 關鍵字 statuses: 各別嘟文 - statuses_hint_html: 此過濾器會套用至所選之各別嘟文,無論其是否符合下列關鍵字。審閱或從過濾條件移除嘟文。 + statuses_hint_html: 此過濾器會套用至所選之各別嘟文,無論其是否符合下列關鍵字。審閱或自過濾條件移除嘟文。 title: 編輯過濾條件 errors: - deprecated_api_multiple_keywords: 這些參數無法從此應用程式中更改,因為它們適用於一或多個過濾器關鍵字。請使用較新的應用程式或是網頁介面。 + deprecated_api_multiple_keywords: 這些參數無法自此應用程式中更改,因為它們適用於一或多個過濾器關鍵字。請使用較新的應用程式或是網頁介面。 invalid_context: 沒有提供內文或內文無效 index: contexts: "%{contexts} 中的過濾器" @@ -1262,7 +1262,7 @@ zh-TW: statuses: back_to_filter: 回到過濾器 batch: - remove: 從過濾器中移除 + remove: 自過濾器中移除 index: hint: 此過濾器會套用至所選之各別嘟文,不管它們有無符合其他條件。您可以從網頁介面中將更多嘟文加入至此過濾器。 title: 已過濾之嘟文 @@ -1419,7 +1419,7 @@ zh-TW: on_cooldown: 您最近已轉移過您的帳號。此功能將於 %{count} 天後可再度使用。 past_migrations: 以往的轉移紀錄 proceed_with_move: 移動跟隨者 - redirected_msg: 您的帳號現在指向 %{acct} + redirected_msg: 您的帳號目前重導向至 %{acct}。 redirecting_to: 您的帳號目前正被重新導向到 %{acct} set_redirect: 設定重新導向 warning: @@ -1552,7 +1552,7 @@ zh-TW: mutual: 跟隨彼此 primary: 主要 relationship: 關係 - remove_selected_domains: 從所選取網域中移除所有跟隨者 + remove_selected_domains: 自所選取網域中移除所有跟隨者 remove_selected_followers: 移除選取的跟隨者 remove_selected_follows: 取消跟隨選取的使用者 status: 帳號狀態 @@ -1599,7 +1599,7 @@ zh-TW: current_session: 目前的 session date: 日期 description: "%{platform} 上的 %{browser}" - explanation: 這些是現在正登入於您 Mastodon 帳號的瀏覽器。 + explanation: 這些是目前正登入於您 Mastodon 帳號的瀏覽器。 ip: IP 位址 platforms: adobe_air: Adobe Air From 00c110ac7ba0dd82840f1ff32113ed0e616c3470 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 09:23:07 +0200 Subject: [PATCH 084/101] New Crowdin Translations (automated) (#31047) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/ga.json | 294 ++++++++++++++++++++++++ app/javascript/mastodon/locales/lt.json | 4 +- config/locales/activerecord.ga.yml | 33 +++ config/locales/ca.yml | 2 + config/locales/da.yml | 1 + config/locales/de.yml | 2 + config/locales/devise.ga.yml | 89 +++++++ config/locales/doorkeeper.ga.yml | 2 + config/locales/en-GB.yml | 1 + config/locales/es-AR.yml | 1 + config/locales/fi.yml | 1 + config/locales/fo.yml | 2 + config/locales/he.yml | 1 + config/locales/hu.yml | 2 + config/locales/it.yml | 2 + config/locales/ko.yml | 1 + config/locales/lt.yml | 19 ++ config/locales/nl.yml | 1 + config/locales/nn.yml | 2 + config/locales/pt-PT.yml | 2 + config/locales/simple_form.en-GB.yml | 3 + config/locales/simple_form.nn.yml | 3 + config/locales/simple_form.vi.yml | 2 + config/locales/sl.yml | 1 + config/locales/sq.yml | 1 + config/locales/sv.yml | 1 + config/locales/tr.yml | 1 + config/locales/uk.yml | 1 + config/locales/vi.yml | 2 + config/locales/zh-CN.yml | 2 + config/locales/zh-TW.yml | 2 + 31 files changed, 479 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json index edf7618148ecff..468c7a03a1c4c6 100644 --- a/app/javascript/mastodon/locales/ga.json +++ b/app/javascript/mastodon/locales/ga.json @@ -17,9 +17,12 @@ "account.badges.group": "Grúpa", "account.block": "Déan cosc ar @{name}", "account.block_domain": "Bac ainm fearainn {domain}", + "account.block_short": "Bloc", "account.blocked": "Bactha", "account.browse_more_on_origin_server": "Brabhsáil níos mó ar an phróifíl bhunaidh", "account.cancel_follow_request": "Éirigh as iarratas leanta", + "account.copy": "Cóipeáil nasc chuig an bpróifíl", + "account.direct": "Luaigh @{name} go príobháideach", "account.disable_notifications": "Éirigh as ag cuir mé in eol nuair bpostálann @{name}", "account.domain_blocked": "Ainm fearainn bactha", "account.edit_profile": "Cuir an phróifíl in eagar", @@ -29,9 +32,12 @@ "account.featured_tags.last_status_never": "Níl postáil ar bith ann", "account.featured_tags.title": "Haischlib {name}", "account.follow": "Lean", + "account.follow_back": "Leanúint ar ais", "account.followers": "Leantóirí", "account.followers.empty": "Ní leanann éinne an t-úsáideoir seo fós.", + "account.followers_counter": "{count, plural, one {{counter} leantóir} other {{counter} leantóirí}}", "account.following": "Ag leanúint", + "account.following_counter": "{count, plural, one {{counter} ag leanúint} other {{counter} ag leanúint}}", "account.follows.empty": "Ní leanann an t-úsáideoir seo duine ar bith fós.", "account.go_to_profile": "Téigh go dtí próifíl", "account.hide_reblogs": "Folaigh moltaí ó @{name}", @@ -44,7 +50,11 @@ "account.mention": "Luaigh @{name}", "account.moved_to": "Tá tugtha le fios ag {name} gurb é an cuntas nua atá acu ná:", "account.mute": "Balbhaigh @{name}", + "account.mute_notifications_short": "Balbhaigh fógraí", + "account.mute_short": "Balbhaigh", "account.muted": "Balbhaithe", + "account.mutual": "Frithpháirteach", + "account.no_bio": "Níor tugadh tuairisc.", "account.open_original_page": "Oscail an leathanach bunaidh", "account.posts": "Postálacha", "account.posts_with_replies": "Postálacha agus freagraí", @@ -53,12 +63,14 @@ "account.requested_follow": "D'iarr {name} ort do chuntas a leanúint", "account.share": "Roinn próifíl @{name}", "account.show_reblogs": "Taispeáin moltaí ó @{name}", + "account.statuses_counter": "{count, plural, one {{counter} post} other {{counter} poist}}", "account.unblock": "Bain bac de @{name}", "account.unblock_domain": "Bain bac den ainm fearainn {domain}", "account.unblock_short": "Bain bac de", "account.unendorse": "Ná chuir ar an phróifíl mar ghné", "account.unfollow": "Ná lean a thuilleadh", "account.unmute": "Díbhalbhaigh @{name}", + "account.unmute_notifications_short": "Díbhalbhaigh fógraí", "account.unmute_short": "Díbhalbhaigh", "account_note.placeholder": "Cliceáil chun nóta a chuir leis", "admin.dashboard.daily_retention": "Ráta coinneála an úsáideora de réir an lae tar éis clárú", @@ -66,6 +78,10 @@ "admin.dashboard.retention.average": "Meán", "admin.dashboard.retention.cohort": "Mí cláraraithe", "admin.dashboard.retention.cohort_size": "Úsáideoirí nua", + "admin.impact_report.instance_accounts": "Próifílí cuntais a scriosfadh sé seo", + "admin.impact_report.instance_followers": "Leanúna a chaillfeadh ár n-úsáideoirí", + "admin.impact_report.instance_follows": "Leanúna a bheadh ​​​​a n-úsáideoirí chailleadh", + "admin.impact_report.title": "Achoimre ar an tionchar", "alert.rate_limited.message": "Atriail aris tar éis {retry_time, time, medium}.", "alert.rate_limited.title": "Rátatheoranta", "alert.unexpected.message": "Tharla earráid gan choinne.", @@ -73,6 +89,14 @@ "announcement.announcement": "Fógra", "attachments_list.unprocessed": "(neamhphróiseáilte)", "audio.hide": "Cuir fuaim i bhfolach", + "block_modal.remote_users_caveat": "Iarrfaimid ar an bhfreastalaí {domain} meas a bheith agat ar do chinneadh. Mar sin féin, ní ráthaítear comhlíonadh toisc go bhféadfadh roinnt freastalaithe bloic a láimhseáil ar bhealach difriúil. Seans go mbeidh postálacha poiblí fós le feiceáil ag úsáideoirí nach bhfuil logáilte isteach.", + "block_modal.show_less": "Taispeáin níos lú", + "block_modal.show_more": "Taispeáin níos mó", + "block_modal.they_cant_mention": "Ní féidir leo tú a lua ná a leanúint.", + "block_modal.they_cant_see_posts": "Ní féidir leo do chuid postálacha a fheiceáil agus ní fheicfidh tú a gcuid postanna.", + "block_modal.they_will_know": "Is féidir leo a fheiceáil go bhfuil bac orthu.", + "block_modal.title": "An bhfuil fonn ort an t-úsáideoir a bhlocáil?", + "block_modal.you_wont_see_mentions": "Ní fheicfidh tú postálacha a luann iad.", "boost_modal.combo": "Is féidir leat {combo} a bhrú chun é seo a scipeáil an chéad uair eile", "bundle_column_error.copy_stacktrace": "Cóipeáil tuairisc earráide", "bundle_column_error.error.body": "Ní féidir an leathanach a iarradh a sholáthar. Seans gurb amhlaidh mar gheall ar fhabht sa chód, nó mar gheall ar mhíréireacht leis an mbrabhsálaí.", @@ -95,8 +119,11 @@ "column.blocks": "Cuntais choiscthe", "column.bookmarks": "Leabharmharcanna", "column.community": "Amlíne áitiúil", + "column.direct": "Luann príobháideach", "column.directory": "Brabhsáil próifílí", "column.domain_blocks": "Fearainn bhactha", + "column.favourites": "Ceanáin", + "column.firehose": "Fothaí beo", "column.follow_requests": "Iarratais leanúnaí", "column.home": "Baile", "column.lists": "Liostaí", @@ -117,6 +144,9 @@ "community.column_settings.remote_only": "Cian amháin", "compose.language.change": "Athraigh teanga", "compose.language.search": "Cuardaigh teangacha...", + "compose.published.body": "Post foilsithe.", + "compose.published.open": "Oscail", + "compose.saved.body": "Postáil sábháilte.", "compose_form.direct_message_warning_learn_more": "Tuilleadh eolais", "compose_form.encryption_warning": "Ní criptiú taobh-go-taobh déanta ar theachtaireachtaí ar Mhastodon. Ná roinn eolas íogair ar Mhastodon.", "compose_form.hashtag_warning": "Ní áireofar an teachtaireacht seo faoi haischlib ar bith mar níl sí ar fáil don phobal. Ní féidir ach teachtaireachtaí poiblí a chuardach de réir haischlib.", @@ -124,11 +154,19 @@ "compose_form.lock_disclaimer.lock": "faoi ghlas", "compose_form.placeholder": "Cad atá ag tarlú?", "compose_form.poll.duration": "Achar suirbhéanna", + "compose_form.poll.multiple": "Ilrogha", + "compose_form.poll.option_placeholder": "Rogha {number}", + "compose_form.poll.single": "Roghnaigh ceann amháin", "compose_form.poll.switch_to_multiple": "Athraigh suirbhé chun cead a thabhairt do ilrogha", "compose_form.poll.switch_to_single": "Athraigh suirbhé chun cead a thabhairt do rogha amháin", + "compose_form.poll.type": "Stíl", + "compose_form.publish": "Postáil", "compose_form.publish_form": "Foilsigh\n", + "compose_form.reply": "Freagra", + "compose_form.save_changes": "Nuashonrú", "compose_form.spoiler.marked": "Bain rabhadh ábhair", "compose_form.spoiler.unmarked": "Cuir rabhadh ábhair", + "compose_form.spoiler_placeholder": "Rabhadh ábhair (roghnach)", "confirmation_modal.cancel": "Cealaigh", "confirmations.block.confirm": "Bac", "confirmations.cancel_follow_request.confirm": "Éirigh as iarratas", @@ -139,11 +177,15 @@ "confirmations.delete_list.message": "An bhfuil tú cinnte gur mhaith leat an liosta seo a scriosadh go buan?", "confirmations.discard_edit_media.confirm": "Faigh réidh de", "confirmations.discard_edit_media.message": "Tá athruithe neamhshlánaithe don tuarascáil gné nó réamhamharc agat, faigh réidh dóibh ar aon nós?", + "confirmations.domain_block.confirm": "Bloc freastalaí", "confirmations.domain_block.message": "An bhfuil tú iontach cinnte gur mhaith leat bac an t-ainm fearainn {domain} in iomlán? I bhformhór na gcásanna, is leor agus is fearr cúpla baic a cur i bhfeidhm nó cúpla úsáideoirí a balbhú. Ní fheicfidh tú ábhair ón t-ainm fearainn sin in amlíne ar bith, nó i d'fhógraí. Scaoilfear do leantóirí ón ainm fearainn sin.", + "confirmations.edit.confirm": "Eagar", + "confirmations.edit.message": "Má dhéanann tú eagarthóireacht anois, déanfar an teachtaireacht atá á cumadh agat faoi láthair a fhorscríobh. An bhfuil tú cinnte gur mhaith leat leanúint ar aghaidh?", "confirmations.logout.confirm": "Logáil amach", "confirmations.logout.message": "An bhfuil tú cinnte gur mhaith leat logáil amach?", "confirmations.mute.confirm": "Balbhaigh", "confirmations.redraft.confirm": "Scrios ⁊ athdhréachtaigh", + "confirmations.redraft.message": "An bhfuil tú cinnte gur mhaith leat an postáil seo a scriosadh agus é a athdhréachtú? Caillfear ceanáin agus treisithe, agus dílleachtaí freagraí ar an mbunphostála.", "confirmations.reply.confirm": "Freagair", "confirmations.reply.message": "Scriosfaidh freagra láithreach an teachtaireacht atá a chumadh anois agat. An bhfuil tú cinnte gur mhaith leat leanúint leat?", "confirmations.unfollow.confirm": "Ná lean", @@ -152,7 +194,9 @@ "conversation.mark_as_read": "Marcáil mar léite", "conversation.open": "Féach ar comhrá", "conversation.with": "Le {names}", + "copy_icon_button.copied": "Cóipeáladh chuig an ngearrthaisce", "copypaste.copied": "Cóipeáilte", + "copypaste.copy_to_clipboard": "Cóipeáil chuig an ngearrthaisce", "directory.federated": "Ó chomhchruinne aitheanta", "directory.local": "Ó {domain} amháin", "directory.new_arrivals": "Daoine atá tar éis teacht", @@ -162,7 +206,30 @@ "dismissable_banner.community_timeline": "Seo iad na postála is déanaí ó dhaoine le cuntais ar {domain}.", "dismissable_banner.dismiss": "Diúltaigh", "dismissable_banner.explore_links": "Tá na scéalta nuachta seo á phlé anseo agus ar fhreastalaithe eile ar an líonra díláraithe faoi láthair.", + "dismissable_banner.explore_statuses": "Is postálacha iad seo ó ar fud an ghréasáin shóisialta atá ag éirí níos tarraingtí inniu. Rangaítear poist níos nuaí le níos mó teanntáin agus ceanáin níos airde.", "dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.", + "dismissable_banner.public_timeline": "Seo iad na postálacha poiblí is déanaí ó dhaoine ar an ngréasán sóisialta a leanann daoine ar {domain}.", + "domain_block_modal.block": "Bloc freastalaí", + "domain_block_modal.block_account_instead": "Cuir bac ar @{name} ina ionad sin", + "domain_block_modal.they_can_interact_with_old_posts": "Is féidir le daoine ón bhfreastalaí seo idirghníomhú le do sheanphoist.", + "domain_block_modal.they_cant_follow": "Ní féidir le duine ar bith ón bhfreastalaí seo tú a leanúint.", + "domain_block_modal.they_wont_know": "Ní bheidh a fhios acu go bhfuil bac orthu.", + "domain_block_modal.title": "Blocáil fearann?", + "domain_block_modal.you_will_lose_followers": "Bainfear do leantóirí go léir ón bhfreastalaí seo.", + "domain_block_modal.you_wont_see_posts": "Ní fheicfidh tú postálacha nó fógraí ó úsáideoirí ar an bhfreastalaí seo.", + "domain_pill.activitypub_lets_connect": "Ligeann sé duit ceangal agus idirghníomhú le daoine, ní hamháin ar Mastodon, ach thar aipeanna sóisialta éagsúla freisin.", + "domain_pill.activitypub_like_language": "Tá GníomhaíochtPub cosúil leis an teanga a labhraíonn Mastodon le líonraí sóisialta eile.", + "domain_pill.server": "Freastalaí", + "domain_pill.their_handle": "A láimhseáil:", + "domain_pill.their_server": "A dteach digiteach, áit a bhfuil a gcuid post go léir ina gcónaí.", + "domain_pill.their_username": "Aitheantóir uathúil ar a bhfreastalaí. Is féidir teacht ar úsáideoirí leis an ainm úsáideora céanna ar fhreastalaithe éagsúla.", + "domain_pill.username": "Ainm úsáideora", + "domain_pill.whats_in_a_handle": "Cad atá i láimhseáil?", + "domain_pill.who_they_are": "Ós rud é go ndeir láimhseálaithe cé hé duine agus cá bhfuil siad, is féidir leat idirghníomhú le daoine ar fud an ghréasáin shóisialta de .", + "domain_pill.who_you_are": "Toisc go ndeir do láimhseáil cé tú féin agus cén áit a bhfuil tú, is féidir le daoine idirghníomhú leat ar fud an ghréasáin shóisialta de .", + "domain_pill.your_handle": "Do láimhseáil:", + "domain_pill.your_server": "Do theach digiteach, áit a bhfuil do phoist go léir ina gcónaí. Nach maith leat an ceann seo? Aistrigh freastalaithe am ar bith agus tabhair leat do leantóirí freisin.", + "domain_pill.your_username": "D'aitheantóir uathúil ar an bhfreastalaí seo. Is féidir teacht ar úsáideoirí leis an ainm úsáideora céanna ar fhreastalaithe éagsúla.", "embed.instructions": "Embed this status on your website by copying the code below.", "embed.preview": "Seo an chuma a bheidh air:", "emoji_button.activity": "Gníomhaíocht", @@ -180,33 +247,48 @@ "emoji_button.search_results": "Torthaí cuardaigh", "emoji_button.symbols": "Comharthaí", "emoji_button.travel": "Taisteal ⁊ Áiteanna", + "empty_column.account_hides_collections": "Roghnaigh an t-úsáideoir seo gan an fhaisnéis seo a chur ar fáil", "empty_column.account_suspended": "Cuntas ar fionraí", "empty_column.account_timeline": "Níl postálacha ar bith anseo!", "empty_column.account_unavailable": "Níl an phróifíl ar fáil", "empty_column.blocks": "Níl aon úsáideoir bactha agat fós.", "empty_column.bookmarked_statuses": "Níl aon phostáil leabharmharcaithe agat fós. Nuair a dhéanann tú leabharmharc, beidh sé le feiceáil anseo.", "empty_column.community": "Tá an amlíne áitiúil folamh. Foilsigh rud éigin go poiblí le tús a chur le cúrsaí!", + "empty_column.direct": "Níl aon tagairtí príobháideacha agat fós. Nuair a sheolann tú nó a gheobhaidh tú ceann, beidh sé le feiceáil anseo.", "empty_column.domain_blocks": "Níl aon fearainn bhactha ann go fóill.", "empty_column.explore_statuses": "Níl rud ar bith ag treochtáil faoi láthair. Tar ar ais ar ball!", + "empty_column.favourited_statuses": "Níl aon postálacha is fearr leat fós. Nuair is fearr leat ceann, beidh sé le feiceáil anseo.", + "empty_column.favourites": "Níor thaitin an post seo le haon duine go fóill. Nuair a dhéanann duine, taispeánfaidh siad suas anseo.", "empty_column.follow_requests": "Níl aon phostáil leabharmharcaithe agat fós. Nuair a dhéanann tú leabharmharc, feicfear anseo é.", + "empty_column.followed_tags": "Níor lean tú aon hashtags fós. Nuair a dhéanann tú, beidh siad a thaispeáint suas anseo.", "empty_column.hashtag": "Níl rud ar bith faoin haischlib seo go fóill.", "empty_column.home": "Tá d'amlíne baile folamh! B'fhiú duit cúpla duine eile a leanúint lena líonadh! {suggestions}", "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.", "empty_column.lists": "Níl aon liostaí fós agat. Nuair a chruthaíonn tú ceann, feicfear anseo é.", "empty_column.mutes": "Níl aon úsáideoir balbhaithe agat fós.", + "empty_column.notification_requests": "Gach soiléir! Níl aon rud anseo. Nuair a gheobhaidh tú fógraí nua, beidh siad le feiceáil anseo de réir do shocruithe.", "empty_column.notifications": "Níl aon fógraí agat fós. Nuair a dhéanann daoine eile idirghníomhú leat, feicfear anseo é.", "empty_column.public": "Faic anseo! Scríobh rud éigin go poiblí, nó lean úsáideoirí ar fhreastalaithe eile chun é a líonadh", "error.unexpected_crash.explanation": "De bharr fabht inár gcód, nó fadhb le chomhoiriúnacht brabhsálaí, níorbh fhéadfadh an leathanach seo a léiriú i gceart.", "error.unexpected_crash.explanation_addons": "Ní taispeántar an leathanach seo mar is ceart. Is dócha go gcruthaíonn breiseán brabhsálaí nó uirlisí uathaistriúcháin an fhadhb seo.", + "error.unexpected_crash.next_steps": "Bain triail as an leathanach a athnuachan. Mura gcabhraíonn sé sin, seans go mbeidh tú fós in ann Mastodon a úsáid trí bhrabhsálaí nó aip dhúchais eile.", + "error.unexpected_crash.next_steps_addons": "Déan iarracht iad a dhíchumasú agus an leathanach a athnuachan. Mura gcabhraíonn sé sin, seans go mbeidh tú fós in ann Mastodon a úsáid trí bhrabhsálaí nó aip dhúchais eile.", + "errors.unexpected_crash.copy_stacktrace": "Cóipeáil rian cruachta go dtí an ghearrthaisce", "errors.unexpected_crash.report_issue": "Tuairiscigh deacracht", "explore.search_results": "Torthaí cuardaigh", + "explore.suggested_follows": "Daoine", "explore.title": "Féach thart", "explore.trending_links": "Nuacht", "explore.trending_statuses": "Postálacha", "explore.trending_tags": "Haischlibeanna", + "filter_modal.added.context_mismatch_explanation": "Ní bhaineann an chatagóir scagaire seo leis an gcomhthéacs ina bhfuair tú rochtain ar an bpostáil seo. Más mian leat an postáil a scagadh sa chomhthéacs seo freisin, beidh ort an scagaire a chur in eagar.", + "filter_modal.added.context_mismatch_title": "Neamhréir comhthéacs!", + "filter_modal.added.expired_explanation": "Tá an chatagóir scagaire seo imithe in éag, beidh ort an dáta éaga a athrú chun é a chur i bhfeidhm.", "filter_modal.added.expired_title": "Scagaire as feidhm!", + "filter_modal.added.review_and_configure": "Chun an chatagóir scagaire seo a athbhreithniú agus a chumrú tuilleadh, téigh chuig {settings_link}.", "filter_modal.added.review_and_configure_title": "Socruithe scagtha", "filter_modal.added.settings_link": "leathan socruithe", + "filter_modal.added.short_explanation": "Cuireadh an postáil seo leis an gcatagóir scagaire seo a leanas: {title}.", "filter_modal.added.title": "Scagaire curtha leis!", "filter_modal.select_filter.context_mismatch": "ní bhaineann sé leis an gcomhthéacs seo", "filter_modal.select_filter.expired": "as feidhm", @@ -215,14 +297,31 @@ "filter_modal.select_filter.subtitle": "Bain úsáid as catagóir reatha nó cruthaigh ceann nua", "filter_modal.select_filter.title": "Déan scagadh ar an bpostáil seo", "filter_modal.title.status": "Déan scagadh ar phostáil", + "filtered_notifications_banner.mentions": "{count, plural, one {tagairt} other {tagairtí}}", + "filtered_notifications_banner.pending_requests": "Fógraí ó {count, plural, =0 {níl éinne} one {duine amháin} two {# daoine} few {# daoine} many {# daoine} other {# daoine}} b'fhéidir go mbeadh a fhios agat", + "filtered_notifications_banner.title": "Fógraí scagtha", + "firehose.all": "Gach", + "firehose.local": "An freastalaí seo", + "firehose.remote": "Freastalaithe eile", "follow_request.authorize": "Ceadaigh", "follow_request.reject": "Diúltaigh", "follow_requests.unlocked_explanation": "Cé nach bhfuil do chuntas faoi ghlas, cheap foireann {domain} gur mhaith leat súil siar ar iarratais leanúnaí as na cuntais seo.", + "follow_suggestions.curated_suggestion": "Pioc foirne", "follow_suggestions.dismiss": "Ná taispeáin arís", + "follow_suggestions.featured_longer": "Lámh-roghnaithe ag an bhfoireann {domain}", + "follow_suggestions.friends_of_friends_longer": "Coitianta i measc na ndaoine a leanann tú", + "follow_suggestions.hints.featured": "Tá an phróifíl seo roghnaithe de láimh ag foireann {domain}.", + "follow_suggestions.hints.friends_of_friends": "Tá an-tóir ar an bpróifíl seo i measc na ndaoine a leanann tú.", + "follow_suggestions.hints.most_followed": "Tá an phróifíl seo ar cheann de na cinn is mó a leantar ar {domain}.", + "follow_suggestions.hints.most_interactions": "Tá an phróifíl seo ag tarraingt go leor airde ar {domain} le déanaí.", + "follow_suggestions.hints.similar_to_recently_followed": "Tá an phróifíl seo cosúil leis na próifílí a lean tú le déanaí.", "follow_suggestions.personalized_suggestion": "Nod pearsantaithe", "follow_suggestions.popular_suggestion": "Nod coiteann", + "follow_suggestions.popular_suggestion_longer": "Tá an-tóir ar {domain}", + "follow_suggestions.similar_to_recently_followed_longer": "Cosúil le próifílí a lean tú le déanaí", "follow_suggestions.view_all": "Féach uile", "follow_suggestions.who_to_follow": "Cé le leanúint", + "followed_tags": "Hashtags le leanúint", "footer.about": "Maidir le", "footer.directory": "Eolaire próifílí", "footer.get_app": "Faigh an aip", @@ -230,6 +329,7 @@ "footer.keyboard_shortcuts": "Aicearraí méarchláir", "footer.privacy_policy": "Polasaí príobháideachais", "footer.source_code": "Féach ar an gcód foinseach", + "footer.status": "Stádas", "generic.saved": "Sábháilte", "getting_started.heading": "Ag tosú amach", "hashtag.column_header.tag_mode.all": "agus {additional}", @@ -238,15 +338,34 @@ "hashtag.column_settings.select.no_options_message": "Moltaí ar bith faighte", "hashtag.column_settings.select.placeholder": "Iontráil haischlibeanna…", "hashtag.column_settings.tag_mode.all": "Iad seo go léir", + "hashtag.column_settings.tag_mode.any": "Aon cheann díobh seo", + "hashtag.column_settings.tag_mode.none": "Níl aon cheann díobh seo", "hashtag.column_settings.tag_toggle": "Include additional tags in this column", + "hashtag.counter_by_accounts": "{count, plural, one {{counter} rannpháirtí} two {{counter} rannpháirtí} few {{counter} rannpháirtí} many {{counter} rannpháirtí} other {{counter} rannpháirtí}}", + "hashtag.counter_by_uses": "{count, plural, one {{counter} post} two {{counter} post} few {{counter} post} many {{counter} post} other {{counter} poist}}", + "hashtag.counter_by_uses_today": "{count, plural, one {{counter} post inniu} other {{counter} poist inniu}} inniu", "hashtag.follow": "Lean haischlib", "hashtag.unfollow": "Ná lean haischlib", + "hashtags.and_other": "agus {count, plural, one {} two {# níos} few {# níos} many {# níos} other {# níos}}", "home.column_settings.show_reblogs": "Taispeáin moltaí", "home.column_settings.show_replies": "Taispeán freagraí", "home.hide_announcements": "Cuir fógraí i bhfolach", + "home.pending_critical_update.body": "Nuashonraigh do fhreastalaí Mastodon chomh luath agus is féidir!", + "home.pending_critical_update.link": "Féach nuashonruithe", + "home.pending_critical_update.title": "Nuashonrú slándála ríthábhachtach ar fáil!", "home.show_announcements": "Taispeáin fógraí", + "interaction_modal.description.favourite": "Le cuntas ar Mastodon, is fearr leat an postáil seo chun a chur in iúl don údar go bhfuil meas agat air agus é a shábháil ar feadh níos déanaí.", + "interaction_modal.description.follow": "Le cuntas ar Mastodon, is féidir leat {name} a leanúint chun a gcuid postálacha a fháil i do fhotha baile.", + "interaction_modal.description.reblog": "Le cuntas ar Mastodon, is féidir leat an postáil seo a threisiú chun é a roinnt le do leantóirí féin.", + "interaction_modal.description.reply": "Le cuntas ar Mastodon, is féidir leat freagra a thabhairt ar an bpostáil seo.", + "interaction_modal.login.action": "Thabhairt dom abhaile", + "interaction_modal.login.prompt": "Fearann ​​do fhreastalaí baile, e.g. mastodon.sóisialta", + "interaction_modal.no_account_yet": "Ní ar Mastodon?", "interaction_modal.on_another_server": "Ar freastalaí eile", "interaction_modal.on_this_server": "Ar an freastalaí seo", + "interaction_modal.sign_in": "Níl tú logáilte isteach ar an bhfreastalaí seo. Cá bhfuil do chuntas á óstáil?", + "interaction_modal.sign_in_hint": "Leid: Sin é an suíomh Gréasáin inar chláraigh tú. Mura cuimhin leat, lorg an ríomhphost fáilte i do bhosca isteach. Is féidir leat d'ainm úsáideora iomlán a chur isteach freisin! (m.sh. @Mastodon@mastodon.social)", + "interaction_modal.title.favourite": "An postáil {name} is fearr leat", "interaction_modal.title.follow": "Lean {name}", "interaction_modal.title.reblog": "Mol postáil de chuid {name}", "interaction_modal.title.reply": "Freagair postáil {name}", @@ -262,6 +381,8 @@ "keyboard_shortcuts.direct": "to open direct messages column", "keyboard_shortcuts.down": "Bog síos ar an liosta", "keyboard_shortcuts.enter": "Oscail postáil", + "keyboard_shortcuts.favourite": "Postáil is fearr leat", + "keyboard_shortcuts.favourites": "Oscail liosta ceanáin", "keyboard_shortcuts.federated": "Oscail amlíne cónaidhmithe", "keyboard_shortcuts.heading": "Aicearraí méarchláir", "keyboard_shortcuts.home": "Oscail amlíne bhaile", @@ -286,15 +407,21 @@ "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "Bog suas ar an liosta", "lightbox.close": "Dún", + "lightbox.compress": "Comhbhrúigh an bosca amhairc íomhá", + "lightbox.expand": "Leathnaigh an bosca amhairc íomhá", "lightbox.next": "An céad eile", "lightbox.previous": "Roimhe seo", "limited_account_hint.action": "Taispeáin an phróifíl ar aon nós", "limited_account_hint.title": "Tá an phróifíl seo curtha i bhfolach ag na modhnóra {domain}.", + "link_preview.author": "Le {name}", + "link_preview.more_from_author": "Tuilleadh ó {name}", + "link_preview.shares": "{count, plural, one {{counter} post} other {{counter} poist}}", "lists.account.add": "Cuir leis an liosta", "lists.account.remove": "Scrios as an liosta", "lists.delete": "Scrios liosta", "lists.edit": "Cuir an liosta in eagar", "lists.edit.submit": "Athraigh teideal", + "lists.exclusive": "Folaigh na poist seo ón mbaile", "lists.new.create": "Cruthaigh liosta", "lists.new.title_placeholder": "Teideal liosta nua", "lists.replies_policy.followed": "Úsáideoir ar bith atá á leanúint", @@ -303,20 +430,38 @@ "lists.replies_policy.title": "Taispeáin freagraí:", "lists.search": "Cuardaigh i measc daoine atá á leanúint agat", "lists.subheading": "Do liostaí", + "load_pending": "{count, plural, one {# mír nua} two {# mír nua} few {# mír nua} many {# mír nua} other {# mír nua}}", + "loading_indicator.label": "Á lódáil…", + "media_gallery.toggle_visible": "{number, plural, one {Folaigh íomhá} two {Folaigh íomhánna} few {Folaigh íomhánna} many {Folaigh íomhánna} other {Folaigh íomhánna}}", + "moved_to_account_banner.text": "Tá do chuntas {disabledAccount} díchumasaithe faoi láthair toisc gur bhog tú go {movedToAccount}.", + "mute_modal.hide_from_notifications": "Folaigh ó fhógraí", + "mute_modal.hide_options": "Folaigh roghanna", + "mute_modal.indefinite": "Go dtí go ndíbhalfaidh mé iad", + "mute_modal.show_options": "Taispeáin roghanna", + "mute_modal.they_can_mention_and_follow": "Is féidir leo tú a lua agus a leanúint, ach ní fheicfidh tú iad.", + "mute_modal.they_wont_know": "Ní bheidh a fhios acu go bhfuil balbhú orthu.", + "mute_modal.title": "An bhfuil fonn ort úsáideoir a bhalbhú?", + "mute_modal.you_wont_see_mentions": "Ní fheicfidh tú postálacha a luann iad.", + "mute_modal.you_wont_see_posts": "Is féidir leo do phoist a fheiceáil go fóill, ach ní fheicfidh tú a gcuid postanna.", "navigation_bar.about": "Maidir le", + "navigation_bar.advanced_interface": "Oscail i gcomhéadan gréasáin chun cinn", "navigation_bar.blocks": "Cuntais bhactha", "navigation_bar.bookmarks": "Leabharmharcanna", "navigation_bar.community_timeline": "Amlíne áitiúil", "navigation_bar.compose": "Cum postáil nua", + "navigation_bar.direct": "Luann príobháideach", "navigation_bar.discover": "Faigh amach", "navigation_bar.domain_blocks": "Fearainn bhactha", "navigation_bar.explore": "Féach thart", + "navigation_bar.favourites": "Ceanáin", "navigation_bar.filters": "Focail bhalbhaithe", "navigation_bar.follow_requests": "Iarratais leanúnaí", + "navigation_bar.followed_tags": "Haischlibeanna ina dhiaidh", "navigation_bar.follows_and_followers": "Ag leanúint agus do do leanúint", "navigation_bar.lists": "Liostaí", "navigation_bar.logout": "Logáil Amach", "navigation_bar.mutes": "Úsáideoirí balbhaithe", + "navigation_bar.opened_in_classic_interface": "Osclaítear poist, cuntais agus leathanaigh shonracha eile de réir réamhshocraithe sa chomhéadan gréasáin clasaiceach.", "navigation_bar.personal": "Pearsanta", "navigation_bar.pins": "Postálacha pionnáilte", "navigation_bar.preferences": "Sainroghanna pearsanta", @@ -326,17 +471,41 @@ "not_signed_in_indicator.not_signed_in": "You need to sign in to access this resource.", "notification.admin.report": "Tuairiscigh {name} {target}", "notification.admin.sign_up": "Chláraigh {name}", + "notification.favourite": "Is fearr le {name} do phostáil", "notification.follow": "Lean {name} thú", "notification.follow_request": "D'iarr {name} ort do chuntas a leanúint", "notification.mention": "Luaigh {name} tú", + "notification.moderation-warning.learn_more": "Foghlaim níos mó", + "notification.moderation_warning": "Tá rabhadh modhnóireachta faighte agat", + "notification.moderation_warning.action_delete_statuses": "Baineadh cuid de do phostálacha.", + "notification.moderation_warning.action_disable": "Díchumasaíodh do chuntas.", + "notification.moderation_warning.action_mark_statuses_as_sensitive": "Tá cuid de do phostálacha marcáilte mar íogair.", + "notification.moderation_warning.action_none": "Tá rabhadh modhnóireachta faighte ag do chuntas.", + "notification.moderation_warning.action_sensitive": "Déanfar do phostálacha a mharcáil mar íogair as seo amach.", + "notification.moderation_warning.action_silence": "Tá do chuntas teoranta.", + "notification.moderation_warning.action_suspend": "Cuireadh do chuntas ar fionraí.", "notification.own_poll": "Tá do suirbhé críochnaithe", "notification.poll": "Tá suirbhé inar vótáil tú tar éis críochnú", "notification.reblog": "Mhol {name} do phostáil", + "notification.relationships_severance_event": "Cailleadh naisc le {name}", + "notification.relationships_severance_event.account_suspension": "Chuir riarthóir ó {from} {target} ar fionraí, rud a chiallaíonn nach féidir leat nuashonruithe a fháil uathu a thuilleadh ná idirghníomhú leo.", + "notification.relationships_severance_event.domain_block": "Chuir riarthóir ó {from} bac ar {target}, lena n-áirítear {followersCount} de do leantóirí agus {followingCount, plural, one {#cuntas} other {#cuntas}} leanann tú.", + "notification.relationships_severance_event.learn_more": "Foghlaim níos mó", + "notification.relationships_severance_event.user_domain_block": "Chuir tú bac ar {target}, bhain tú {followersCount} de do leantóirí agus {followingCount, plural, one {# cuntas} other {# cuntais}} a leanann tú.", "notification.status": "Phostáil {name} díreach", "notification.update": "Chuir {name} postáil in eagar", + "notification_requests.accept": "Glac", + "notification_requests.dismiss": "Díbhe", + "notification_requests.notifications_from": "Fógraí ó {name}", + "notification_requests.title": "Fógraí scagtha", "notifications.clear": "Glan fógraí", + "notifications.clear_confirmation": "An bhfuil tú cinnte gur mhaith leat d'fhógraí go léir a ghlanadh go buan?", "notifications.column_settings.admin.report": "Tuairiscí nua:", + "notifications.column_settings.admin.sign_up": "Clárúcháin nua:", "notifications.column_settings.alert": "Fógraí deisce", + "notifications.column_settings.favourite": "Ceanáin:", + "notifications.column_settings.filter_bar.advanced": "Taispeáin gach catagóir", + "notifications.column_settings.filter_bar.category": "Barra scagairí tapa", "notifications.column_settings.follow": "Leantóirí nua:", "notifications.column_settings.follow_request": "Iarratais leanúnaí nua:", "notifications.column_settings.mention": "Tráchtanna:", @@ -347,33 +516,77 @@ "notifications.column_settings.sound": "Seinn an fhuaim", "notifications.column_settings.status": "Postálacha nua:", "notifications.column_settings.unread_notifications.category": "Brúfhógraí neamhléite", + "notifications.column_settings.unread_notifications.highlight": "Aibhsigh fógraí neamhléite", "notifications.column_settings.update": "Eagair:", "notifications.filter.all": "Uile", "notifications.filter.boosts": "Treisithe", + "notifications.filter.favourites": "Ceanáin", "notifications.filter.follows": "Ag leanúint", "notifications.filter.mentions": "Tráchtanna", "notifications.filter.polls": "Torthaí suirbhéanna", "notifications.filter.statuses": "Nuashonruithe ó dhaoine a leanann tú", "notifications.grant_permission": "Tabhair cead.", "notifications.group": "{count} fógraí", + "notifications.mark_as_read": "Marcáil gach fógra mar atá léite", + "notifications.permission_denied": "Níl fógraí deisce ar fáil mar gheall ar iarratas ar chead brabhsálaí a diúltaíodh roimhe seo", + "notifications.permission_denied_alert": "Ní féidir fógraí deisce a chumasú, mar gur diúltaíodh cead brabhsálaí roimhe seo", + "notifications.permission_required": "Níl fógraí deisce ar fáil toisc nár tugadh an cead riachtanach.", + "notifications.policy.filter_new_accounts.hint": "Cruthaithe laistigh den {days, plural, one {lae amháin} two {# lá} few {# lá} many {# lá} other {# lá}}", + "notifications.policy.filter_new_accounts_title": "Cuntais nua", + "notifications.policy.filter_not_followers_hint": "Agus daoine a bhfuil siad ag leanúint ort le níos lú ná {days, plural, one {lae amháin} two {# lá} few {# lá} many {# lá} other {# lá}}", + "notifications.policy.filter_not_followers_title": "Daoine nach leanann tú", + "notifications.policy.filter_not_following_hint": "Go dtí go gceadaíonn tú iad de láimh", + "notifications.policy.filter_not_following_title": "Daoine nach leanann tú", + "notifications.policy.filter_private_mentions_hint": "Scagtha mura bhfuil sé mar fhreagra ar do lua féin nó má leanann tú an seoltóir", + "notifications.policy.filter_private_mentions_title": "Tagairtí príobháideacha gan iarraidh", + "notifications.policy.title": "Scag fógraí ó…", "notifications_permission_banner.enable": "Ceadaigh fógraí ar an deasc", + "notifications_permission_banner.how_to_control": "Chun fógraí a fháil nuair nach bhfuil Mastodon oscailte, cumasaigh fógraí deisce. Is féidir leat a rialú go beacht cé na cineálacha idirghníomhaíochtaí a ghineann fógraí deisce tríd an gcnaipe {icon} thuas nuair a bhíonn siad cumasaithe.", "notifications_permission_banner.title": "Ná caill aon rud go deo", + "onboarding.action.back": "Tóg ar ais mé", + "onboarding.actions.back": "Tóg ar ais mé", "onboarding.actions.go_to_explore": "See what's trending", "onboarding.actions.go_to_home": "Go to your home feed", + "onboarding.compose.template": "Dia duit #Mastodon!", + "onboarding.follows.empty": "Ar an drochuair, ní féidir aon torthaí a thaispeáint faoi láthair. Is féidir leat triail a bhaint as cuardach nó brabhsáil ar an leathanach taiscéalaíochta chun teacht ar dhaoine le leanúint, nó bain triail eile as níos déanaí.", "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": "Déan mo phróifíl a fháil amach", + "onboarding.profile.discoverable_hint": "Nuair a roghnaíonn tú infhionnachtana ar Mastodon, d’fhéadfadh do phoist a bheith le feiceáil i dtorthaí cuardaigh agus treochtaí, agus d’fhéadfaí do phróifíl a mholadh do dhaoine a bhfuil na leasanna céanna acu leat.", + "onboarding.profile.display_name": "Ainm taispeána", + "onboarding.profile.display_name_hint": "D’ainm iomlán nó d’ainm spraíúil…", + "onboarding.profile.lead": "Is féidir leat é seo a chomhlánú i gcónaí níos déanaí sna socruithe, áit a bhfuil níos mó roghanna saincheaptha ar fáil.", + "onboarding.profile.note": "Bith", + "onboarding.profile.note_hint": "Is féidir leat @ daoine eile a lua nó #hashtags…", + "onboarding.profile.save_and_continue": "Sábháil agus lean ar aghaidh", + "onboarding.profile.title": "Socrú próifíle", + "onboarding.profile.upload_avatar": "Íosluchtaigh pictiúr próifíl", + "onboarding.profile.upload_header": "Íoslódáil an ceanntásca próifíl", + "onboarding.share.lead": "Cuir in iúl do dhaoine conas is féidir leo tú a aimsiú ar Mastodon!", + "onboarding.share.message": "Is {username} mé ar #Mastodon! Tar lean mé ag {url}", + "onboarding.share.next_steps": "Na chéad chéimeanna eile is féidir:", + "onboarding.share.title": "Roinn do phróifíl", "onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:", "onboarding.start.skip": "Want to skip right ahead?", + "onboarding.start.title": "Tá sé déanta agat!", "onboarding.steps.follow_people.body": "You curate your own feed. Lets fill it with interesting people.", "onboarding.steps.follow_people.title": "Follow {count, plural, one {one person} other {# people}}", "onboarding.steps.publish_status.body": "Say hello to the world.", + "onboarding.steps.publish_status.title": "Déan do chéad phostáil", "onboarding.steps.setup_profile.body": "Others are more likely to interact with you with a filled out profile.", "onboarding.steps.setup_profile.title": "Customize your profile", "onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!", "onboarding.steps.share_profile.title": "Share your profile", + "onboarding.tips.2fa": "An raibh a fhios agat? Is féidir leat do chuntas a dhéanamh slán trí fhíordheimhniú dhá fhachtóir a shocrú i socruithe do chuntais. Oibríonn sé le haon aip TOTP de do rogha féin, níl aon uimhir theileafóin riachtanach!", + "onboarding.tips.accounts_from_other_servers": "An raibh a fhios agat? Ós rud é go bhfuil Mastodon díláraithe, déanfar roinnt próifílí a dtagann tú trasna orthu a óstáil ar fhreastalaithe seachas do fhreastalaithe. Agus fós is féidir leat idirghníomhú leo gan uaim! Tá an freastalaí acu sa dara leath dá n-ainm úsáideora!", + "onboarding.tips.migration": "An raibh a fhios agat? Más dóigh leat nach rogha freastalaí iontach é {domain} amach anseo, is féidir leat bogadh go freastalaí Mastodon eile gan do leantóirí a chailliúint. Is féidir leat do fhreastalaí féin a óstáil fiú!", + "onboarding.tips.verification": "An raibh a fhios agat? Is féidir leat do chuntas a fhíorú trí nasc a chur le do phróifíl Mastodon ar do shuíomh Gréasáin féin agus an suíomh Gréasáin a chur le do phróifíl. Níl aon táillí nó doiciméid riachtanach!", + "password_confirmation.exceeds_maxlength": "Sáraíonn dearbhú pasfhocail uasfhad an phasfhocail", + "password_confirmation.mismatching": "Ní hionann dearbhú pasfhocail", "picture_in_picture.restore": "Cuir é ar ais", "poll.closed": "Dúnta", "poll.refresh": "Athnuaigh", + "poll.reveal": "Féach torthaí", "poll.total_people": "{count, plural, one {# duine} other {# duine}}", "poll.total_votes": "{count, plural, one {# vóta} other {# vóta}}", "poll.vote": "Vótáil", @@ -382,10 +595,21 @@ "poll_button.add_poll": "Cruthaigh suirbhé", "poll_button.remove_poll": "Bain suirbhé", "privacy.change": "Adjust status privacy", + "privacy.direct.long": "Luaigh gach duine sa phost", + "privacy.direct.short": "Daoine ar leith", + "privacy.private.long": "Do leanúna amháin", + "privacy.private.short": "Leantóirí", + "privacy.public.long": "Duine ar bith ar agus amach Mastodon", "privacy.public.short": "Poiblí", + "privacy.unlisted.additional": "Iompraíonn sé seo díreach mar a bheadh ​​poiblí, ach amháin ní bheidh an postáil le feiceáil i bhfothaí beo nó i hashtags, in iniúchadh nó i gcuardach Mastodon, fiú má tá tú liostáilte ar fud an chuntais.", + "privacy.unlisted.long": "Níos lú fanfarraí algarthacha", + "privacy.unlisted.short": "Poiblí ciúin", + "privacy_policy.last_updated": "Nuashonraithe {date}", "privacy_policy.title": "Polasaí príobháideachais", + "recommended": "Molta", "refresh": "Athnuaigh", "regeneration_indicator.label": "Ag lódáil…", + "regeneration_indicator.sublabel": "Tá do bheathú baile á ullmhú!", "relative_time.days": "{number}l", "relative_time.full.days": "{number, plural, one {# lá} other {# lá}} ó shin", "relative_time.full.hours": "{number, plural, one {# uair} other {# uair}} ó shin", @@ -397,47 +621,90 @@ "relative_time.minutes": "{number}n", "relative_time.seconds": "{number}s", "relative_time.today": "inniu", + "reply_indicator.attachments": "{count, plural, one {# alán} two {# aláin} few {# aláin} many {# aláin} other {# aláin}}", "reply_indicator.cancel": "Cealaigh", + "reply_indicator.poll": "Vótaíocht", "report.block": "Bac", "report.block_explanation": "Ní fheicfidh tú a c(h)úid postálacha. Ní bheidh sé/sí in ann do chuid postálacha a fheiceáil ná tú féin a leanúint. Beidh sé/sí in ann a dhéanamh amach go bhfuil sé/sí bactha.", + "report.categories.legal": "Dlíthiúil", "report.categories.other": "Eile", "report.categories.spam": "Turscar", "report.categories.violation": "Sáraíonn ábhar riail freastalaí amháin nó níos mó", "report.category.subtitle": "Roghnaigh an toradh is fearr", + "report.category.title": "Inis dúinn cad atá ar siúl leis an {type} seo", "report.category.title_account": "próifíl", "report.category.title_status": "postáil", "report.close": "Déanta", + "report.comment.title": "An bhfuil aon rud eile ba chóir dúinn a fhios agat, dar leat?", "report.forward": "Seol ar aghaidh chun {target}", + "report.forward_hint": "Is ó fhreastalaí eile an cuntas. Cuir cóip gan ainm den tuarascáil ansin freisin?", "report.mute": "Balbhaigh", "report.mute_explanation": "Ní fheicfidh tú a postálacha. Is féidir an té seo tú a leanúint agus do phostálacha a fheiceáil, agus ní fhios go bhfuil iad balbhaithe.", "report.next": "An céad eile", "report.placeholder": "Ráitis bhreise", "report.reasons.dislike": "Ní maith liom é", + "report.reasons.dislike_description": "Ní rud é ba mhaith leat a fheiceáil", + "report.reasons.legal": "Tá sé mídhleathach", + "report.reasons.legal_description": "Creideann tú go sáraíonn sé dlí do thíre nó do thír an fhreastalaí", "report.reasons.other": "Is rud eile é", + "report.reasons.other_description": "Ní luíonn an cheist le catagóirí eile", "report.reasons.spam": "Is turscar é", + "report.reasons.spam_description": "Naisc mhailíseacha, rannpháirtíocht bhréige, nó freagraí athchleachtach", "report.reasons.violation": "Sáraíonn sé rialacha an fhreastalaí", "report.reasons.violation_description": "Tá a fhios agat go sáraíonn sé rialacha ar leith", "report.rules.subtitle": "Roghnaigh gach atá i bhfeidhm", "report.rules.title": "Cén rialacha atá á sárú?", "report.statuses.subtitle": "Roghnaigh gach atá i bhfeidhm", + "report.statuses.title": "An bhfuil aon phoist a thacaíonn leis an tuarascáil seo?", "report.submit": "Cuir isteach", "report.target": "Ag tuairisciú {target}", + "report.thanks.take_action": "Seo do roghanna chun an méid a fheiceann tú ar Mastodon a rialú:", + "report.thanks.take_action_actionable": "Agus é seo á athbhreithniú againn, is féidir leat caingean a dhéanamh in aghaidh @{name}:", + "report.thanks.title": "Ná bí ag iarraidh é seo a fheiceáil?", + "report.thanks.title_actionable": "Go raibh maith agat as tuairisc a thabhairt, scrúdóimid é seo.", "report.unfollow": "Ná lean @{name}", + "report.unfollow_explanation": "Tá tú ag leanúint an chuntais seo. Chun nach bhfeicfidh tú a bpoist i do fhotha baile a thuilleadh, dílean ​​iad.", "report_notification.attached_statuses": "{count, plural, one {# post} other {# posts}} attached", + "report_notification.categories.legal": "Dlíthiúil", "report_notification.categories.other": "Eile", "report_notification.categories.spam": "Turscar", "report_notification.categories.violation": "Sárú rialach", "report_notification.open": "Oscail tuairisc", + "search.no_recent_searches": "Níl aon chuardach le déanaí", "search.placeholder": "Cuardaigh", + "search.quick_action.account_search": "Próifílí a mheaitseálann {x}", + "search.quick_action.go_to_account": "Téigh go próifíl {x}", + "search.quick_action.go_to_hashtag": "Téigh chuig hashtag {x}", + "search.quick_action.open_url": "Oscail URL i Mastodon", + "search.quick_action.status_search": "Postálacha a mheaitseálann {x}", "search.search_or_paste": "Cuardaigh nó cuir URL isteach", + "search_popout.full_text_search_disabled_message": "Níl sé ar fáil ar {domain}.", + "search_popout.full_text_search_logged_out_message": "Ar fáil ach amháin nuair atá tú logáilte isteach.", + "search_popout.language_code": "Cód teanga ISO", + "search_popout.options": "Roghanna cuardaigh", + "search_popout.quick_actions": "Gníomhartha tapa", + "search_popout.recent": "Cuardach le déanaí", + "search_popout.specific_date": "dáta ar leith", + "search_popout.user": "úsáideoir", + "search_results.accounts": "Próifílí", "search_results.all": "Uile", "search_results.hashtags": "Haischlibeanna", + "search_results.nothing_found": "Níorbh fhéidir aon rud a aimsiú do na téarmaí cuardaigh seo", + "search_results.see_all": "Gach rud a fheicáil", "search_results.statuses": "Postálacha", "search_results.title": "Cuardaigh ar thóir {q}", + "server_banner.about_active_users": "Daoine a úsáideann an freastalaí seo le 30 lá anuas (Úsáideoirí Gníomhacha Míosúla)", "server_banner.active_users": "úsáideoirí gníomhacha", + "server_banner.administered_by": "Arna riar ag:", + "server_banner.is_one_of_many": "Tá {domain} ar cheann de na freastalaithe Mastodon neamhspleácha go leor is féidir leat a úsáid chun páirt a ghlacadh sa feiliúnach.", "server_banner.server_stats": "Staitisticí freastalaí:", "sign_in_banner.create_account": "Cruthaigh cuntas", + "sign_in_banner.follow_anyone": "Lean aon duine ar fud an fhealsúnachta agus féach ar fad in ord croineolaíoch. Gan algartam, fógraí, nó clickbait i radharc.", + "sign_in_banner.mastodon_is": "Is é Mastodon an bealach is fearr le coinneáil suas lena bhfuil ag tarlú.", "sign_in_banner.sign_in": "Sinigh isteach", + "sign_in_banner.sso_redirect": "Logáil isteach nó Cláraigh", + "status.admin_account": "Oscail comhéadan modhnóireachta do @{name}", + "status.admin_domain": "Oscail comhéadan modhnóireachta le haghaidh {domain}", "status.admin_status": "Open this status in the moderation interface", "status.block": "Bac @{name}", "status.bookmark": "Leabharmharcanna", @@ -445,15 +712,23 @@ "status.cannot_reblog": "Ní féidir an phostáil seo a mholadh", "status.copy": "Copy link to status", "status.delete": "Scrios", + "status.detailed_status": "Amharc comhrá mionsonraithe", + "status.direct": "Luaigh @{name} go príobháideach", + "status.direct_indicator": "Lua príobháideach", "status.edit": "Cuir in eagar", + "status.edited": "Arna chuir in eagar anuas {date}", "status.edited_x_times": "Curtha in eagar {count, plural, one {{count} uair amháin} two {{count} uair} few {{count} uair} many {{count} uair} other {{count} uair}}", "status.embed": "Leabaigh", + "status.favourite": "Is fearr leat", + "status.favourites": "{count, plural, one {a bhfuil grá agat do} two {gráite} few {gráite} many {gráite} other {gráite}}", "status.filter": "Déan scagadh ar an bpostáil seo", "status.filtered": "Scagtha", "status.hide": "Cuir postáil i bhfolach", "status.history.created": "Chruthaigh {name} {date}", "status.history.edited": "Curtha in eagar ag {name} in {date}", "status.load_more": "Lódáil a thuilleadh", + "status.media.open": "Cliceáil chun oscailt", + "status.media.show": "Cliceáil chun a thaispeáint", "status.media_hidden": "Cuirtear meáin i bhfolach", "status.mention": "Luaigh @{name}", "status.more": "Tuilleadh", @@ -466,8 +741,11 @@ "status.reblog": "Mol", "status.reblog_private": "Mol le léargas bunúsach", "status.reblogged_by": "Mhol {name}", + "status.reblogs": "{count, plural, one {buaic} other {buaic}}", "status.reblogs.empty": "Níor mhol éinne an phostáil seo fós. Nuair a mholfaidh duine éigin í, taispeánfar anseo é sin.", "status.redraft": "Scrios ⁊ athdhréachtaigh", + "status.remove_bookmark": "Bain leabharmharc", + "status.replied_to": "D'fhreagair {name}", "status.reply": "Freagair", "status.replyAll": "Freagair le snáithe", "status.report": "Tuairiscigh @{name}", @@ -482,38 +760,54 @@ "status.title.with_attachments": "{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}", "status.translate": "Aistrigh", "status.translated_from_with": "D'Aistrigh ón {lang} ag úsáid {provider}", + "status.uncached_media_warning": "Níl an réamhamharc ar fáil", "status.unmute_conversation": "Díbhalbhaigh comhrá", "status.unpin": "Díphionnáil de do phróifíl", + "subscribed_languages.lead": "Ní bheidh ach postálacha i dteangacha roghnaithe le feiceáil ar do bhaile agus liostaí amlínte tar éis an athraithe. Roghnaigh ceann ar bith chun postálacha a fháil i ngach teanga.", "subscribed_languages.save": "Sábháil athruithe", + "subscribed_languages.target": "Athraigh teangacha suibscríofa le haghaidh {target}", "tabs_bar.home": "Baile", "tabs_bar.notifications": "Fógraí", "time_remaining.days": "{number, plural, one {# lá} other {# lá}} fágtha", "time_remaining.hours": "{number, plural, one {# uair} other {# uair}} fágtha", "time_remaining.minutes": "{number, plural, one {# nóiméad} other {# nóiméad}} fágtha", + "time_remaining.moments": "Chuimhneacháin fágtha", "time_remaining.seconds": "{number, plural, one {# soicind} other {# soicind}} fágtha", + "timeline_hint.remote_resource_not_displayed": "Níl {resource} ó fhreastalaithe eile ar taispeáint.", "timeline_hint.resources.followers": "Leantóirí", "timeline_hint.resources.follows": "Cuntais leanta", "timeline_hint.resources.statuses": "Postáilí níos sine", "trends.counter_by_accounts": "{count, plural, one {{counter} duine} other {{counter} duine}} le {days, plural, one {lá} other {{days} lá}} anuas", "trends.trending_now": "Ag treochtáil anois", + "ui.beforeunload": "Caillfear do dhréacht má fhágann tú Mastodon.", "units.short.billion": "{count}B", "units.short.million": "{count}M", "units.short.thousand": "{count}k", "upload_area.title": "Tarraing ⁊ scaoil chun uaslódáil", + "upload_button.label": "Cuir íomhánna, físeán nó comhad fuaime leis", + "upload_error.limit": "Sáraíodh an teorainn uaslódála comhaid.", + "upload_error.poll": "Ní cheadaítear uaslódáil comhad le pobalbhreith.", "upload_form.audio_description": "Describe for people with hearing loss", "upload_form.description": "Describe for the visually impaired", "upload_form.edit": "Cuir in eagar", + "upload_form.thumbnail": "Athraigh mionsamhail", "upload_form.video_description": "Describe for people with hearing loss or visual impairment", "upload_modal.analyzing_picture": "Ag anailísiú íomhá…", "upload_modal.apply": "Cuir i bhFeidhm", "upload_modal.applying": "Á gcur i bhfeidhm…", "upload_modal.choose_image": "Roghnaigh íomhá", "upload_modal.description_placeholder": "Chuaigh bé mhórsách le dlúthspád fíorfhinn trí hata mo dhea-phorcáin bhig", + "upload_modal.detect_text": "Braith téacs ó phictiúr", "upload_modal.edit_media": "Cuir gné in eagar", + "upload_modal.hint": "Cliceáil nó tarraing an ciorcal ar an réamhamharc chun an pointe fócasach a roghnú a bheidh le feiceáil i gcónaí ar na mionsamhlacha go léir.", + "upload_modal.preparing_ocr": "OCR á ullmhú…", + "upload_modal.preview_label": "Réamhamharc ({ratio})", "upload_progress.label": "Ag uaslódáil...", "upload_progress.processing": "Ag próiseáil…", + "username.taken": "Glactar leis an ainm úsáideora sin. Bain triail eile as", "video.close": "Dún físeán", "video.download": "Íoslódáil comhad", + "video.exit_fullscreen": "Scoir scáileán iomlán", "video.expand": "Leath físeán", "video.fullscreen": "Lánscáileán", "video.hide": "Cuir físeán i bhfolach", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index bb69b733997bd1..258e8ce08e92f3 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -1,6 +1,6 @@ { "about.blocks": "Prižiūrimi serveriai", - "about.contact": "Kontaktai:", + "about.contact": "Kontaktuoti:", "about.disclaimer": "„Mastodon“ – tai nemokama atvirojo kodo programinė įranga ir „Mastodon“ gGmbH prekės ženklas.", "about.domain_blocks.no_reason_available": "Priežastis nepateikta", "about.domain_blocks.preamble": "„Mastodon“ paprastai leidžia peržiūrėti turinį ir bendrauti su naudotojais iš bet kurio kito fediverse esančio serverio. Šios yra išimtys, kurios buvo padarytos šiame konkrečiame serveryje.", @@ -230,7 +230,7 @@ "domain_pill.your_handle": "Tavo socialinis medijos vardas:", "domain_pill.your_server": "Tavo skaitmeniniai namai, kuriuose saugomi visi tavo įrašai. Nepatinka šis? Bet kada perkelk serverius ir atsivesk ir savo sekėjus.", "domain_pill.your_username": "Tavo unikalus identifikatorius šiame serveryje. Skirtinguose serveriuose galima rasti naudotojų su tuo pačiu naudotojo vardu.", - "embed.instructions": "Įterpk šį įrašą į savo svetainę nukopijavus (-usi) toliau pateiktą kodą.", + "embed.instructions": "Įterpk šį įrašą į savo svetainę nukopijuojant toliau pateiktą kodą.", "embed.preview": "Štai kaip tai atrodys:", "emoji_button.activity": "Veikla", "emoji_button.clear": "Išvalyti", diff --git a/config/locales/activerecord.ga.yml b/config/locales/activerecord.ga.yml index 236cc479e17e1b..1b61edb2e2fe00 100644 --- a/config/locales/activerecord.ga.yml +++ b/config/locales/activerecord.ga.yml @@ -19,8 +19,41 @@ ga: account: attributes: username: + invalid: ní mór go mbeadh litreacha, uimhreacha agus pointí béime amháin reserved: in áirithe + admin/webhook: + attributes: + url: + invalid: nach URL bailí é + doorkeeper/application: + attributes: + website: + invalid: nach URL bailí é import: attributes: data: malformed: míchumtha + status: + attributes: + reblog: + taken: den phost cheana féin + user: + attributes: + email: + blocked: úsáideann soláthraí ríomhphoist dícheadaithe + unreachable: ní cosúil go bhfuil sé ann + role_id: + elevated: ní féidir leat a bheith níos airde ná do ról reatha + user_role: + attributes: + permissions_as_keys: + dangerous: cuir san áireamh ceadanna nach bhfuil sábháilte don ról bunúsach + elevated: ní féidir ceadanna nach bhfuil ag do ról reatha a áireamh + own_role: ní féidir é a athrú le do ról reatha + position: + elevated: ní féidir leat a bheith níos airde ná do ról reatha + own_role: ní féidir é a athrú le do ról reatha + webhook: + attributes: + events: + invalid_permissions: ní féidir imeachtaí nach bhfuil na cearta agat ina leith a chur san áireamh diff --git a/config/locales/ca.yml b/config/locales/ca.yml index f892df02c1e689..4d0e2ab389d2a4 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -226,6 +226,7 @@ ca: update_custom_emoji: Actualitza l'emoji personalitzat update_domain_block: Actualitza el Bloqueig de Domini update_ip_block: Actualitza norma IP + update_report: Actualitza l'informe update_status: Actualitza l'estat update_user_role: Actualitza Rol actions: @@ -638,6 +639,7 @@ ca: report: 'Informe #%{id}' reported_account: Compte denunciat reported_by: Denunciat per + reported_with_application: Reportat amb l'aplicació resolved: Resolt resolved_msg: Informe resolt correctament! skip_to_actions: Salta a les accions diff --git a/config/locales/da.yml b/config/locales/da.yml index fc7373cc94c700..75da9a0b363fdf 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -226,6 +226,7 @@ da: update_custom_emoji: Opdatér tilpasset emoji update_domain_block: Opdatér domæneblokering update_ip_block: Opdatér IP-regel + update_report: Opdatér anmeldelse update_status: Opdatér indlæg update_user_role: Opdatér rolle actions: diff --git a/config/locales/de.yml b/config/locales/de.yml index 82ed9d8178c3d5..81b7cd172d64fb 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -226,6 +226,7 @@ de: update_custom_emoji: Eigenes Emoji aktualisieren update_domain_block: Domain-Sperre aktualisieren update_ip_block: IP-Regel aktualisieren + update_report: Meldung aktualisieren update_status: Beitrag aktualisieren update_user_role: Rolle bearbeiten actions: @@ -638,6 +639,7 @@ de: report: "%{id}. Meldung" reported_account: Gemeldetes Konto reported_by: Gemeldet von + reported_with_application: Per App gemeldet resolved: Geklärt resolved_msg: Meldung erfolgreich geklärt! skip_to_actions: Zur Maßnahme springen diff --git a/config/locales/devise.ga.yml b/config/locales/devise.ga.yml index 6e6bd0a01360b3..e083896f370ec1 100644 --- a/config/locales/devise.ga.yml +++ b/config/locales/devise.ga.yml @@ -11,19 +11,108 @@ ga: invalid: "%{authentication_keys} nó pasfhocal neamhbhailí." last_attempt: Tá iarracht amháin eile agat sula gcuirtear do chuntas faoi ghlas. locked: Tá do chuntas faoi ghlas. + not_found_in_database: "%{authentication_keys} nó pasfhocal neamhbhailí." + omniauth_user_creation_failure: Earráid agus cuntas á chruthú don aitheantas seo. pending: Tá do chuntas fós faoi athbhreithniú. + timeout: Chuaigh do sheisiún in éag. Logáil isteach arís chun leanúint ar aghaidh. unauthenticated: Ní mór duit lógáil isteach nó síniú suas roimh leanúint leat. unconfirmed: Caithfidh tú do r-phost a dheimhniú roimh leanúint leat. mailer: confirmation_instructions: action: Deimhnigh seoladh r-phost action_with_app: Deimhnigh agus fill ar %{app} + explanation: Tá cuntas cruthaithe agat ar %{host} leis an seoladh ríomhphoist seo. Tá tú ceann cliceáil ar shiúl ó é a ghníomhachtú. Mura tusa a bhí ann, déan neamhaird den ríomhphost seo le do thoil. + explanation_when_pending: Chuir tú isteach ar chuireadh chuig %{host} leis an seoladh ríomhphoist seo. Nuair a dheimhníonn tú do sheoladh r-phoist, déanfaimid athbhreithniú ar d’iarratas. Is féidir leat logáil isteach chun do shonraí a athrú nó chun do chuntas a scriosadh, ach ní féidir leat rochtain a fháil ar fhormhór na bhfeidhmeanna go dtí go mbeidh do chuntas faofa. Má dhiúltaítear do d’iarratas, bainfear do shonraí, mar sin ní bheidh aon ghníomh eile ag teastáil uait. Mura tusa a bhí ann, déan neamhaird den ríomhphost seo le do thoil. + extra_html: Seiceáil freisin rialacha an fhreastalaí agus ár dtéarmaí seirbhíse. + subject: 'Mastodon: Treoracha dearbhaithe do %{instance}' + title: Fíoraigh seoladh ríomhphoist email_changed: + explanation: 'Tá seoladh ríomhphoist do chuntais á athrú go:' + extra: Murar athraigh tú do ríomhphost, is dócha go bhfuil rochtain faighte ag duine éigin ar do chuntas. Athraigh do phasfhocal láithreach le do thoil nó déan teagmháil le riarthóir an fhreastalaí má tá tú glasáilte amach as do chuntas. + subject: 'Mastodon: Athraíodh an ríomhphost' title: Seoladh ríomhphoist nua password_change: + explanation: Athraíodh an pasfhocal do do chuntas. + extra: Murar athraigh tú do phasfhocal, is dócha go bhfuil rochtain faighte ag duine éigin ar do chuntas. Athraigh do phasfhocal láithreach le do thoil nó déan teagmháil le riarthóir an fhreastalaí má tá tú glasáilte amach as do chuntas. + subject: 'Mastodon: Athraíodh an pasfhocal' title: Pasfhocal athraithe + reconfirmation_instructions: + explanation: Deimhnigh an seoladh nua chun do ríomhphost a athrú. + extra: Murar tusa a thionscain an t-athrú seo, déan neamhaird den ríomhphost seo le do thoil. Ní athrófar seoladh ríomhphoist an chuntais Mastodon go dtí go bhfaighidh tú rochtain ar an nasc thuas. + subject: 'Mastodon: Deimhnigh ríomhphost le haghaidh %{instance}' + title: Fíoraigh seoladh ríomhphoist reset_password_instructions: action: Athraigh pasfhocal + explanation: D'iarr tú pasfhocal nua do do chuntas. + extra: Murar iarr tú é seo, déan neamhaird den ríomhphost seo le do thoil. Ní athrófar do phasfhocal go dtí go bhfaighidh tú rochtain ar an nasc thuas agus go gcruthóidh tú ceann nua. + subject: 'Mastodon: Athshocraigh treoracha pasfhocail' + title: Athshocrú pasfhocal + two_factor_disabled: + explanation: Is féidir logáil isteach anois ach úsáid a bhaint as seoladh ríomhphoist agus pasfhocal amháin. + subject: 'Mastodon: Díchumasaíodh fíordheimhniú dhá fhachtóir' + subtitle: Díchumasaíodh fíordheimhniú dhá-fhachtóir do do chuntas. + title: 2FA díchumasaithe + two_factor_enabled: + explanation: Beidh comhartha ginte ag an aip TOTP péireáilte ag teastáil chun logáil isteach. + subject: 'Mastodon: Fíordheimhniú dhá-fhachtóir cumasaithe' + subtitle: Cumasaíodh fíordheimhniú dhá fhachtóir do do chuntas. + title: 2FA cumasaithe + two_factor_recovery_codes_changed: + explanation: Tá na cóid athshlánaithe roimhe seo neamhbhailí agus gineadh cinn nua. + subject: 'Mastodon: Cóid aisghabhála dhá-fhachtóir athghinte' + subtitle: Tá na cóid athshlánaithe roimhe seo neamhbhailí agus gineadh cinn nua. + title: Athraíodh cóid aisghabhála 2FA + unlock_instructions: + subject: 'Mastodon: Díghlasáil treoracha' + webauthn_credential: + added: + explanation: Cuireadh an eochair shlándála seo a leanas le do chuntas + subject: 'Mastodon: Eochair shlándála nua' + title: Tá eochair shlándála nua curtha leis + deleted: + explanation: Scriosadh an eochair shlándála seo a leanas ó do chuntas + subject: 'Mastodon: Scriosadh an eochair shlándála' + title: Scriosadh ceann de d'eochracha slándála + webauthn_disabled: + explanation: Díchumasaíodh fíordheimhniú le heochracha slándála do do chuntas. + extra: Is féidir logáil isteach anois ag baint úsáide as an comhartha a ghineann an aip TOTP péireáilte amháin. + subject: 'Mastodon: Díchumasaíodh fíordheimhniú le heochracha slándála' + title: Eochracha slándála díchumasaithe + webauthn_enabled: + explanation: Cumasaíodh fíordheimhniú eochair shlándála do do chuntas. + extra: Is féidir d'eochair shlándála a úsáid le logáil isteach anois. + subject: 'Mastodon: Fíordheimhniú eochair shlándála cumasaithe' + title: Eochracha slándála cumasaithe + omniauth_callbacks: + failure: Níorbh fhéidir tú a fhíordheimhniú ó %{kind} toisc "%{reason}". + success: D'éirigh le fíordheimhniú ó chuntas %{kind}. + passwords: + no_token: Ní féidir leat rochtain a fháil ar an leathanach seo gan teacht ó ríomhphost athshocraithe pasfhocail. Má thagann tú ó ríomhphost athshocraithe pasfhocail, cinntigh le do thoil gur úsáid tú an URL iomlán a cuireadh ar fáil. + send_instructions: Má tá do sheoladh ríomhphoist inár mbunachar sonraí, gheobhaidh tú nasc athshlánaithe pasfhocail ag do sheoladh ríomhphoist i gceann cúpla nóiméad. Seiceáil d'fhillteán turscair mura bhfuair tú an ríomhphost seo. + send_paranoid_instructions: Má tá do sheoladh ríomhphoist inár mbunachar sonraí, gheobhaidh tú nasc athshlánaithe pasfhocail ag do sheoladh ríomhphoist i gceann cúpla nóiméad. Seiceáil d'fhillteán turscair mura bhfuair tú an ríomhphost seo. + updated: Athraíodh do phasfhocal go rathúil. Tá tú sínithe isteach anois. + updated_not_active: Your password has been changed successfully. + registrations: + destroyed: Slán! Cealaíodh do chuntas go rathúil. Tá súil againn tú a fheiceáil arís go luath. + signed_up: Fáilte romhat! D'éirigh leat síniú suas. + signed_up_but_inactive: D'éirigh leat síniú suas. Mar sin féin, níorbh fhéidir linn tú a shíniú isteach toisc nach bhfuil do chuntas gníomhachtaithe fós. + signed_up_but_locked: D'éirigh leat síniú suas. Mar sin féin, níorbh fhéidir linn tú a shíniú isteach toisc go bhfuil do chuntas glasáilte. + signed_up_but_pending: Tá teachtaireacht le nasc deimhnithe seolta chuig do sheoladh ríomhphoist. Tar éis duit an nasc a chliceáil, déanfaimid athbhreithniú ar d’iarratas. Cuirfear in iúl duit má cheadaítear é. + signed_up_but_unconfirmed: Tá teachtaireacht le nasc deimhnithe seolta chuig do sheoladh ríomhphoist. Lean an nasc chun do chuntas a ghníomhachtú. Seiceáil d'fhillteán turscair mura bhfuair tú an ríomhphost seo. + update_needs_confirmation: D'éirigh leat do chuntas a nuashonrú, ach caithfimid do sheoladh ríomhphoist nua a fhíorú. Seiceáil do ríomhphost agus lean an nasc dearbhaithe chun do sheoladh ríomhphoist nua a dhearbhú. Seiceáil d'fhillteán turscair mura bhfuair tú an ríomhphost seo. + updated: D'éirigh le do chuntas a nuashonrú. + sessions: + already_signed_out: D'éirigh le síniú amach. + signed_in: D'éirigh le síniú isteach. + signed_out: D'éirigh le síniú amach. + unlocks: + send_instructions: Gheobhaidh tú ríomhphost le treoracha maidir le conas do chuntas a dhíghlasáil i gceann cúpla nóiméad. Seiceáil d'fhillteán turscair mura bhfuair tú an ríomhphost seo. + send_paranoid_instructions: Má tá do chuntas ann, gheobhaidh tú ríomhphost le treoracha maidir le conas é a dhíghlasáil i gceann cúpla nóiméad. Seiceáil d'fhillteán turscair mura bhfuair tú an ríomhphost seo. + unlocked: Díghlasáladh do chuntas go rathúil. Logáil isteach le do thoil chun leanúint ar aghaidh. errors: messages: + already_confirmed: deimhníodh cheana féin, bain triail as síniú isteach + confirmation_period_expired: ní mór é a dhearbhú laistigh de %{period}, iarr ceann nua le do thoil + expired: imithe in éag, iarr ceann nua le do thoil not_found: níor aimsíodh é + not_locked: nach raibh faoi ghlas diff --git a/config/locales/doorkeeper.ga.yml b/config/locales/doorkeeper.ga.yml index a263a6b15dc529..9ac4740691c928 100644 --- a/config/locales/doorkeeper.ga.yml +++ b/config/locales/doorkeeper.ga.yml @@ -40,6 +40,7 @@ ga: accounts: Cuntais bookmarks: Leabharmharcanna conversations: Comhráite + favourites: Ceanáin filters: Scagairí follows: Cuntais leanta lists: Liostaí @@ -47,5 +48,6 @@ ga: search: Cuardaigh statuses: Postálacha scopes: + read:favourites: féach ar do cheanáin write:filters: cruthaigh scagairí write:lists: cruthaigh liostaí diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 928823b995b674..a45ba290b74a20 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -226,6 +226,7 @@ en-GB: update_custom_emoji: Update Custom Emoji update_domain_block: Update Domain Block update_ip_block: Update IP rule + update_report: Update Report update_status: Update Post update_user_role: Update Role actions: diff --git a/config/locales/es-AR.yml b/config/locales/es-AR.yml index fda1566e22c478..8c55551ce06320 100644 --- a/config/locales/es-AR.yml +++ b/config/locales/es-AR.yml @@ -226,6 +226,7 @@ es-AR: update_custom_emoji: Actualizar emoji personalizado update_domain_block: Actualizar bloque de dominio update_ip_block: Actualizar regla de dirección IP + update_report: Actualizar denuncia update_status: Actualizar mensaje update_user_role: Actualizar rol actions: diff --git a/config/locales/fi.yml b/config/locales/fi.yml index be87258daf22bb..2d10250f68e134 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -226,6 +226,7 @@ fi: update_custom_emoji: Päivitä mukautettu emoji update_domain_block: Päivitä verkkotunnuksen esto update_ip_block: Päivitä IP-sääntö + update_report: Päivitä raportti update_status: Päivitä julkaisu update_user_role: Päivitä rooli actions: diff --git a/config/locales/fo.yml b/config/locales/fo.yml index 945a2216a3d6ba..6a5401ced4882f 100644 --- a/config/locales/fo.yml +++ b/config/locales/fo.yml @@ -226,6 +226,7 @@ fo: update_custom_emoji: Dagfør Sergjørt Kenslutekn update_domain_block: Dagfør Domain forðing update_ip_block: Dagfør IP reglur + update_report: Dagfør frágreiðing update_status: Dagfør Uppslag update_user_role: Dagfør Leiklut actions: @@ -638,6 +639,7 @@ fo: report: 'Melding #%{id}' reported_account: Meldað konta reported_by: Meldað av + reported_with_application: Fráboðað við umsókn resolved: Loyst resolved_msg: Melding avgreidd! skip_to_actions: Leyp til atgerðir diff --git a/config/locales/he.yml b/config/locales/he.yml index 3ba823f99bc685..ecafbec960ba00 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -232,6 +232,7 @@ he: update_custom_emoji: עדכון סמלון מותאם אישית update_domain_block: עדכון חסימת שם מתחם update_ip_block: עדכון כלל IP + update_report: עדכון דו"ח עבירה update_status: סטטוס עדכון update_user_role: עדכון תפקיד actions: diff --git a/config/locales/hu.yml b/config/locales/hu.yml index 19076910e2992c..5def03d902a367 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -226,6 +226,7 @@ hu: update_custom_emoji: Egyéni emodzsi frissítése update_domain_block: Domain tiltás frissítése update_ip_block: IP-szabály frissítése + update_report: Bejelentés Frissítése update_status: Bejegyzés frissítése update_user_role: Szerepkör frissítése actions: @@ -638,6 +639,7 @@ hu: report: "#%{id} számú jelentés" reported_account: Bejelentett fiók reported_by: 'Jelentette:' + reported_with_application: Alkalmazással bejelentve resolved: Megoldott resolved_msg: A bejelentést sikeresen megoldottuk! skip_to_actions: Tovább az intézkedésekhez diff --git a/config/locales/it.yml b/config/locales/it.yml index 1e17c5cbd0c721..68e1608d9c1990 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -226,6 +226,7 @@ it: update_custom_emoji: Aggiorna Emoji Personalizzata update_domain_block: Aggiorna Blocco del Dominio update_ip_block: Aggiorna regola IP + update_report: Aggiorna segnalazione update_status: Aggiorna Toot update_user_role: Aggiorna Ruolo actions: @@ -638,6 +639,7 @@ it: report: 'Rapporto #%{id}' reported_account: Account segnalato reported_by: Inviato da + reported_with_application: Segnalato con applicazione resolved: Risolto resolved_msg: Rapporto risolto! skip_to_actions: Passa alle azioni diff --git a/config/locales/ko.yml b/config/locales/ko.yml index f7feb73ba0b03d..632bd7c097647f 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -223,6 +223,7 @@ ko: update_custom_emoji: 커스텀 에모지 업데이트 update_domain_block: 도메인 차단 갱신 update_ip_block: IP 규칙 수정 + update_report: 신고 업데이트 update_status: 게시물 수정 update_user_role: 역할 수정 actions: diff --git a/config/locales/lt.yml b/config/locales/lt.yml index e731deedc8e64b..004f5b4d043410 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -542,6 +542,8 @@ lt: manage_taxonomies_description: Leidžia naudotojams peržiūrėti tendencingą turinį ir atnaujinti saitažodžių nustatymus manage_user_access_description: Leidžia naudotojams išjungti kitų naudotojų dvigubo tapatybės nustatymą, pakeisti el. pašto adresą ir iš naujo nustatyti slaptažodį. settings: + branding: + title: Firminio ženklo kūrimas captcha_enabled: desc_html: Tai priklauso nuo hCaptcha išorinių skriptų, kurie gali kelti susirūpinimą dėl saugumo ir privatumo. Be to, dėl to registracijos procesas kai kuriems žmonėms (ypač neįgaliesiems) gali būti gerokai sunkiau prieinami. Dėl šių priežasčių apsvarstyk alternatyvias priemones, pavyzdžiui, patvirtinimu arba kvietimu grindžiamą registraciją. content_retention: @@ -663,7 +665,19 @@ lt: edit_preset: Keisti įspėjimo nustatymus title: Įspėjamieji numatytieji webhooks: + add_new: Pridėti galutinį tašką + delete: Ištrinti description_html: "Webhook leidžia Mastodon siųsti realaus laiko pranešimus apie pasirinktus įvykius į tavo programą, kad programa galėtų automatiškai paleisti reakcijas." + disable: Išjungti + disabled: Išjungta + edit: Redaguoti galutinį tašką + enable: Įjungti + enabled: Aktyvi + enabled_events: + few: "%{count} įjungti įvykiai" + many: "%{count} įjungto įvykio" + one: "%{count} įjungtas įvykis" + other: "%{count} įjungtų įvykių" events: Įvykiai admin_mailer: auto_close_registrations: @@ -909,6 +923,7 @@ lt: login_activities: authentication_methods: otp: dvigubas tapatybės nustatymo programėlė + webauthn: saugumo raktai description_html: Jei pastebėjei neatpažįstamą veiklą, apsvarstyk galimybę pakeisti slaptažodį ir įjungti dvigubą tapatybės nustatymą. empty: Tapatybės nustatymas istorijos nėra title: Tapatybės nustatymo istorija @@ -1049,6 +1064,7 @@ lt: relationships: Sekimai ir sekėjai severed_relationships: Nutrūkę sąryšiai two_factor_authentication: Dvigubas tapatybės nustatymas + webauthn_authentication: Saugumo raktai severed_relationships: download: Atsisiųsti (%{count}) preamble: Užblokavus domeną arba prižiūrėtojams nusprendus pristabdyti nuotolinio serverio veiklą, gali prarasti sekimus ir sekėjus. Kai taip atsitiks, galėsi atsisiųsti nutrauktų sąryšių sąrašus, kad juos patikrinti ir galbūt importuoti į kitą serverį. @@ -1110,6 +1126,7 @@ lt: recovery_codes: Atsarginio atkūrimo kodai recovery_codes_regenerated: Atkūrimo kodai sėkmingai sugeneruoti recovery_instructions_html: Jeigu prarandate prieiga prie telefono, jūs galite naudoti atkūrimo kodus esančius žemiau, kad atgautumėte priega prie savo paskyros.Laikykite atkūrimo kodus saugiai Pavyzdžiui, galite norėti juos išspausdinti, ir laikyti kartu su kitais svarbiais dokumentais. + webauthn: Saugumo raktai user_mailer: appeal_approved: action: Paskyros nustatymai @@ -1196,9 +1213,11 @@ lt: verification: Patvirtinimas verified_links: Tavo patikrintos nuorodos webauthn_credentials: + add: Pridėti naują saugumo raktą create: error: Kilo problema pridedant saugumo raktą. Bandyk dar kartą. success: Tavo saugumo raktas buvo sėkmingai pridėtas. + delete: Ištrinti delete_confirmation: Ar tikrai nori ištrinti šį saugumo raktą? description_html: Jei įjungsi saugumo rakto tapatybės nustatymą, prisijungiant reikės naudoti vieną iš savo saugumo raktų. destroy: diff --git a/config/locales/nl.yml b/config/locales/nl.yml index baab2cb587a0e7..3b88dd95282de0 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -226,6 +226,7 @@ nl: update_custom_emoji: Lokale emoji bijwerken update_domain_block: Domeinblokkade bijwerken update_ip_block: IP-regel bijwerken + update_report: Rapportage bijwerken update_status: Bericht bijwerken update_user_role: Rol bijwerken actions: diff --git a/config/locales/nn.yml b/config/locales/nn.yml index 2da30e6627184a..ec77a2edf8792d 100644 --- a/config/locales/nn.yml +++ b/config/locales/nn.yml @@ -226,6 +226,7 @@ nn: update_custom_emoji: Oppdater tilpassa emoji update_domain_block: Oppdater domene-sperring update_ip_block: Oppdater IP-regel + update_report: Oppdater rapport update_status: Oppdater tut update_user_role: Oppdater rolla actions: @@ -638,6 +639,7 @@ nn: report: 'Rapporter #%{id}' reported_account: Rapportert konto reported_by: Rapportert av + reported_with_application: Rapportert med app resolved: Oppløyst resolved_msg: Rapporten er løyst! skip_to_actions: Gå til handlingar diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml index 839ae2184edfc2..1df423bb7de93f 100644 --- a/config/locales/pt-PT.yml +++ b/config/locales/pt-PT.yml @@ -226,6 +226,7 @@ pt-PT: update_custom_emoji: Atualizar Emoji Personalizado update_domain_block: Atualizar Bloqueio de Domínio update_ip_block: Atualizar regra de IP + update_report: Atualizar Relatório update_status: Atualizar Estado update_user_role: Atualizar Função actions: @@ -638,6 +639,7 @@ pt-PT: report: 'Denúncia #%{id}' reported_account: Conta denunciada reported_by: Denunciado por + reported_with_application: Reportado com a aplicação resolved: Resolvido resolved_msg: Denúncia correctamente resolvida! skip_to_actions: Passar para as ações diff --git a/config/locales/simple_form.en-GB.yml b/config/locales/simple_form.en-GB.yml index eaf0501a27ba9e..c1f2a015e94b27 100644 --- a/config/locales/simple_form.en-GB.yml +++ b/config/locales/simple_form.en-GB.yml @@ -211,6 +211,7 @@ en-GB: setting_default_privacy: Posting privacy setting_default_sensitive: Always mark media as sensitive setting_delete_modal: Show confirmation dialogue before deleting a post + setting_disable_hover_cards: Disable profile preview on hover setting_disable_swiping: Disable swiping motions setting_display_media: Media display setting_display_media_default: Default @@ -242,11 +243,13 @@ en-GB: warn: Hide with a warning form_admin_settings: activity_api_enabled: Publish aggregate statistics about user activity in the API + app_icon: App icon backups_retention_period: User archive retention period bootstrap_timeline_accounts: Always recommend these accounts to new users closed_registrations_message: Custom message when sign-ups are not available content_cache_retention_period: Remote content retention period custom_css: Custom CSS + favicon: Favicon mascot: Custom mascot (legacy) media_cache_retention_period: Media cache retention period peers_api_enabled: Publish list of discovered servers in the API diff --git a/config/locales/simple_form.nn.yml b/config/locales/simple_form.nn.yml index a200e1206e38e1..c9075a89c210e3 100644 --- a/config/locales/simple_form.nn.yml +++ b/config/locales/simple_form.nn.yml @@ -211,6 +211,7 @@ nn: setting_default_privacy: Privatliv setting_default_sensitive: Merk alltid media som nærtakande setting_delete_modal: Vis stadfesting før du slettar eit tut + setting_disable_hover_cards: Skru av profilvising når peikaren er over setting_disable_swiping: Skru av sveipebevegelser setting_display_media: Medievisning setting_display_media_default: Standard @@ -242,11 +243,13 @@ nn: warn: Gøym med ei åtvaring form_admin_settings: activity_api_enabled: Legg ut samla statistikk om brukaraktiviteten i APIet + app_icon: App-ikon backups_retention_period: Arkiveringsperiode for brukararkiv bootstrap_timeline_accounts: Tilrå alltid desse kontoane for nye brukarar closed_registrations_message: Eigendefinert melding når registrering ikkje er mogleg content_cache_retention_period: Oppbevaringstid for eksternt innhald custom_css: Egendefinert CSS + favicon: Favorittikon mascot: Eigendefinert maskot (eldre funksjon) media_cache_retention_period: Oppbevaringsperiode for mediebuffer peers_api_enabled: Legg ut ei liste over oppdaga tenarar i APIet diff --git a/config/locales/simple_form.vi.yml b/config/locales/simple_form.vi.yml index 9006156db1a4c7..c45da4713718ca 100644 --- a/config/locales/simple_form.vi.yml +++ b/config/locales/simple_form.vi.yml @@ -243,11 +243,13 @@ vi: warn: Ẩn kèm theo cảnh báo form_admin_settings: activity_api_enabled: Công khai số liệu thống kê tổng hợp về hoạt động của người dùng trong API + app_icon: Biểu tượng ứng dụng backups_retention_period: Thời hạn lưu trữ nội dung người dùng sao lưu bootstrap_timeline_accounts: Luôn đề xuất những người này đến người mới closed_registrations_message: Thông báo tùy chỉnh khi tắt đăng ký content_cache_retention_period: Khoảng thời gian lưu giữ nội dung máy chủ khác custom_css: Tùy chỉnh CSS + favicon: Favicon mascot: Tùy chỉnh linh vật (kế thừa) media_cache_retention_period: Thời hạn lưu trữ cache media peers_api_enabled: Công khai danh sách các máy chủ được phát hiện trong API diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 6087b1a1749642..344368a08a8776 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -232,6 +232,7 @@ sl: update_custom_emoji: Posodobi emotikon po meri update_domain_block: Posodobi blokado domene update_ip_block: Posodobi pravilo IP + update_report: Posodobi poročilo update_status: Posodobi objavo update_user_role: Posodobi vlogo actions: diff --git a/config/locales/sq.yml b/config/locales/sq.yml index 5439f08a04a8b2..c4bed80cc7cf47 100644 --- a/config/locales/sq.yml +++ b/config/locales/sq.yml @@ -226,6 +226,7 @@ sq: update_custom_emoji: Përditëso Emoxhi Vetjake update_domain_block: Përditëso Bllok Përkatësish update_ip_block: Përditësoni rregull IP + update_report: Përditësoni Raportimin update_status: Përditëso Gjendjen update_user_role: Përditësoni Rol actions: diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 91b1720b553f2c..de2664f8b5f109 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -226,6 +226,7 @@ sv: update_custom_emoji: Uppdatera egna emojis update_domain_block: Uppdatera blockerad domän update_ip_block: Uppdatera IP-regel + update_report: Uppdatera rapport update_status: Uppdatera inlägg update_user_role: Uppdatera roll actions: diff --git a/config/locales/tr.yml b/config/locales/tr.yml index c690d22580cc20..6b28e17441ad4c 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -226,6 +226,7 @@ tr: update_custom_emoji: Özel İfadeyi Güncelle update_domain_block: Engellenen Alan Adını Güncelle update_ip_block: IP kuralını güncelle + update_report: Raporu Güncelle update_status: Durumu Güncelle update_user_role: Rolü Güncelle actions: diff --git a/config/locales/uk.yml b/config/locales/uk.yml index 9994cd9d0dadc8..69f5ca665c6362 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -232,6 +232,7 @@ uk: update_custom_emoji: Оновити користувацькі емодзі update_domain_block: Оновити блокування домену update_ip_block: Оновити правило IP + update_report: Оновити звіт update_status: Оновити допис update_user_role: Оновити роль actions: diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 467356d65adfab..48e026d377ff87 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -223,6 +223,7 @@ vi: update_custom_emoji: Cập nhật emoji update_domain_block: Cập nhật máy chủ chặn update_ip_block: Cập nhật chặn IP + update_report: Cập nhật báo cáo update_status: Cập nhật tút update_user_role: Cập nhật vai trò actions: @@ -626,6 +627,7 @@ vi: report: 'Báo cáo #%{id}' reported_account: Tài khoản bị báo cáo reported_by: Báo cáo bởi + reported_with_application: Báo cáo bằng ứng dụng resolved: Đã xong resolved_msg: Đã xử lý báo cáo xong! skip_to_actions: Kiểm duyệt diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index 794b8344c10783..d2d64152fa7bcc 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -223,6 +223,7 @@ zh-CN: update_custom_emoji: 更新自定义表情符号 update_domain_block: 更新域名屏蔽 update_ip_block: 编辑 IP 封禁规则 + update_report: 更新举报 update_status: 更新嘟文 update_user_role: 更新角色 actions: @@ -626,6 +627,7 @@ zh-CN: report: '举报 #%{id}' reported_account: 举报用户 reported_by: 举报人 + reported_with_application: 举报人使用的应用 resolved: 已处理 resolved_msg: 举报处理成功! skip_to_actions: 跳转到操作 diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 3a548c226557e9..375c79048a4d71 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -223,6 +223,7 @@ zh-TW: update_custom_emoji: 更新自訂 emoji 表情符號 update_domain_block: 更新網域封鎖 update_ip_block: 更新 IP 規則 + update_report: 更新檢舉報告 update_status: 更新狀態 update_user_role: 更新角色 actions: @@ -626,6 +627,7 @@ zh-TW: report: '檢舉 #%{id}' reported_account: 被檢舉使用者 reported_by: 檢舉人 + reported_with_application: 透過應用程式檢舉 resolved: 已解決 resolved_msg: 檢舉報告已處理完成! skip_to_actions: 跳過行動 From 81877e79505f387284c7a9de88f631eab758a319 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 17 Jul 2024 03:24:14 -0400 Subject: [PATCH 085/101] Reduce extra round trips in `AP::FetchRemoteStatusService` spec (#31045) --- .../fetch_remote_status_service_spec.rb | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/spec/services/activitypub/fetch_remote_status_service_spec.rb b/spec/services/activitypub/fetch_remote_status_service_spec.rb index c3adb9c470afd1..635fcb7976c954 100644 --- a/spec/services/activitypub/fetch_remote_status_service_spec.rb +++ b/spec/services/activitypub/fetch_remote_status_service_spec.rb @@ -227,7 +227,7 @@ context 'with statuses referencing other statuses', :inline_jobs do before do - stub_const 'ActivityPub::FetchRemoteStatusService::DISCOVERIES_PER_REQUEST', 5 + stub_const 'ActivityPub::FetchRemoteStatusService::DISCOVERIES_PER_REQUEST', 3 end context 'when using inReplyTo' do @@ -243,7 +243,7 @@ end before do - 8.times do |i| + 5.times do |i| status_json = { '@context': 'https://www.w3.org/ns/activitystreams', id: "https://foo.bar/@foo/#{i}", @@ -257,12 +257,10 @@ end end - it 'creates at least some statuses' do - expect { subject.call(object[:id], prefetched_body: Oj.dump(object)) }.to change { sender.statuses.count }.by_at_least(2) - end - - it 'creates no more account than the limit allows' do - expect { subject.call(object[:id], prefetched_body: Oj.dump(object)) }.to change { sender.statuses.count }.by_at_most(5) + it 'creates statuses but not more than limit allows' do + expect { subject.call(object[:id], prefetched_body: Oj.dump(object)) } + .to change { sender.statuses.count }.by_at_least(2) + .and change { sender.statuses.count }.by_at_most(3) end end @@ -287,7 +285,7 @@ end before do - 8.times do |i| + 5.times do |i| status_json = { '@context': 'https://www.w3.org/ns/activitystreams', id: "https://foo.bar/@foo/#{i}", @@ -309,12 +307,10 @@ end end - it 'creates at least some statuses' do - expect { subject.call(object[:id], prefetched_body: Oj.dump(object)) }.to change { sender.statuses.count }.by_at_least(2) - end - - it 'creates no more account than the limit allows' do - expect { subject.call(object[:id], prefetched_body: Oj.dump(object)) }.to change { sender.statuses.count }.by_at_most(5) + it 'creates statuses but not more than limit allows' do + expect { subject.call(object[:id], prefetched_body: Oj.dump(object)) } + .to change { sender.statuses.count }.by_at_least(2) + .and change { sender.statuses.count }.by_at_most(3) end end end From f5e90f3de3efa52c9f8ea72ffdbf091d5032cc4b Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 17 Jul 2024 03:33:08 -0400 Subject: [PATCH 086/101] Reduce extra round trips in `AP::SynchronizeFollowersService` spec (#31044) --- .../synchronize_followers_service_spec.rb | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/spec/services/activitypub/synchronize_followers_service_spec.rb b/spec/services/activitypub/synchronize_followers_service_spec.rb index 648f9a33212c3e..974368b7d77e79 100644 --- a/spec/services/activitypub/synchronize_followers_service_spec.rb +++ b/spec/services/activitypub/synchronize_followers_service_spec.rb @@ -13,11 +13,9 @@ let(:collection_uri) { 'http://example.com/partial-followers' } let(:items) do - [ - ActivityPub::TagManager.instance.uri_for(alice), - ActivityPub::TagManager.instance.uri_for(eve), - ActivityPub::TagManager.instance.uri_for(mallory), - ] + [alice, eve, mallory].map do |account| + ActivityPub::TagManager.instance.uri_for(account) + end end let(:payload) do @@ -40,20 +38,15 @@ subject.call(actor, collection_uri) end - it 'keeps expected followers' do - expect(alice.following?(actor)).to be true - end - - it 'removes local followers not in the remote list' do - expect(bob.following?(actor)).to be false - end - - it 'converts follow requests to follow relationships when they have been accepted' do - expect(mallory.following?(actor)).to be true - end - - it 'sends an Undo Follow to the actor' do - expect(ActivityPub::DeliveryWorker).to have_received(:perform_async).with(anything, eve.id, actor.inbox_url) + it 'maintains following records and sends Undo Follow to actor' do + expect(alice) + .to be_following(actor) # Keep expected followers + expect(bob) + .to_not be_following(actor) # Remove local followers not in remote list + expect(mallory) + .to be_following(actor) # Convert follow request to follow when accepted + expect(ActivityPub::DeliveryWorker) + .to have_received(:perform_async).with(anything, eve.id, actor.inbox_url) # Send Undo Follow to actor end end From 76c2c5c748ba47c2b7bc13570cd4301516290d90 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 17 Jul 2024 04:09:34 -0400 Subject: [PATCH 087/101] Reduce extra round trips in `activitypub` controller specs (#31041) --- .../collections_controller_spec.rb | 28 ++--- ...lowers_synchronizations_controller_spec.rb | 25 ++-- .../activitypub/outboxes_controller_spec.rb | 107 +++++------------- .../activitypub/replies_controller_spec.rb | 12 +- 4 files changed, 51 insertions(+), 121 deletions(-) diff --git a/spec/controllers/activitypub/collections_controller_spec.rb b/spec/controllers/activitypub/collections_controller_spec.rb index 11ef03c8425c1c..a5718fbd7d3c06 100644 --- a/spec/controllers/activitypub/collections_controller_spec.rb +++ b/spec/controllers/activitypub/collections_controller_spec.rb @@ -25,14 +25,12 @@ context 'without signature' do let(:remote_account) { nil } - it 'returns http success and correct media type' do + it_behaves_like 'cacheable response' + + it 'returns http success and correct media type and correct items' do expect(response).to have_http_status(200) expect(response.media_type).to eq 'application/activity+json' - end - it_behaves_like 'cacheable response' - - it 'returns orderedItems with correct items' do expect(body_as_json[:orderedItems]) .to be_an(Array) .and have_attributes(size: 3) @@ -66,14 +64,12 @@ let(:remote_account) { Fabricate(:account, domain: 'example.com') } context 'when getting a featured resource' do - it 'returns http success and correct media type' do + it_behaves_like 'cacheable response' + + it 'returns http success and correct media type and expected items' do expect(response).to have_http_status(200) expect(response.media_type).to eq 'application/activity+json' - end - - it_behaves_like 'cacheable response' - it 'returns orderedItems with expected items' do expect(body_as_json[:orderedItems]) .to be_an(Array) .and have_attributes(size: 3) @@ -92,16 +88,14 @@ account.block!(remote_account) end - it 'returns http success and correct media type and cache headers' do + it 'returns http success and correct media type and cache headers and empty items' do expect(response).to have_http_status(200) expect(response.media_type).to eq 'application/activity+json' expect(response.headers['Cache-Control']).to include 'private' - end - it 'returns empty orderedItems' do expect(body_as_json[:orderedItems]) .to be_an(Array) - .and have_attributes(size: 0) + .and be_empty end end @@ -110,16 +104,14 @@ account.block_domain!(remote_account.domain) end - it 'returns http success and correct media type and cache headers' do + it 'returns http success and correct media type and cache headers and empty items' do expect(response).to have_http_status(200) expect(response.media_type).to eq 'application/activity+json' expect(response.headers['Cache-Control']).to include 'private' - end - it 'returns empty orderedItems' do expect(body_as_json[:orderedItems]) .to be_an(Array) - .and have_attributes(size: 0) + .and be_empty end end end diff --git a/spec/controllers/activitypub/followers_synchronizations_controller_spec.rb b/spec/controllers/activitypub/followers_synchronizations_controller_spec.rb index b50c7b9cdcb1d7..c030078d43a643 100644 --- a/spec/controllers/activitypub/followers_synchronizations_controller_spec.rb +++ b/spec/controllers/activitypub/followers_synchronizations_controller_spec.rb @@ -37,25 +37,18 @@ let(:body) { body_as_json } let(:remote_account) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/instance') } - it 'returns http success' do + it 'returns http success and cache control and activity json types and correct items' do expect(response).to have_http_status(200) - end - - it 'returns application/activity+json' do + expect(response.headers['Cache-Control']).to eq 'max-age=0, private' expect(response.media_type).to eq 'application/activity+json' - end - it 'returns orderedItems with followers from example.com' do - expect(body[:orderedItems]).to be_an Array - expect(body[:orderedItems]).to contain_exactly( - follower_example_com_instance_actor.uri, - follower_example_com_user_a.uri, - follower_example_com_user_b.uri - ) - end - - it 'returns private Cache-Control header' do - expect(response.headers['Cache-Control']).to eq 'max-age=0, private' + expect(body[:orderedItems]) + .to be_an(Array) + .and contain_exactly( + follower_example_com_instance_actor.uri, + follower_example_com_user_a.uri, + follower_example_com_user_b.uri + ) end context 'when account is permanently suspended' do diff --git a/spec/controllers/activitypub/outboxes_controller_spec.rb b/spec/controllers/activitypub/outboxes_controller_spec.rb index ead231f29fbe43..3c8e8e399f6296 100644 --- a/spec/controllers/activitypub/outboxes_controller_spec.rb +++ b/spec/controllers/activitypub/outboxes_controller_spec.rb @@ -25,22 +25,13 @@ context 'with page not requested' do let(:page) { nil } - it 'returns http success' do - expect(response).to have_http_status(200) - end - - it 'returns application/activity+json' do - expect(response.media_type).to eq 'application/activity+json' - end - - it 'returns totalItems' do - expect(body[:totalItems]).to eq 4 - end - it_behaves_like 'cacheable response' - it 'does not have a Vary header' do + it 'returns http success and correct media type and headers and items count' do + expect(response).to have_http_status(200) + expect(response.media_type).to eq 'application/activity+json' expect(response.headers['Vary']).to be_nil + expect(body[:totalItems]).to eq 4 end context 'when account is permanently suspended' do @@ -68,26 +59,18 @@ context 'with page requested' do let(:page) { 'true' } - it 'returns http success' do - expect(response).to have_http_status(200) - end + it_behaves_like 'cacheable response' - it 'returns application/activity+json' do + it 'returns http success and correct media type and vary header and items' do + expect(response).to have_http_status(200) expect(response.media_type).to eq 'application/activity+json' - end + expect(response.headers['Vary']).to include 'Signature' - it 'returns orderedItems with public or unlisted statuses' do expect(body[:orderedItems]).to be_an Array expect(body[:orderedItems].size).to eq 2 expect(body[:orderedItems].all? { |item| targets_public_collection?(item) }).to be true end - it_behaves_like 'cacheable response' - - it 'returns Vary header with Signature' do - expect(response.headers['Vary']).to include 'Signature' - end - context 'when account is permanently suspended' do before do account.suspend! @@ -120,23 +103,14 @@ get :show, params: { account_username: account.username, page: page } end - it 'returns http success' do + it 'returns http success and correct media type and headers and items' do expect(response).to have_http_status(200) - end - - it 'returns application/activity+json' do expect(response.media_type).to eq 'application/activity+json' - end - - it 'returns orderedItems with public or unlisted statuses' do - json = body_as_json - expect(json[:orderedItems]).to be_an Array - expect(json[:orderedItems].size).to eq 2 - expect(json[:orderedItems].all? { |item| targets_public_collection?(item) }).to be true - end - - it 'returns private Cache-Control header' do expect(response.headers['Cache-Control']).to eq 'max-age=60, private' + + expect(body_as_json[:orderedItems]).to be_an Array + expect(body_as_json[:orderedItems].size).to eq 2 + expect(body_as_json[:orderedItems].all? { |item| targets_public_collection?(item) }).to be true end end @@ -146,23 +120,14 @@ get :show, params: { account_username: account.username, page: page } end - it 'returns http success' do + it 'returns http success and correct media type and headers and items' do expect(response).to have_http_status(200) - end - - it 'returns application/activity+json' do expect(response.media_type).to eq 'application/activity+json' - end - - it 'returns orderedItems with private statuses' do - json = body_as_json - expect(json[:orderedItems]).to be_an Array - expect(json[:orderedItems].size).to eq 3 - expect(json[:orderedItems].all? { |item| targets_public_collection?(item) || targets_followers_collection?(item, account) }).to be true - end - - it 'returns private Cache-Control header' do expect(response.headers['Cache-Control']).to eq 'max-age=60, private' + + expect(body_as_json[:orderedItems]).to be_an Array + expect(body_as_json[:orderedItems].size).to eq 3 + expect(body_as_json[:orderedItems].all? { |item| targets_public_collection?(item) || targets_followers_collection?(item, account) }).to be true end end @@ -172,22 +137,14 @@ get :show, params: { account_username: account.username, page: page } end - it 'returns http success' do + it 'returns http success and correct media type and headers and items' do expect(response).to have_http_status(200) - end - - it 'returns application/activity+json' do expect(response.media_type).to eq 'application/activity+json' - end - - it 'returns empty orderedItems' do - json = body_as_json - expect(json[:orderedItems]).to be_an Array - expect(json[:orderedItems].size).to eq 0 - end - - it 'returns private Cache-Control header' do expect(response.headers['Cache-Control']).to eq 'max-age=60, private' + + expect(body_as_json[:orderedItems]) + .to be_an(Array) + .and be_empty end end @@ -197,22 +154,14 @@ get :show, params: { account_username: account.username, page: page } end - it 'returns http success' do + it 'returns http success and correct media type and headers and items' do expect(response).to have_http_status(200) - end - - it 'returns application/activity+json' do expect(response.media_type).to eq 'application/activity+json' - end - - it 'returns empty orderedItems' do - json = body_as_json - expect(json[:orderedItems]).to be_an Array - expect(json[:orderedItems].size).to eq 0 - end - - it 'returns private Cache-Control header' do expect(response.headers['Cache-Control']).to eq 'max-age=60, private' + + expect(body_as_json[:orderedItems]) + .to be_an(Array) + .and be_empty end end end diff --git a/spec/controllers/activitypub/replies_controller_spec.rb b/spec/controllers/activitypub/replies_controller_spec.rb index db7f60d3f873dc..c556e0727047c4 100644 --- a/spec/controllers/activitypub/replies_controller_spec.rb +++ b/spec/controllers/activitypub/replies_controller_spec.rb @@ -66,19 +66,15 @@ context 'when status is public' do let(:parent_visibility) { :public } - let(:json) { body_as_json } - let(:page_json) { json[:first] } + let(:page_json) { body_as_json[:first] } - it 'returns http success' do - expect(response).to have_http_status(200) - end + it_behaves_like 'cacheable response' - it 'returns application/activity+json' do + it 'returns http success and correct media type' do + expect(response).to have_http_status(200) expect(response.media_type).to eq 'application/activity+json' end - it_behaves_like 'cacheable response' - context 'without only_other_accounts' do it "returns items with thread author's replies" do expect(page_json).to be_a Hash From 4957771a8ae36e46cba4b8c4a6278f0e95e5850a Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 17 Jul 2024 11:18:25 +0200 Subject: [PATCH 088/101] Update rexml to version 3.3.2 (#31048) --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 935b28215c6fc1..04505a7cc459b6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -696,7 +696,7 @@ GEM responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) - rexml (3.3.1) + rexml (3.3.2) strscan rotp (6.3.0) rouge (4.2.1) From aa4d8987a00fe82490fb949b82d3baa33d0acd49 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:42:54 +0200 Subject: [PATCH 089/101] chore(deps): update dependency faker to v3.4.2 (#31046) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 04505a7cc459b6..c3e3adbbf3e323 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -231,7 +231,7 @@ GEM tzinfo excon (0.110.0) fabrication (2.31.0) - faker (3.4.1) + faker (3.4.2) i18n (>= 1.8.11, < 2) faraday (1.10.3) faraday-em_http (~> 1.0) From c5f8256801c8c840becf0ea7201bb891adf3c35a Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 18 Jul 2024 03:45:59 -0400 Subject: [PATCH 090/101] Reduce extra factories in `FanOutOnWriteService` spec (#31053) --- .../services/fan_out_on_write_service_spec.rb | 85 ++++++++----------- 1 file changed, 34 insertions(+), 51 deletions(-) diff --git a/spec/services/fan_out_on_write_service_spec.rb b/spec/services/fan_out_on_write_service_spec.rb index 82cdffb8cf9f5c..c6dd020cdff4b6 100644 --- a/spec/services/fan_out_on_write_service_spec.rb +++ b/spec/services/fan_out_on_write_service_spec.rb @@ -34,21 +34,14 @@ def home_feed_of(account) context 'when status is public' do let(:visibility) { 'public' } - it 'is added to the home feed of its author' do - expect(home_feed_of(alice)).to include status.id - end - - it 'is added to the home feed of a follower', :inline_jobs do - expect(home_feed_of(bob)).to include status.id - expect(home_feed_of(tom)).to include status.id - end + it 'adds status to home feed of author and followers and broadcasts', :inline_jobs do + expect(status.id) + .to be_in(home_feed_of(alice)) + .and be_in(home_feed_of(bob)) + .and be_in(home_feed_of(tom)) - it 'is broadcast to the hashtag stream' do expect(redis).to have_received(:publish).with('timeline:hashtag:hoge', anything) expect(redis).to have_received(:publish).with('timeline:hashtag:hoge:local', anything) - end - - it 'is broadcast to the public stream' do expect(redis).to have_received(:publish).with('timeline:public', anything) expect(redis).to have_received(:publish).with('timeline:public:local', anything) expect(redis).to have_received(:publish).with('timeline:public:media', anything) @@ -58,60 +51,41 @@ def home_feed_of(account) context 'when status is limited' do let(:visibility) { 'limited' } - it 'is added to the home feed of its author' do - expect(home_feed_of(alice)).to include status.id - end - - it 'is added to the home feed of the mentioned follower', :inline_jobs do - expect(home_feed_of(bob)).to include status.id - end - - it 'is not added to the home feed of the other follower' do - expect(home_feed_of(tom)).to_not include status.id - end + it 'adds status to home feed of author and mentioned followers and does not broadcast', :inline_jobs do + expect(status.id) + .to be_in(home_feed_of(alice)) + .and be_in(home_feed_of(bob)) + expect(status.id) + .to_not be_in(home_feed_of(tom)) - it 'is not broadcast publicly' do - expect(redis).to_not have_received(:publish).with('timeline:hashtag:hoge', anything) - expect(redis).to_not have_received(:publish).with('timeline:public', anything) + expect_no_broadcasting end end context 'when status is private' do let(:visibility) { 'private' } - it 'is added to the home feed of its author' do - expect(home_feed_of(alice)).to include status.id - end - - it 'is added to the home feed of a follower', :inline_jobs do - expect(home_feed_of(bob)).to include status.id - expect(home_feed_of(tom)).to include status.id - end + it 'adds status to home feed of author and followers and does not broadcast', :inline_jobs do + expect(status.id) + .to be_in(home_feed_of(alice)) + .and be_in(home_feed_of(bob)) + .and be_in(home_feed_of(tom)) - it 'is not broadcast publicly' do - expect(redis).to_not have_received(:publish).with('timeline:hashtag:hoge', anything) - expect(redis).to_not have_received(:publish).with('timeline:public', anything) + expect_no_broadcasting end end context 'when status is direct' do let(:visibility) { 'direct' } - it 'is added to the home feed of its author' do - expect(home_feed_of(alice)).to include status.id - end - - it 'is added to the home feed of the mentioned follower', :inline_jobs do - expect(home_feed_of(bob)).to include status.id - end - - it 'is not added to the home feed of the other follower' do - expect(home_feed_of(tom)).to_not include status.id - end + it 'is added to the home feed of its author and mentioned followers and does not broadcast', :inline_jobs do + expect(status.id) + .to be_in(home_feed_of(alice)) + .and be_in(home_feed_of(bob)) + expect(status.id) + .to_not be_in(home_feed_of(tom)) - it 'is not broadcast publicly' do - expect(redis).to_not have_received(:publish).with('timeline:hashtag:hoge', anything) - expect(redis).to_not have_received(:publish).with('timeline:public', anything) + expect_no_broadcasting end context 'when handling status updates' do @@ -131,4 +105,13 @@ def home_feed_of(account) end end end + + def expect_no_broadcasting + expect(redis) + .to_not have_received(:publish) + .with('timeline:hashtag:hoge', anything) + expect(redis) + .to_not have_received(:publish) + .with('timeline:public', anything) + end end From 2616fde9e6edfbdbf31efad5d1818ad950ab4d4c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 18 Jul 2024 03:49:44 -0400 Subject: [PATCH 091/101] Use change-requiring records in admin/reports controller spec (#31052) --- spec/controllers/admin/reports_controller_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/controllers/admin/reports_controller_spec.rb b/spec/controllers/admin/reports_controller_spec.rb index 02760154fbe5ff..5849163b5fe06e 100644 --- a/spec/controllers/admin/reports_controller_spec.rb +++ b/spec/controllers/admin/reports_controller_spec.rb @@ -64,7 +64,7 @@ describe 'POST #reopen' do it 'reopens the report' do - report = Fabricate(:report) + report = Fabricate(:report, action_taken_at: 3.days.ago) put :reopen, params: { id: report } expect(response).to redirect_to(admin_report_path(report)) @@ -89,7 +89,7 @@ describe 'POST #unassign' do it 'reopens the report' do - report = Fabricate(:report) + report = Fabricate(:report, assigned_account_id: Account.last.id) put :unassign, params: { id: report } expect(response).to redirect_to(admin_report_path(report)) From 64c7ffdc656135b7986ce92d645f5355f638cfe4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 09:53:36 +0200 Subject: [PATCH 092/101] chore(deps): update dependency ruby-vips to v2.2.2 (#31050) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index c3e3adbbf3e323..c9781a40500cb0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -766,8 +766,9 @@ GEM ruby-saml (1.16.0) nokogiri (>= 1.13.10) rexml - ruby-vips (2.2.1) + ruby-vips (2.2.2) ffi (~> 1.12) + logger ruby2_keywords (0.0.5) rubyzip (2.3.2) rufus-scheduler (3.9.1) From 47ea83d2469e461b82e6d837ea83ad561128fe7a Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 18 Jul 2024 04:00:19 -0400 Subject: [PATCH 093/101] Reduce factory creation in `AP::ProcessStatusUpdateService` spec (#31051) --- .../process_status_update_service_spec.rb | 101 +++++------------- 1 file changed, 28 insertions(+), 73 deletions(-) diff --git a/spec/services/activitypub/process_status_update_service_spec.rb b/spec/services/activitypub/process_status_update_service_spec.rb index e451d15dc0359c..a97e8408026b7c 100644 --- a/spec/services/activitypub/process_status_update_service_spec.rb +++ b/spec/services/activitypub/process_status_update_service_spec.rb @@ -40,14 +40,13 @@ def poll_option_json(name, votes) end describe '#call' do - it 'updates text' do + it 'updates text and content warning' do subject.call(status, json, json) - expect(status.reload.text).to eq 'Hello universe' - end - - it 'updates content warning' do - subject.call(status, json, json) - expect(status.reload.spoiler_text).to eq 'Show more' + expect(status.reload) + .to have_attributes( + text: eq('Hello universe'), + spoiler_text: eq('Show more') + ) end context 'when the changes are only in sanitized-out HTML' do @@ -67,12 +66,9 @@ def poll_option_json(name, votes) subject.call(status, json, json) end - it 'does not create any edits' do + it 'does not create any edits and does not mark status edited' do expect(status.reload.edits).to be_empty - end - - it 'does not mark status as edited' do - expect(status.edited?).to be false + expect(status).to_not be_edited end end @@ -90,15 +86,9 @@ def poll_option_json(name, votes) subject.call(status, json, json) end - it 'does not create any edits' do + it 'does not create any edits, mark status edited, or update text' do expect(status.reload.edits).to be_empty - end - - it 'does not mark status as edited' do - expect(status.reload.edited?).to be false - end - - it 'does not update the text' do + expect(status.reload).to_not be_edited expect(status.reload.text).to eq 'Hello world' end end @@ -137,19 +127,10 @@ def poll_option_json(name, votes) subject.call(status, json, json) end - it 'does not create any edits' do + it 'does not create any edits, mark status edited, update text but does update tallies' do expect(status.reload.edits).to be_empty - end - - it 'does not mark status as edited' do - expect(status.reload.edited?).to be false - end - - it 'does not update the text' do + expect(status.reload).to_not be_edited expect(status.reload.text).to eq 'Hello world' - end - - it 'updates tallies' do expect(status.poll.reload.cached_tallies).to eq [4, 3] end end @@ -189,19 +170,10 @@ def poll_option_json(name, votes) subject.call(status, json, json) end - it 'does not create any edits' do + it 'does not create any edits, mark status edited, update text, or update tallies' do expect(status.reload.edits).to be_empty - end - - it 'does not mark status as edited' do - expect(status.reload.edited?).to be false - end - - it 'does not update the text' do + expect(status.reload).to_not be_edited expect(status.reload.text).to eq 'Hello world' - end - - it 'does not update tallies' do expect(status.poll.reload.cached_tallies).to eq [0, 0] end end @@ -213,13 +185,10 @@ def poll_option_json(name, votes) status.snapshot!(rate_limit: false) end - it 'does not create any edits' do - expect { subject.call(status, json, json) }.to_not(change { status.reload.edits.pluck(&:id) }) - end - - it 'does not update the text, spoiler_text or edited_at' do + it 'does not create any edits or update relevant attributes' do expect { subject.call(status, json, json) } - .to_not(change { status.reload.attributes.slice('text', 'spoiler_text', 'edited_at').values }) + .to not_change { status.reload.edits.pluck(&:id) } + .and(not_change { status.reload.attributes.slice('text', 'spoiler_text', 'edited_at').values }) end end @@ -237,12 +206,9 @@ def poll_option_json(name, votes) subject.call(status, json, json) end - it 'does not create any edits' do + it 'does not create any edits or mark status edited' do expect(status.reload.edits).to be_empty - end - - it 'does not mark status as edited' do - expect(status.edited?).to be false + expect(status).to_not be_edited end end @@ -261,12 +227,9 @@ def poll_option_json(name, votes) subject.call(status, json, json) end - it 'does not create any edits' do + it 'does not create any edits or mark status edited' do expect(status.reload.edits).to be_empty - end - - it 'does not mark status as edited' do - expect(status.edited?).to be false + expect(status).to_not be_edited end end @@ -412,11 +375,8 @@ def poll_option_json(name, votes) subject.call(status, json, json) end - it 'removes poll' do + it 'removes poll and records media change in edit' do expect(status.reload.poll).to be_nil - end - - it 'records media change in edit' do expect(status.edits.reload.last.poll_options).to be_nil end end @@ -442,26 +402,21 @@ def poll_option_json(name, votes) subject.call(status, json, json) end - it 'creates a poll' do + it 'creates a poll and records media change in edit' do poll = status.reload.poll expect(poll).to_not be_nil expect(poll.options).to eq %w(Foo Bar Baz) - end - - it 'records media change in edit' do expect(status.edits.reload.last.poll_options).to eq %w(Foo Bar Baz) end end - it 'creates edit history' do - subject.call(status, json, json) - expect(status.edits.reload.map(&:text)).to eq ['Hello world', 'Hello universe'] - end - - it 'sets edited timestamp' do + it 'creates edit history and sets edit timestamp' do subject.call(status, json, json) - expect(status.reload.edited_at.to_s).to eq '2021-09-08 22:39:25 UTC' + expect(status.edits.reload.map(&:text)) + .to eq ['Hello world', 'Hello universe'] + expect(status.reload.edited_at.to_s) + .to eq '2021-09-08 22:39:25 UTC' end end end From 7d090b2ab6a76676c861f737c3b6922da8c1292b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 10:00:29 +0200 Subject: [PATCH 094/101] New Crowdin Translations (automated) (#31055) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/ga.json | 76 +- app/javascript/mastodon/locales/kab.json | 4 +- config/locales/bg.yml | 2 + config/locales/devise.ga.yml | 6 + config/locales/doorkeeper.ga.yml | 144 ++ config/locales/ga.yml | 1532 +++++++++++++++++++++- config/locales/gl.yml | 2 + config/locales/hi.yml | 2 + config/locales/hu.yml | 2 +- config/locales/simple_form.bg.yml | 3 + config/locales/simple_form.ga.yml | 265 ++++ config/locales/sl.yml | 1 + config/locales/tr.yml | 1 + 13 files changed, 1999 insertions(+), 41 deletions(-) diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json index 468c7a03a1c4c6..8c5039e2a46b4f 100644 --- a/app/javascript/mastodon/locales/ga.json +++ b/app/javascript/mastodon/locales/ga.json @@ -29,8 +29,8 @@ "account.enable_notifications": "Cuir mé in eol nuair bpostálann @{name}", "account.endorse": "Cuir ar an phróifíl mar ghné", "account.featured_tags.last_status_at": "Postáil is déanaí ar {date}", - "account.featured_tags.last_status_never": "Níl postáil ar bith ann", - "account.featured_tags.title": "Haischlib {name}", + "account.featured_tags.last_status_never": "Gan aon phoist", + "account.featured_tags.title": "Haischlib faoi thrácht {name}", "account.follow": "Lean", "account.follow_back": "Leanúint ar ais", "account.followers": "Leantóirí", @@ -38,7 +38,7 @@ "account.followers_counter": "{count, plural, one {{counter} leantóir} other {{counter} leantóirí}}", "account.following": "Ag leanúint", "account.following_counter": "{count, plural, one {{counter} ag leanúint} other {{counter} ag leanúint}}", - "account.follows.empty": "Ní leanann an t-úsáideoir seo duine ar bith fós.", + "account.follows.empty": "Ní leanann an t-úsáideoir seo aon duine go fóill.", "account.go_to_profile": "Téigh go dtí próifíl", "account.hide_reblogs": "Folaigh moltaí ó @{name}", "account.in_memoriam": "Cuimhneachán.", @@ -46,7 +46,7 @@ "account.languages": "Athraigh teangacha foscríofa", "account.link_verified_on": "Seiceáladh úinéireacht an naisc seo ar {date}", "account.locked_info": "Tá an socrú príobháideachais don cuntas seo curtha go 'faoi ghlas'. Déanann an t-úinéir léirmheas ar cén daoine atá ceadaithe an cuntas leanúint.", - "account.media": "Ábhair", + "account.media": "Meáin", "account.mention": "Luaigh @{name}", "account.moved_to": "Tá tugtha le fios ag {name} gurb é an cuntas nua atá acu ná:", "account.mute": "Balbhaigh @{name}", @@ -66,7 +66,7 @@ "account.statuses_counter": "{count, plural, one {{counter} post} other {{counter} poist}}", "account.unblock": "Bain bac de @{name}", "account.unblock_domain": "Bain bac den ainm fearainn {domain}", - "account.unblock_short": "Bain bac de", + "account.unblock_short": "Díbhlocáil", "account.unendorse": "Ná chuir ar an phróifíl mar ghné", "account.unfollow": "Ná lean a thuilleadh", "account.unmute": "Díbhalbhaigh @{name}", @@ -100,7 +100,7 @@ "boost_modal.combo": "Is féidir leat {combo} a bhrú chun é seo a scipeáil an chéad uair eile", "bundle_column_error.copy_stacktrace": "Cóipeáil tuairisc earráide", "bundle_column_error.error.body": "Ní féidir an leathanach a iarradh a sholáthar. Seans gurb amhlaidh mar gheall ar fhabht sa chód, nó mar gheall ar mhíréireacht leis an mbrabhsálaí.", - "bundle_column_error.error.title": "Ná habair!", + "bundle_column_error.error.title": "Ó, níl sé sin go maith!", "bundle_column_error.network.body": "Tharla earráid agus an leathanach á lódáil. Seans gur mar gheall ar fhadhb shealadach le do nasc idirlín nó i ndáil leis an bhfreastalaí seo atá sé.", "bundle_column_error.network.title": "Earráid líonra", "bundle_column_error.retry": "Bain triail as arís", @@ -135,9 +135,9 @@ "column_header.hide_settings": "Folaigh socruithe", "column_header.moveLeft_settings": "Bog an colún ar chlé", "column_header.moveRight_settings": "Bog an colún ar dheis", - "column_header.pin": "Greamaigh", + "column_header.pin": "Pionna", "column_header.show_settings": "Taispeáin socruithe", - "column_header.unpin": "Díghreamaigh", + "column_header.unpin": "Bain pionna", "column_subheading.settings": "Socruithe", "community.column_settings.local_only": "Áitiúil amháin", "community.column_settings.media_only": "Meáin Amháin", @@ -161,7 +161,7 @@ "compose_form.poll.switch_to_single": "Athraigh suirbhé chun cead a thabhairt do rogha amháin", "compose_form.poll.type": "Stíl", "compose_form.publish": "Postáil", - "compose_form.publish_form": "Foilsigh\n", + "compose_form.publish_form": "Post nua", "compose_form.reply": "Freagra", "compose_form.save_changes": "Nuashonrú", "compose_form.spoiler.marked": "Bain rabhadh ábhair", @@ -291,7 +291,7 @@ "filter_modal.added.short_explanation": "Cuireadh an postáil seo leis an gcatagóir scagaire seo a leanas: {title}.", "filter_modal.added.title": "Scagaire curtha leis!", "filter_modal.select_filter.context_mismatch": "ní bhaineann sé leis an gcomhthéacs seo", - "filter_modal.select_filter.expired": "as feidhm", + "filter_modal.select_filter.expired": "imithe in éag", "filter_modal.select_filter.prompt_new": "Catagóir nua: {name}", "filter_modal.select_filter.search": "Cuardaigh nó cruthaigh", "filter_modal.select_filter.subtitle": "Bain úsáid as catagóir reatha nó cruthaigh ceann nua", @@ -377,7 +377,7 @@ "keyboard_shortcuts.boost": "Treisigh postáil", "keyboard_shortcuts.column": "to focus a status in one of the columns", "keyboard_shortcuts.compose": "to focus the compose textarea", - "keyboard_shortcuts.description": "Cuntas", + "keyboard_shortcuts.description": "Cur síos", "keyboard_shortcuts.direct": "to open direct messages column", "keyboard_shortcuts.down": "Bog síos ar an liosta", "keyboard_shortcuts.enter": "Oscail postáil", @@ -394,17 +394,17 @@ "keyboard_shortcuts.my_profile": "Oscail do phróifíl", "keyboard_shortcuts.notifications": "to open notifications column", "keyboard_shortcuts.open_media": "Oscail meáin", - "keyboard_shortcuts.pinned": "to open pinned posts list", + "keyboard_shortcuts.pinned": "Oscail liosta postálacha pinn", "keyboard_shortcuts.profile": "Oscail próifíl an t-údar", "keyboard_shortcuts.reply": "Freagair ar phostáil", "keyboard_shortcuts.requests": "Oscail liosta iarratas leanúnaí", - "keyboard_shortcuts.search": "to focus search", - "keyboard_shortcuts.spoilers": "to show/hide CW field", - "keyboard_shortcuts.start": "to open \"get started\" column", - "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", + "keyboard_shortcuts.search": "Díriú ar an mbosca cuardaigh", + "keyboard_shortcuts.spoilers": "Taispeáin / folaigh réimse CW", + "keyboard_shortcuts.start": "Oscail an colún “tosaigh”", + "keyboard_shortcuts.toggle_hidden": "Taispeáin/folaigh an téacs taobh thiar de CW", "keyboard_shortcuts.toggle_sensitivity": "Taispeáin / cuir i bhfolach meáin", "keyboard_shortcuts.toot": "Cuir tús le postáil nua", - "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", + "keyboard_shortcuts.unfocus": "Unfocus cum textarea/search", "keyboard_shortcuts.up": "Bog suas ar an liosta", "lightbox.close": "Dún", "lightbox.compress": "Comhbhrúigh an bosca amhairc íomhá", @@ -545,12 +545,12 @@ "notifications_permission_banner.title": "Ná caill aon rud go deo", "onboarding.action.back": "Tóg ar ais mé", "onboarding.actions.back": "Tóg ar ais mé", - "onboarding.actions.go_to_explore": "See what's trending", - "onboarding.actions.go_to_home": "Go to your home feed", + "onboarding.actions.go_to_explore": "Tóg mé chun trending", + "onboarding.actions.go_to_home": "Tóg go dtí mo bheathú baile mé", "onboarding.compose.template": "Dia duit #Mastodon!", "onboarding.follows.empty": "Ar an drochuair, ní féidir aon torthaí a thaispeáint faoi láthair. Is féidir leat triail a bhaint as cuardach nó brabhsáil ar an leathanach taiscéalaíochta chun teacht ar dhaoine le leanúint, nó bain triail eile as níos déanaí.", - "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.follows.lead": "Is é do bheathú baile an príomhbhealach chun taithí a fháil ar Mastodon. Dá mhéad daoine a leanann tú, is ea is gníomhaí agus is suimiúla a bheidh sé. Chun tú a chur ar bun, seo roinnt moltaí:", + "onboarding.follows.title": "Cuir do chuid fotha baile in oiriúint duit féin", "onboarding.profile.discoverable": "Déan mo phróifíl a fháil amach", "onboarding.profile.discoverable_hint": "Nuair a roghnaíonn tú infhionnachtana ar Mastodon, d’fhéadfadh do phoist a bheith le feiceáil i dtorthaí cuardaigh agus treochtaí, agus d’fhéadfaí do phróifíl a mholadh do dhaoine a bhfuil na leasanna céanna acu leat.", "onboarding.profile.display_name": "Ainm taispeána", @@ -566,17 +566,17 @@ "onboarding.share.message": "Is {username} mé ar #Mastodon! Tar lean mé ag {url}", "onboarding.share.next_steps": "Na chéad chéimeanna eile is féidir:", "onboarding.share.title": "Roinn do phróifíl", - "onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:", - "onboarding.start.skip": "Want to skip right ahead?", + "onboarding.start.lead": "Tá tú mar chuid de Mastodon anois, ardán meán sóisialta díláraithe uathúil ina ndéanann tú - ní algartam - do thaithí féin a choimeád. Cuirimis tús leat ar an teorainn shóisialta nua seo:", + "onboarding.start.skip": "Nach bhfuil cabhair uait le tosú?", "onboarding.start.title": "Tá sé déanta agat!", - "onboarding.steps.follow_people.body": "You curate your own feed. Lets fill it with interesting people.", - "onboarding.steps.follow_people.title": "Follow {count, plural, one {one person} other {# people}}", - "onboarding.steps.publish_status.body": "Say hello to the world.", + "onboarding.steps.follow_people.body": "Is éard atá i gceist le daoine suimiúla a leanúint ná Mastodon.", + "onboarding.steps.follow_people.title": "Cuir do chuid fotha baile in oiriúint duit féin", + "onboarding.steps.publish_status.body": "Abair heileo leis an domhan le téacs, grianghraif, físeáin nó pobalbhreith {emoji}", "onboarding.steps.publish_status.title": "Déan do chéad phostáil", - "onboarding.steps.setup_profile.body": "Others are more likely to interact with you with a filled out profile.", - "onboarding.steps.setup_profile.title": "Customize your profile", - "onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!", - "onboarding.steps.share_profile.title": "Share your profile", + "onboarding.steps.setup_profile.body": "Cuir le d'idirghníomhaíochtaí trí phróifíl chuimsitheach a bheith agat.", + "onboarding.steps.setup_profile.title": "Déan do phróifíl a phearsantú", + "onboarding.steps.share_profile.body": "Cuir in iúl do do chairde conas tú a aimsiú ar Mastodon", + "onboarding.steps.share_profile.title": "Roinn do phróifíl Mastodon", "onboarding.tips.2fa": "An raibh a fhios agat? Is féidir leat do chuntas a dhéanamh slán trí fhíordheimhniú dhá fhachtóir a shocrú i socruithe do chuntais. Oibríonn sé le haon aip TOTP de do rogha féin, níl aon uimhir theileafóin riachtanach!", "onboarding.tips.accounts_from_other_servers": "An raibh a fhios agat? Ós rud é go bhfuil Mastodon díláraithe, déanfar roinnt próifílí a dtagann tú trasna orthu a óstáil ar fhreastalaithe seachas do fhreastalaithe. Agus fós is féidir leat idirghníomhú leo gan uaim! Tá an freastalaí acu sa dara leath dá n-ainm úsáideora!", "onboarding.tips.migration": "An raibh a fhios agat? Más dóigh leat nach rogha freastalaí iontach é {domain} amach anseo, is féidir leat bogadh go freastalaí Mastodon eile gan do leantóirí a chailliúint. Is féidir leat do fhreastalaí féin a óstáil fiú!", @@ -594,7 +594,7 @@ "poll.votes": "{votes, plural, one {# vóta} other {# vóta}}", "poll_button.add_poll": "Cruthaigh suirbhé", "poll_button.remove_poll": "Bain suirbhé", - "privacy.change": "Adjust status privacy", + "privacy.change": "Athraigh príobháideacht postála", "privacy.direct.long": "Luaigh gach duine sa phost", "privacy.direct.short": "Daoine ar leith", "privacy.private.long": "Do leanúna amháin", @@ -687,8 +687,8 @@ "search_popout.specific_date": "dáta ar leith", "search_popout.user": "úsáideoir", "search_results.accounts": "Próifílí", - "search_results.all": "Uile", - "search_results.hashtags": "Haischlibeanna", + "search_results.all": "Gach", + "search_results.hashtags": "Haischlib", "search_results.nothing_found": "Níorbh fhéidir aon rud a aimsiú do na téarmaí cuardaigh seo", "search_results.see_all": "Gach rud a fheicáil", "search_results.statuses": "Postálacha", @@ -705,12 +705,12 @@ "sign_in_banner.sso_redirect": "Logáil isteach nó Cláraigh", "status.admin_account": "Oscail comhéadan modhnóireachta do @{name}", "status.admin_domain": "Oscail comhéadan modhnóireachta le haghaidh {domain}", - "status.admin_status": "Open this status in the moderation interface", + "status.admin_status": "Oscail an postáil seo sa chomhéadan modhnóireachta", "status.block": "Bac @{name}", "status.bookmark": "Leabharmharcanna", "status.cancel_reblog_private": "Dímhol", "status.cannot_reblog": "Ní féidir an phostáil seo a mholadh", - "status.copy": "Copy link to status", + "status.copy": "Cóipeáil an nasc chuig an bpostáil", "status.delete": "Scrios", "status.detailed_status": "Amharc comhrá mionsonraithe", "status.direct": "Luaigh @{name} go príobháideach", @@ -734,11 +734,11 @@ "status.more": "Tuilleadh", "status.mute": "Balbhaigh @{name}", "status.mute_conversation": "Balbhaigh comhrá", - "status.open": "Expand this status", + "status.open": "Leathnaigh an post seo", "status.pin": "Pionnáil ar do phróifíl", "status.pinned": "Postáil pionnáilte", "status.read_more": "Léan a thuilleadh", - "status.reblog": "Mol", + "status.reblog": "Treisiú", "status.reblog_private": "Mol le léargas bunúsach", "status.reblogged_by": "Mhol {name}", "status.reblogs": "{count, plural, one {buaic} other {buaic}}", @@ -757,7 +757,7 @@ "status.show_more": "Taispeáin níos mó", "status.show_more_all": "Taispeáin níos mó d'uile", "status.show_original": "Taispeáin bunchóip", - "status.title.with_attachments": "{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}", + "status.title.with_attachments": "{user} a sheol {attachmentCount, plural, one {ceangal} two {{attachmentCount} ceangal} few {{attachmentCount} ceangail} many {{attachmentCount} ceangal} other {{attachmentCount} ceangal}}", "status.translate": "Aistrigh", "status.translated_from_with": "D'Aistrigh ón {lang} ag úsáid {provider}", "status.uncached_media_warning": "Níl an réamhamharc ar fáil", diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json index a5c3280712a97f..ab0a6ce22bdba0 100644 --- a/app/javascript/mastodon/locales/kab.json +++ b/app/javascript/mastodon/locales/kab.json @@ -28,7 +28,7 @@ "account.featured_tags.last_status_at": "Tasuffeɣt taneggarut ass n {date}", "account.featured_tags.last_status_never": "Ulac tisuffaɣ", "account.follow": "Ḍfer", - "account.follow_back": "Ḍfer-it ula d kečč·m", + "account.follow_back": "Ḍfer-it ula d kečč·mm", "account.followers": "Imeḍfaren", "account.followers.empty": "Ar tura, ulac yiwen i yeṭṭafaṛen amseqdac-agi.", "account.followers_counter": "{count, plural, one {{counter} n umḍfar} other {{counter} n yimeḍfaren}}", @@ -38,6 +38,7 @@ "account.go_to_profile": "Ddu ɣer umaɣnu", "account.hide_reblogs": "Ffer ayen i ibeṭṭu @{name}", "account.joined_short": "Izeddi da seg ass n", + "account.languages": "Beddel tutlayin yettwajerden", "account.link_verified_on": "Taɣara n useɣwen-a tettwasenqed ass n {date}", "account.locked_info": "Amiḍan-agi uslig isekweṛ. D bab-is kan i izemren ad yeǧǧ, s ufus-is, win ara t-iḍefṛen.", "account.media": "Timidyatin", @@ -235,6 +236,7 @@ "follow_request.authorize": "Ssireg", "follow_request.reject": "Agi", "follow_suggestions.dismiss": "Dayen ur t-id-skan ara", + "follow_suggestions.popular_suggestion_longer": "Yettwassen deg {domain}", "follow_suggestions.view_all": "Wali-ten akk", "follow_suggestions.who_to_follow": "Ad tḍefreḍ?", "followed_tags": "Ihacṭagen yettwaḍfaren", diff --git a/config/locales/bg.yml b/config/locales/bg.yml index 602a26225c2d23..c0abc3c845579d 100644 --- a/config/locales/bg.yml +++ b/config/locales/bg.yml @@ -226,6 +226,7 @@ bg: update_custom_emoji: Обновяване на персонализираното емоджи update_domain_block: Обновяване на блокирането за домейна update_ip_block: Обновяване на правило за IP + update_report: Обновяване на доклада update_status: Обновяване на публикация update_user_role: Обновяване на ролята actions: @@ -638,6 +639,7 @@ bg: report: 'Докладване на #%{id}' reported_account: Докладван акаунт reported_by: Докладвано от + reported_with_application: Докладвано с приложението resolved: Разрешено resolved_msg: Успешно разрешен доклад! skip_to_actions: Прескок към действия diff --git a/config/locales/devise.ga.yml b/config/locales/devise.ga.yml index e083896f370ec1..cc8ae0874ea816 100644 --- a/config/locales/devise.ga.yml +++ b/config/locales/devise.ga.yml @@ -116,3 +116,9 @@ ga: expired: imithe in éag, iarr ceann nua le do thoil not_found: níor aimsíodh é not_locked: nach raibh faoi ghlas + not_saved: + few: 'Chuir %{count} earráid cosc ​​ar an %{resource} seo a shábháil:' + many: 'Chuir %{count} earráid cosc ​​ar an %{resource} seo a shábháil:' + one: 'Chuir earráid 1 cosc ​​ar an %{resource} seo a shábháil:' + other: 'Chuir %{count} earráid cosc ​​ar an %{resource} seo a shábháil:' + two: 'Chuir %{count} earráid cosc ​​ar an %{resource} seo a shábháil:' diff --git a/config/locales/doorkeeper.ga.yml b/config/locales/doorkeeper.ga.yml index 9ac4740691c928..09857768e39744 100644 --- a/config/locales/doorkeeper.ga.yml +++ b/config/locales/doorkeeper.ga.yml @@ -5,7 +5,17 @@ ga: doorkeeper/application: name: Ainm feidhmchláir redirect_uri: Atreoraigh URI + scopes: Scóip website: Suíomh gréasáin feidhmchláir + errors: + models: + doorkeeper/application: + attributes: + redirect_uri: + fragment_present: ní féidir blúire a bheith ann. + invalid_uri: caithfidh gur URI bailí é. + relative_uri: a bheith ina URI iomlán. + secured_uri: caithfidh gur URI HTTPS/SSL é. doorkeeper: applications: buttons: @@ -16,38 +26,172 @@ ga: submit: Cuir isteach confirmations: destroy: An bhfuil tú cinnte? + edit: + title: Cuir feidhmchlár in eagar + form: + error: Úps! Seiceáil d'fhoirm le haghaidh earráidí féideartha + help: + native_redirect_uri: Úsáid %{native_redirect_uri} le haghaidh tástálacha logánta + redirect_uri: Úsáid líne amháin in aghaidh an URI + scopes: Scóipeanna ar leith le spásanna. Fág bán chun na scóip réamhshocraithe a úsáid. index: application: Ainm feidhmchláir + callback_url: URL aisghlaoch delete: Scrios + empty: Níl aon iarratais agat. name: Ainm + new: Feidhmchlár nua + scopes: Scóip show: Taispeáin + title: D'iarratais + new: + title: Feidhmchlár nua show: + actions: Gníomhartha application_id: Eochair chliaint + callback_urls: URLanna aisghlaoch + scopes: Scóip secret: Rún cliaint title: 'Ainm feidhmchláir: %{name}' authorizations: buttons: authorize: Ceadaigh deny: Diúltaigh + error: + title: Tharla earráid + new: + prompt_html: Ba mhaith le %{client_name} cead rochtain a fháil ar do chuntas. Is iarratas tríú páirtí é. Mura bhfuil muinín agat as, níor cheart duit é a údarú. + review_permissions: Ceadanna a athbhreithniú + title: Tá údarú ag teastáil + show: + title: Cóipeáil an cód údaraithe seo agus greamaigh don fheidhmchlár é. authorized_applications: + buttons: + revoke: Cúlghair confirmations: revoke: An bhfuil tú cinnte? index: + authorized_at: Ceadaithe ar %{date} + description_html: Is feidhmchláir iad seo ar féidir rochtain a fháil ar do chuntas leis an API. Má tá feidhmchláir ann nach n-aithníonn tú anseo, nó má tá feidhmchlár mí-iompair, is féidir leat a rochtain a chúlghairm. + last_used_at: Úsáidte an uair dheireanach ar %{date} + never_used: Ná húsáideadh scopes: Ceadanna superapp: Inmheánach + title: D’iarratais údaraithe + errors: + messages: + access_denied: Shéan úinéir na hacmhainne nó an freastalaí údaraithe an t-iarratas. + credential_flow_not_configured: Theip ar shreabhadh Dintiúir Pasfhocal Úinéir Acmhainne toisc go raibh Doorkeeper.configure.resource_owner_from_credentials díchumraithe. + invalid_client: Theip ar fhíordheimhniú cliant de bharr cliant anaithnid, níl fíordheimhniú cliant san áireamh, nó modh fíordheimhnithe nach dtacaítear leis. + invalid_grant: Tá an deonú údaraithe ar choinníoll neamhbhailí, imithe in éag, cúlghairthe, nach ionann é agus an URI atreoraithe a úsáideadh san iarratas ar údarú, nó gur eisíodh é chuig cliant eile. + invalid_redirect_uri: Níl an uri atreoraithe atá san áireamh bailí. + invalid_request: + missing_param: 'Paraiméadar riachtanach in easnamh: %{value}.' + request_not_authorized: Ní mór an t-iarratas a údarú. Tá an paraiméadar riachtanach chun iarratas a údarú in easnamh nó neamhbhailí. + unknown: Tá paraiméadar riachtanach in easnamh ar an iarratas, folaíonn sé luach paraiméadar nach dtacaítear leis, nó tá sé míchumtha ar shlí eile. + invalid_resource_owner: Níl na dintiúir úinéara acmhainne a soláthraíodh bailí, nó ní féidir úinéir na hacmhainne a aimsiú + invalid_scope: Tá an scóip iarrtha neamhbhailí, anaithnid nó míchumtha. + invalid_token: + expired: Chuaigh an comhartha rochtana in éag + revoked: Cúlghairmeadh an comhartha rochtana + unknown: Tá an comhartha rochtana neamhbhailí + resource_owner_authenticator_not_configured: Theip ar aimsiú Úinéir Acmhainne toisc go bhfuil Doorkeeper.configure.resource_owner_authenticator díchumraithe. + server_error: Tháinig an freastalaí údaraithe ar riocht gan choinne a chuir cosc ​​air an t-iarratas a chomhlíonadh. + temporarily_unavailable: Ní féidir leis an bhfreastalaí údaraithe an t-iarratas a láimhseáil mar gheall ar ró-ualú sealadach nó cothabháil an fhreastalaí. + unauthorized_client: Níl an cliant údaraithe an t-iarratas seo a dhéanamh leis an modh seo. + unsupported_grant_type: Ní thacaíonn an freastalaí údaraithe leis an gcineál deontais údaraithe. + unsupported_response_type: Ní thacaíonn an freastalaí údaraithe leis an gcineál freagartha seo. + flash: + applications: + create: + notice: Cruthaíodh feidhmchlár. + destroy: + notice: Scriosadh an feidhmchlár. + update: + notice: Feidhmchlár nuashonraithe. + authorized_applications: + destroy: + notice: Cúlghairmeadh an t-iarratas. grouped_scopes: + access: + read: Rochtain inléite-amháin + read/write: Léigh agus scríobh rochtain + write: Rochtain scríofa-amháin title: accounts: Cuntais + admin/accounts: Cuntas a riar + admin/all: Feidhmeanna riaracháin go léir + admin/reports: Tuarascálacha a riar + all: Rochtain iomlán ar do chuntas Mastodon + blocks: Bloic bookmarks: Leabharmharcanna conversations: Comhráite + crypto: Criptiú ceann-go-deireadh favourites: Ceanáin filters: Scagairí + follow: Leanann, Múchann agus Blocálann follows: Cuntais leanta lists: Liostaí + media: Ceangaltáin meán + mutes: Múchann notifications: Fógraí + profile: Do phróifíl Mastodon + push: Fógraí a bhrú + reports: Tuarascálacha search: Cuardaigh statuses: Postálacha + layouts: + admin: + nav: + applications: Feidhmchláir + oauth2_provider: Soláthraí OAuth2 + application: + title: Tá údarú OAuth riachtanach scopes: + admin:read: léigh na sonraí go léir ar an bhfreastalaí + admin:read:accounts: faisnéis íogair na gcuntas go léir a léamh + admin:read:canonical_email_blocks: léigh faisnéis íogair ar gach bloc ríomhphoist canónach + admin:read:domain_allows: léigh faisnéis íogair gach fearainn + admin:read:domain_blocks: léigh faisnéis íogair gach bloc fearainn + admin:read:email_domain_blocks: léigh faisnéis íogair gach bloc fearainn ríomhphoist + admin:read:ip_blocks: léigh faisnéis íogair gach bloic IP + admin:read:reports: faisnéis íogair na dtuarascálacha agus na gcuntas tuairiscithe go léir a léamh + admin:write: na sonraí go léir ar an bhfreastalaí a mhodhnú + admin:write:accounts: gníomhartha modhnóireachta a dhéanamh ar chuntais + admin:write:canonical_email_blocks: gníomhartha modhnóireachta a dhéanamh ar bhlocanna ríomhphoist chanónacha + admin:write:domain_allows: gníomhartha modhnóireachta a dhéanamh ar cheadaíonn fearainn + admin:write:domain_blocks: gníomhartha modhnóireachta a dhéanamh ar bhlocanna fearainn + admin:write:email_domain_blocks: gníomhartha modhnóireachta a dhéanamh ar bhlocanna fearainn ríomhphoist + admin:write:ip_blocks: gníomhartha modhnóireachta a dhéanamh ar bhlocanna IP + admin:write:reports: gníomhartha modhnóireachta a dhéanamh ar thuarascálacha + crypto: úsáid criptiú ceann-go-ceann + follow: caidrimh chuntais a mhodhnú + profile: léigh faisnéis phróifíle do chuntais amháin + push: faigh do bhrúfhógraí + read: léigh sonraí do chuntais go léir + read:accounts: féach eolas cuntais + read:blocks: féach ar do bloic + read:bookmarks: féach ar do leabharmharcanna read:favourites: féach ar do cheanáin + read:filters: féach ar do chuid scagairí + read:follows: féach do chuid seo a leanas + read:lists: féach ar do liostaí + read:mutes: féach ar do bhalbh + read:notifications: féach ar do chuid fógraí + read:reports: féach ar do thuarascálacha + read:search: cuardach ar do shon + read:statuses: féach ar gach post + write: sonraí do chuntais go léir a mhodhnú + write:accounts: do phróifíl a mhodhnú + write:blocks: cuntais agus fearainn a bhlocáil + write:bookmarks: poist leabharmharcála + write:conversations: comhráite balbh agus scrios + write:favourites: poist is fearr leat write:filters: cruthaigh scagairí + write:follows: daoine a leanúint write:lists: cruthaigh liostaí + write:media: uaslódáil comhaid meáin + write:mutes: balbhaigh daoine agus comhráite + write:notifications: soiléir do chuid fógraí + write:reports: tuairisc a thabhairt do dhaoine eile + write:statuses: foilsigh poist diff --git a/config/locales/ga.yml b/config/locales/ga.yml index a3ad293e5bd79d..370f3e82e62ed4 100644 --- a/config/locales/ga.yml +++ b/config/locales/ga.yml @@ -3,13 +3,24 @@ ga: about: about_mastodon_html: 'Líonra sóisialta a sheasfaidh an aimsir: Gan fógraíocht, gan faire chorparáideach, le leagan amach eiticiúil agus dílárú. Bíodh do chuid sonraí agatsa féin le Mastodon!' contact_missing: Gan socrú - contact_unavailable: N/B + contact_unavailable: N/A hosted_on: Mastodon arna óstáil ar %{domain} title: Maidir le accounts: follow: Lean + followers: + few: Leantóirí + many: Leantóirí + one: Leantóir + other: LeantóiríLeantóirí + two: Leantóirí following: Ag leanúint + instance_actor_flash: Is gníomhaí fíorúil é an cuntas seo a úsáidtear chun an freastalaí féin agus ní aon úsáideoir aonair a léiriú. Úsáidtear é chun críocha cónaidhme agus níor cheart é a chur ar fionraí. + last_active: deireanach gníomhach + link_verified_on: Seiceáladh úinéireacht an naisc seo ar %{date} nothing_here: Níl rud ar bith anseo! + pin_errors: + following: Ní mór duit a bheith ag leanúint an duine is mian leat a fhormhuiniú cheana féin posts: few: Postálacha many: Postálacha @@ -23,12 +34,17 @@ ga: title: Dean gníomh modhnóireachta ar %{acct} account_moderation_notes: create: Fág nóta + created_msg: Cruthaíodh nóta modhnóireachta go rathúil! + destroyed_msg: Nóta measarthachta scriosta go rathúil! accounts: + add_email_domain_block: Cuir bac ar fhearann ​​​​ríomhphoist approve: Faomh + approved_msg: D'éirigh le feidhmchlár sínithe %{username} a cheadú are_you_sure: An bhfuil tú cinnte? avatar: Abhatár by_domain: Fearann change_email: + changed_msg: D'éirigh leis an ríomhphost a athrú! current_email: Ríomhphost reatha label: Athraigh ríomhphost new_email: Ríomhphost nua @@ -46,7 +62,9 @@ ga: delete: Scrios sonraí deleted: Scriosta demote: Ísligh + destroyed_msg: Tá sonraí %{username} ciúáilte anois le scriosadh gan mhoill disable: Reoigh + disable_sign_in_token_auth: Díchumasaigh fíordheimhniú comhartha r-phoist disable_two_factor_authentication: Díchumasaigh 2FA disabled: Reoite display_name: Ainm taispeána @@ -55,22 +73,31 @@ ga: email: Ríomhphost email_status: Stádas ríomhphoist enable: Dí-reoigh + enable_sign_in_token_auth: Cumasaigh fíordheimhniú comhartha ríomhphoist enabled: Ar chumas + enabled_msg: D'éirigh le dí-reoite cuntas %{username} followers: Leantóirí follows: Ag leanúint header: Ceanntásc + inbox_url: URL an Bhosca Isteach + invite_request_text: Cúiseanna le bheith páirteach + invited_by: Ar cuireadh ó ip: IP joined: Cláraithe location: all: Uile local: Áitiúil remote: Cian + title: Suíomh login_status: Stádas logála isteach + media_attachments: Ceangaltáin meán memorialize: Déan cuntas chuimhneacháin de memorialized: Cuntas chuimhneacháin + memorialized_msg: D'éirigh le %{username} a iompú ina chuntas cuimhneacháin moderation: active: Gníomhach all: Uile + disabled: Faoi mhíchumas pending: Ar feitheamh silenced: Teoranta suspended: Ar fionraí @@ -78,111 +105,389 @@ ga: moderation_notes: Nótaí modhnóireacht most_recent_activity: Gníomhaíocht is déanaí most_recent_ip: IP is déanaí + no_account_selected: Níor athraíodh aon chuntas mar níor roghnaíodh ceann ar bith + no_limits_imposed: Ní fhorchuirtear aon teorainneacha + no_role_assigned: Níl aon ról sannta + not_subscribed: Gan suibscríofa + pending: Athbhreithniú ar feitheamh perform_full_suspension: Fionraí previous_strikes: Cionta roimhe seo + previous_strikes_description_html: + few: Tá %{count} buailte ag an gcuntas seo. + many: Tá %{count} buailte ag an gcuntas seo. + one: Tá buail amháin ag an gcuntas seo. + other: Tá %{count} buailte ag an gcuntas seo. + two: Tá %{count} buailte ag an gcuntas seo. promote: Ardaigh protocol: Prótacal public: Poiblí + push_subscription_expires: Téann síntiús PuSH in éag redownload: Athnuaigh próifíl + redownloaded_msg: D'éirigh le hathnuachan próifíl %{username} ón mbunús reject: Diúltaigh + rejected_msg: D'éirigh le diúltú le feidhmchlár sínithe %{username} + remote_suspension_irreversible: Scriosadh sonraí an chuntais seo go do-aisiompaithe. + remote_suspension_reversible_hint_html: Cuireadh an cuntas ar fionraí ar a fhreastalaí, agus bainfear na sonraí go hiomlán ar %{date}. Go dtí sin, is féidir leis an gcianfhreastalaí an cuntas seo a chur ar ais gan aon drochéifeachtaí. Más mian leat sonraí uile an chuntais a bhaint láithreach, is féidir leat é sin a dhéanamh thíos. remove_avatar: Bain abhatár remove_header: Bain ceanntásc + removed_avatar_msg: D'éirigh leis an íomhá avatar %{username} a bhaint + removed_header_msg: D'éirigh le híomhá ceanntásc %{username} a bhaint + resend_confirmation: + already_confirmed: Tá an t-úsáideoir seo deimhnithe cheana féin + send: Seol an nasc deimhnithe arís + success: D'éirigh le nasc deimhnithe seolta! reset: Athshocraigh reset_password: Athshocraigh pasfhocal resubscribe: Athchláraigh role: Ról search: Cuardaigh + search_same_email_domain: Úsáideoirí eile a bhfuil an fearann ​​ríomhphoist céanna acu + search_same_ip: Úsáideoirí eile a bhfuil an IP céanna acu + security: Slándáil security_measures: only_password: Pasfhocal amháin password_and_2fa: Pasfhocal agus fíordheimhniú déshraithe + sensitive: Fórsa-íogair sensitized: Marcáladh mar íogair + shared_inbox_url: URL an bhosca isteach roinnte + show: + created_reports: Tuarascálacha déanta + targeted_reports: Thuairiscigh daoine eile silence: Teorannaigh silenced: Teoranta statuses: Postálacha + strikes: Stailceanna roimhe seo subscribe: Cláraigh suspend: Cuir ar fionraí suspended: Ar fionraí + suspension_irreversible: Scriosadh sonraí an chuntais seo go do-aisiompaithe. Is féidir leat an cuntas a chur ar fionraí chun é a dhéanamh inúsáidte ach ní dhéanfaidh sé aon sonraí a bhí aige roimhe seo a aisghabháil. + suspension_reversible_hint_html: Tá an cuntas curtha ar fionraí, agus bainfear na sonraí go hiomlán ar %{date}. Go dtí sin, is féidir an cuntas a chur ar ais gan aon drochthionchar. Más mian leat sonraí uile an chuntais a bhaint láithreach, is féidir leat é sin a dhéanamh thíos. title: Cuntais + unblock_email: Bain an bac den seoladh ríomhphoist + unblocked_email_msg: D'éirigh leis an mbac a bhain le seoladh ríomhphoist %{username} + unconfirmed_email: Ríomhphost neamhdheimhnithe + undo_sensitized: Cealaigh fórsa-íogair + undo_silenced: Cealaigh teorainn + undo_suspension: Cealaigh fionraí + unsilenced_msg: D'éirigh leis an teorainn chuntas %{username} a bhaint + unsubscribe: Díliostáil + unsuspended_msg: D'éirigh le dífhorlannú cuntas %{username} username: Ainm úsáideora + view_domain: Féach ar achoimre le haghaidh fearainn warn: Rabhadh a thabhairt web: Gréasán + whitelisted: Ceadaithe do chónaidhm action_logs: action_types: + approve_appeal: Achomharc a cheadú + approve_user: Úsáideoir a cheadú assigned_to_self_report: Sann Tuairisc + change_email_user: Athraigh Ríomhphost don Úsáideoir + change_role_user: Athrú Ról an Úsáideora + confirm_user: Deimhnigh Úsáideoir create_account_warning: Cruthaigh Rabhadh + create_announcement: Cruthaigh Fógra + create_canonical_email_block: Cruthaigh Bloc Ríomhphoist + create_custom_emoji: Cruthaigh Emoji Saincheaptha + create_domain_allow: Cruthaigh Ceadaigh Fearainn + create_domain_block: Cruthaigh Bloc Fearainn + create_email_domain_block: Cruthaigh Bloc Fearainn Ríomhphoist create_ip_block: Cruthaigh riail IP + create_unavailable_domain: Cruthaigh Fearann ​​Gan Fáil create_user_role: Cruthaigh Ról demote_user: Ísligh úsáideoir destroy_announcement: Scrios Fógra + destroy_canonical_email_block: Scrios Bloc Ríomhphoist + destroy_custom_emoji: Scrios Saincheaptha Emoji + destroy_domain_allow: Scrios Ceadaigh Fearainn + destroy_domain_block: Scrios Bloc Fearainn + destroy_email_domain_block: Scrios Bloc Fearainn R-phoist + destroy_instance: Fearann ​​Purge destroy_ip_block: Scrios riail IP destroy_status: Scrios Postáil + destroy_unavailable_domain: Scrios Fearann ​​Gan Fáil destroy_user_role: Scrios ról + disable_2fa_user: Díchumasaigh 2FA + disable_custom_emoji: Díchumasaigh Emoji Saincheaptha + disable_sign_in_token_auth_user: Díchumasaigh Fíordheimhniú Comhartha R-phoist don Úsáideoir + disable_user: Díchumasaigh Úsáideoir + enable_custom_emoji: Cumasaigh Emoji Saincheaptha + enable_sign_in_token_auth_user: Cumasaigh Fíordheimhniú Comhartha R-phoist don Úsáideoir + enable_user: Cumasaigh Úsáideoir + memorialize_account: Cuntas Cuimhneacháin + promote_user: Úsáideoir a chur chun cinn + reject_appeal: Diúltaigh Achomharc + reject_user: Diúltaigh Úsáideoir remove_avatar_user: Bain Abhatár reopen_report: Athoscail tuairisc + resend_user: Seol Ríomhphost Deimhnithe arís reset_password_user: Athshocraigh Pasfhocal resolve_report: Réitigh tuairisc + sensitive_account: Cuntas Íogair Fórsa + silence_account: Cuntas Teorainn + suspend_account: Cuntas a Fhionraí unassigned_report: Díshann Tuairisc + unblock_email_account: Bain an bac den seoladh ríomhphoist + unsensitive_account: Cealaigh Cuntas Íogair Fórsa + unsilence_account: Cealaigh an Cuntas Teorainn + unsuspend_account: Díghlasáil Cuntas update_announcement: Nuashonraigh Fógra + update_custom_emoji: Nuashonraigh Emoji Saincheaptha + update_domain_block: Nuashonraigh Emoji Saincheaptha + update_ip_block: Íoslódáil an riail IP + update_report: Tuairisc Nuashonraithe update_status: Nuashonraigh Postáil update_user_role: Nuashonraigh Ról actions: + approve_appeal_html: Cheadaigh %{name} achomharc ar chinneadh modhnóireachta ó %{target} + approve_user_html: Cheadaigh %{name} clárú ó %{target} + assigned_to_self_report_html: Shann %{name} tuairisc %{target} dóibh féin + change_email_user_html: D'athraigh %{name} seoladh ríomhphoist úsáideora %{target} + change_role_user_html: D'athraigh %{name} ról %{target} + confirm_user_html: Dhearbhaigh %{name} seoladh ríomhphoist úsáideora %{target} create_account_warning_html: Sheol %{name} rabhadh chuig %{target} + create_announcement_html: Chruthaigh %{name} fógra nua %{target} + create_canonical_email_block_html: Chuir %{name} bac ar ríomhphost leis an hash %{target} + create_custom_emoji_html: D'uaslódáil %{name} emoji nua %{target} + create_domain_allow_html: Cheadaigh %{name} cónaidhm le fearann ​​%{target} + create_domain_block_html: "%{name} fearann ​​bactha %{target}" + create_email_domain_block_html: Chuir %{name} bac ar fhearann ​​ríomhphoist %{target} + create_ip_block_html: Chruthaigh %{name} riail don IP %{target} + create_unavailable_domain_html: Chuir %{name} deireadh leis an seachadadh chuig fearann ​​%{target} + create_user_role_html: Chruthaigh %{name} %{target} ról + demote_user_html: "%{name} úsáideoir scriosta %{target}" + destroy_announcement_html: "%{name} fógra scriosta %{target}" + destroy_canonical_email_block_html: "%{name} ríomhphost díchoiscthe leis an hash %{target}" + destroy_custom_emoji_html: Scriosadh %{name} emoji %{target} + destroy_domain_allow_html: Dhiúltaigh %{name} cónaidhm le fearann ​​%{target} + destroy_domain_block_html: "%{name} fearann ​​%{target} bainte de" + destroy_email_domain_block_html: "%{name} bain an bac den fhearann ​​ríomhphoist %{target}" + destroy_instance_html: Glanadh %{name} fearann ​​%{target} + destroy_ip_block_html: "%{name} scriosta riail le haghaidh IP %{target}" + destroy_status_html: Bhain %{name} postáil le %{target} + destroy_unavailable_domain_html: D'athchrom %{name} ar an seachadadh chuig fearann ​​%{target} destroy_user_role_html: Scrios %{name} ról %{target} + disable_2fa_user_html: Dhíchumasaigh %{name} riachtanas dhá fhachtóir don úsáideoir %{target} + disable_custom_emoji_html: Dhíchumasaigh %{name} emoji %{target} + disable_sign_in_token_auth_user_html: Dhíchumasaigh %{name} fíordheimhniú comhartha ríomhphoist le haghaidh %{target} + disable_user_html: "%{name} logáil isteach díchumasaithe d'úsáideoir %{target}" + enable_custom_emoji_html: "%{name} emoji cumasaithe %{target}" + enable_sign_in_token_auth_user_html: Chuir %{name} fíordheimhniú comhartha ríomhphoist ar chumas %{target} + enable_user_html: "%{name} logáil isteach cumasaithe don úsáideoir %{target}" + memorialize_account_html: Rinne %{name} cuntas %{target} a iompú ina leathanach cuimhneacháin + promote_user_html: Chuir %{name} úsáideoir %{target} chun cinn + reject_appeal_html: Dhiúltaigh %{name} achomharc ar chinneadh modhnóireachta ó %{target} + reject_user_html: Dhiúltaigh %{name} síniú suas ó %{target} + remove_avatar_user_html: Bhain %{name} avatar %{target} + reopen_report_html: D'athoscail %{name} tuairisc %{target} + resend_user_html: "%{name} athsheoladh ríomhphost deimhnithe le haghaidh %{target}" + reset_password_user_html: Athshocraigh %{name} pasfhocal úsáideora %{target} + resolve_report_html: Réitigh %{name} tuairisc %{target} + sensitive_account_html: Mharcáil %{name} meán %{target} mar íogair + silence_account_html: Chuir %{name} teorainn le cuntas %{target} + suspend_account_html: Chuir %{name} cuntas %{target} ar fionraí + unassigned_report_html: "%{name} tuairisc neamhshannta %{target}" + unblock_email_account_html: Bhain %{name} seoladh ríomhphoist %{target} amach + unsensitive_account_html: "%{name} meán %{target} gan mharcáil mar íogair" + unsilence_account_html: Chealaigh %{name} teorainn chuntas %{target} + unsuspend_account_html: Níor chuir %{name} cuntas %{target} ar fionraí + update_announcement_html: "%{name} fógra nuashonraithe %{target}" + update_custom_emoji_html: "%{name} emoji nuashonraithe %{target}" + update_domain_block_html: "%{name} nuashonraithe bloc fearainn le haghaidh %{target}" + update_ip_block_html: D'athraigh %{name} riail an IP %{target} + update_report_html: "%{name} tuairisc nuashonraithe %{target}" + update_status_html: "%{name} postáil nuashonraithe faoi %{target}" update_user_role_html: D'athraigh %{name} ról %{target} deleted_account: cuntas scriosta + empty: Níor aimsíodh aon logaí. + filter_by_action: Scag de réir gnímh + filter_by_user: Scag de réir úsáideora + title: Loga iniúchta + unavailable_instance: "(ainm fearainn ar fáil)" announcements: + destroyed_msg: D'éirigh leis an bhfógra a scriosadh! edit: title: Cuir fógra in eagar + empty: Níor aimsíodh aon fhógraí. live: Beo new: create: Cruthaigh fógra title: Fógra nua publish: Foilsigh + published_msg: D’éirigh leis an bhfógra a fhoilsiú! + scheduled_for: Sceidealta le haghaidh %{time} + scheduled_msg: Tá an fógra le foilsiú! title: Fógraí + unpublish: Dífhoilsiú + unpublished_msg: D’éirigh leis an bhfógra neamhfhoilsithe! + updated_msg: D'éirigh leis an bhfógra a nuashonrú! + critical_update_pending: Nuashonrú criticiúil ar feitheamh custom_emojis: + assign_category: Sann catagóir by_domain: Fearann + copied_msg: Cruthaíodh cóip áitiúil den emoji go rathúil copy: Cóipeáil + copy_failed_msg: Níorbh fhéidir cóip áitiúil den emoji sin a dhéanamh create_new_category: Cruthaigh catagóir nua created_msg: Cruthaíodh emoji go rathúil! delete: Scrios destroyed_msg: Scriosadh emoji go rathúil! disable: Díchumasaigh disabled: Díchumasaithe + disabled_msg: D'éirigh leis an emoji sin a dhíchumasú emoji: Emoji enable: Cumasaigh enabled: Ar chumas + enabled_msg: D'éirigh leis an emoji sin a chumasú + image_hint: PNG nó GIF suas go %{size} list: Liosta listed: Liostaithe + new: + title: Cuir emoji saincheaptha nua leis + no_emoji_selected: Níor athraíodh emojis ar bith mar níor roghnaíodh ceann ar bith + not_permitted: Níl cead agat an gníomh seo a dhéanamh overwrite: Forscríobh + shortcode: Gearrchód + shortcode_hint: 2 charachtar ar a laghad, gan ach carachtair alfa-uimhriúla agus béim ar leith + title: Emojis saincheaptha uncategorized: Neamhchatagóirithe unlist: Neamhliostaigh unlisted: Neamhliostaithe + update_failed_msg: Níorbh fhéidir an emoji sin a nuashonrú + updated_msg: D'éirigh le Emoji a nuashonrú! upload: Uaslódáil dashboard: active_users: úsáideoirí gníomhacha + interactions: idirghníomhaíochtaí + media_storage: Stóráil meáin new_users: úsáideoirí nua opened_reports: tuairiscí oscailte + pending_appeals_html: + few: "%{count} achomharc ar feitheamh" + many: "%{count} achomharc ar feitheamh" + one: "%{count} ar feitheamh achomhairc" + other: "%{count} achomharc ar feitheamh" + two: "%{count} achomharc ar feitheamh" + pending_reports_html: + few: "%{count} tuairiscí ar feitheamh" + many: "%{count} tuairiscí ar feitheamh" + one: "%{count} tuairisc ar feitheamh" + other: "%{count} tuairiscí ar feitheamh" + two: "%{count} tuairiscí ar feitheamh" + pending_tags_html: + few: "%{count} hashtags ar feitheamh" + many: "%{count} hashtags ar feitheamh" + one: "%{count} hashtag ar feitheamh" + other: "%{count} hashtags ar feitheamh" + two: "%{count} hashtags ar feitheamh" + pending_users_html: + few: "%{count} úsáideoirí ar feitheamh" + many: "%{count} úsáideoirí ar feitheamh" + one: "%{count} úsáideoir ar feitheamh" + other: "%{count} úsáideoirí ar feitheamh" + two: "%{count} úsáideoirí ar feitheamh" + resolved_reports: tuarascálacha réitithe software: Bogearraí + sources: Foinsí sínithe + space: Úsáid spáis title: Deais + top_languages: Barr teangacha gníomhacha + top_servers: Barr freastalaithe gníomhacha website: Suíomh Gréasáin disputes: appeals: + empty: Níor aimsíodh aon achomharc. title: Achomhairc domain_allows: + add_new: Ceadaigh cónaidhm leis an bhfearann + created_msg: Ceadaíodh fearann ​​don chónaidhm go rathúil + destroyed_msg: Dícheadaíodh an fearann ​​ón gcónaidhm export: Easpórtáil import: Iompórtáil + undo: Dícheadaigh cónaidhm leis an bhfearann domain_blocks: + add_new: Cuir bloc fearainn nua leis + confirm_suspension: + cancel: Cealaigh + confirm: Fionraí + permanent_action: Má dhéantar an fionraí a chealú, ní dhéanfar aon sonraí nó gaol a athbhunú. + preamble_html: Tá tú ar tí %{domain} agus a fhofhearainn a chur ar fionraí. + remove_all_data: Bainfidh sé seo gach ábhar, meán agus sonraí próifíle do chuntais an fhearainn seo de do fhreastalaí. + stop_communication: Stopfaidh do fhreastalaí ag cumarsáid leis na freastalaithe seo. + title: Deimhnigh bloc fearainn le haghaidh %{domain} + undo_relationships: Déanfaidh sé seo aon ghaol leantach idir cuntais na bhfreastalaithe seo agus do chuid féin a chealú. + created_msg: Tá bloc fearainn á phróiseáil anois + destroyed_msg: Tá an bloc fearainn cealaithe domain: Fearann + edit: Cuir bloc fearainn in eagar + existing_domain_block: Chuir tú teorainneacha níos déine ar %{name} cheana féin. + existing_domain_block_html: Chuir tú teorainneacha níos déine ar %{name} cheana féin, ní mór duit an bac a bhaint as ar dtús. export: Easpórtáil import: Iompórtáil new: + create: Cruthaigh bloc + hint: Ní choiscfidh an bloc fearainn iontrálacha cuntais a chruthú sa bhunachar sonraí, ach cuirfidh sé modhanna modhnóireachta sonracha i bhfeidhm go haisghníomhach agus go huathoibríoch ar na cuntais sin. severity: + desc_html: Déanfaidh Teorainn postálacha ó chuntais ag an bhfearann ​​seo dofheicthe d'aon duine nach bhfuil á leanúint. Bainfidh Fionraí gach ábhar, meán agus sonraí próifíle do chuntais an fhearainn seo de do fhreastalaí. Úsáid Dada más mian leat comhaid meán a dhiúltú. + noop: Dada silence: Teorannaigh suspend: Cuir ar fionraí + title: Bloc fearainn nua + no_domain_block_selected: Níor athraíodh aon bhloc fearainn mar níor roghnaíodh ceann ar bith + not_permitted: Níl cead agat an gníomh seo a dhéanamh + obfuscate: Obfuscate ainm fearainn + obfuscate_hint: Cuir bac páirteach ar an ainm fearainn sa liosta má tá fógraíocht ar an liosta teorainneacha fearainn cumasaithe + private_comment: Trácht príobháideach + private_comment_hint: Déan trácht ar an teorannú fearainn seo le haghaidh úsáid inmheánach ag na modhnóirí. + public_comment: Trácht poiblí + public_comment_hint: Déan trácht ar an teorannú fearainn seo don phobal i gcoitinne, má tá fógraíocht ar an liosta teorainneacha fearainn cumasaithe. + reject_media: Diúltaigh comhaid meán + reject_media_hint: Baintear comhaid meán atá stóráilte go háitiúil agus diúltaíonn sé aon cheann a íoslódáil amach anseo. Ní bhaineann le fionraí + reject_reports: Diúltaigh tuarascálacha + reject_reports_hint: Déan neamhaird de gach tuairisc a thagann ón bhfearann ​​seo. Ní bhaineann le fionraí + undo: Cealaigh bloc fearainn + view: Féach ar bhloc fearainn email_domain_blocks: + add_new: Cuir nua leis + allow_registrations_with_approval: Ceadaigh clárúcháin le ceadú + attempts_over_week: + few: "%{count} iarracht chun síniú suas le seachtain anuas" + many: "%{count} iarracht chun síniú suas le seachtain anuas" + one: "%{count} iarracht le seachtain anuas" + other: "%{count} iarracht chun síniú suas le seachtain anuas" + two: "%{count} iarracht chun síniú suas le seachtain anuas" + created_msg: D'éirigh leis an bhfearann ​​ríomhphoist a bhlocáil delete: Scrios + dns: + types: + mx: Taifead MX domain: Fearann + new: + create: Cuir fearann ​​leis + resolve: Réitigh fearann + title: Cuir bac ar fhearann ​​​​r-phoist nua + no_email_domain_block_selected: Níor athraíodh aon bhloc fearainn ríomhphoist mar níor roghnaíodh ceann ar bith + not_permitted: Níl sé ceadaithe + resolved_dns_records_hint_html: Réitíonn an t-ainm fearainn chuig na fearainn MX seo a leanas, atá freagrach sa deireadh as glacadh le ríomhphost. Má dhéantar fearann ​​MX a bhlocáil, cuirfear bac ar chlárúcháin ó aon seoladh ríomhphoist a úsáideann an fearann ​​MX céanna, fiú má tá an t-ainm fearainn infheicthe difriúil. Bí cúramach gan bac a chur ar phríomhsholáthraithe ríomhphoist. + resolved_through_html: Réitithe trí %{domain} + title: Fearainn ríomhphoist bactha + export_domain_allows: + new: + title: Ceadaíonn fearann ​​​​iomportála + no_file: Níor roghnaíodh aon chomhad + export_domain_blocks: + import: + description_html: Tá tú ar tí liosta de bhlocanna fearainn a allmhairiú. Athbhreithnigh an liosta seo go han-chúramach, le do thoil, go háirithe murar scríobh tú féin an liosta seo. + existing_relationships_warning: Caidreamh leantach atá ann cheana féin + private_comment_description_html: 'Chun cabhrú leat teacht ar cad as a dtagann bloic iompórtáilte, cruthófar bloic iompórtáilte leis an nóta tráchta príobháideach seo a leanas: %{comment}' + private_comment_template: Iompórtáilte ó %{source} ar %{date} + title: Iompórtáil bloic fearainn + invalid_domain_block: 'Léiríodh bloc fearainn amháin nó níos mó mar gheall ar an earráid(í): %{error}' + new: + title: Iompórtáil bloic fearainn + no_file: Níor roghnaíodh aon chomhad follow_recommendations: + description_html: "Lean na moltaí cabhraíonn sé le húsáideoirí nua ábhar suimiúil a aimsiú go tapa. Nuair nach mbíonn go leor idirghníomhaithe ag úsáideoir le daoine eile chun moltaí pearsantaithe a leanúint, moltar na cuntais seo ina ionad sin. Déantar iad a athríomh ar bhonn laethúil ó mheascán de chuntais a bhfuil na rannpháirtíochtaí is airde acu le déanaí agus na háirimh áitiúla is airde leanúna do theanga ar leith." language: Don teanga status: Stádas suppress: Coisc moladh leanúna @@ -190,36 +495,94 @@ ga: title: Moltaí leanúna unsuppress: Aischuir moladh leanúna instances: + availability: + description_html: + few: Má theipeann ar sheachadadh chuig an bhfearann ​​ar %{count} laethanta éagsúla gan rath, ní dhéanfar aon iarrachtaí seachadta eile mura bhfaightear ó seachadadh ón bhfearann. + many: Má theipeann ar sheachadadh chuig an bhfearann ​​ar %{count} laethanta éagsúla gan rath, ní dhéanfar aon iarrachtaí seachadta eile mura bhfaightear ó seachadadh ón bhfearann. + one: Má theipeann ar sheachadadh chuig an bhfearann ​​%{count} day gan rath, ní dhéanfar aon iarrachtaí seachadta eile mura bhfaightear ó seachadadh ón bhfearann. + other: Má theipeann ar sheachadadh chuig an bhfearann ​​ar %{count} laethanta éagsúla gan rath, ní dhéanfar aon iarrachtaí seachadta eile mura bhfaightear ó seachadadh ón bhfearann. + two: Má theipeann ar sheachadadh chuig an bhfearann ​​ar %{count} laethanta éagsúla gan rath, ní dhéanfar aon iarrachtaí seachadta eile mura bhfaightear ó seachadadh ón bhfearann. + failure_threshold_reached: Baineadh an tairseach teipe amach ar %{date}. + failures_recorded: + few: Theip ar iarrachtaí ar %{count} lá difriúil. + many: Theip ar iarrachtaí ar %{count} lá difriúil. + one: Theip ar iarracht ar %{count} lá. + other: Theip ar iarrachtaí ar %{count} lá difriúil. + two: Theip ar iarrachtaí ar %{count} lá difriúil. + no_failures_recorded: Uimh teipeanna ar taifead. + title: Infhaighteacht + warning: Níor éirigh leis an iarracht dheireanach chun ceangal leis an bhfreastalaí seo back_to_all: Uile back_to_limited: Teoranta back_to_warning: Rabhadh by_domain: Fearann + confirm_purge: An bhfuil tú cinnte gur mian leat sonraí a scriosadh go buan ón bhfearann ​​seo? content_policies: comment: Nóta inmheánach + description_html: Is féidir leat beartais inneachair a shainiú a chuirfear i bhfeidhm ar gach cuntas ón bhfearann ​​seo agus aon cheann dá fhofhearainn. + limited_federation_mode_description_html: Is féidir leat an rogha a dhéanamh maidir le cónaidhm a cheadú leis an bhfearann ​​seo. policies: + reject_media: Na meáin a dhiúltú + reject_reports: Diúltaigh tuarascálacha silence: Teorannaigh suspend: Cuir ar fionraí policy: Polasaí + reason: Cúis phoiblí + title: Polasaithe ábhair dashboard: + instance_accounts_dimension: Cuntais a lean a bhformhór + instance_accounts_measure: cuntais stóráilte + instance_followers_measure: ár leantóirí ann + instance_follows_measure: a lucht leanta anseo instance_languages_dimension: Teangacha is airde + instance_media_attachments_measure: ceangaltáin meáin stóráilte + instance_reports_measure: tuairiscí mar gheall orthu + instance_statuses_measure: postanna stóráilte delivery: all: Uile + clear: Earráidí seachadta soiléir failing: Ag teip + restart: Seachadadh a atosú + stop: Stad seachadadh unavailable: Níl ar fáil + delivery_available: Tá seachadadh ar fáil + delivery_error_days: Laethanta earráide seachadta + delivery_error_hint: Mura féidir seachadadh a dhéanamh ar feadh %{count} lá, marcálfar go huathoibríoch é mar dosheachadta. + destroyed_msg: Tá sonraí ó %{domain} ciúáilte anois le haghaidh scriosadh gan mhoill. + empty: Níor aimsíodh aon fhearainn. + known_accounts: + few: "%{count} cuntas aitheanta" + many: "%{count} cuntas aitheanta" + one: "%{count} cuntas aitheanta" + other: "%{count} cuntas aitheanta" + two: "%{count} cuntas aitheanta" moderation: all: Uile limited: Teoranta + title: Measarthacht + private_comment: Trácht príobháideach + public_comment: Trácht poiblí purge: Glan + purge_description_html: Má chreideann tú go bhfuil an fearann ​​seo as líne ar feadh tamaill mhaith, is féidir leat gach taifead cuntais agus sonraí gaolmhara ón bhfearann ​​​​seo a scriosadh ó do stór. Seans go dtógfaidh sé seo tamall. title: Cónascadh + total_blocked_by_us: Blocáilte ag dúinn + total_followed_by_them: Ina dhiaidh sin iad total_followed_by_us: Á leanúint againn + total_reported: Tuarascálacha mar gheall orthu + total_storage: Ceangaltáin meán + totals_time_period_hint_html: Áiríonn na hiomláin a thaispeántar thíos sonraí don am ar fad. + unknown_instance: Níl aon taifead den fhearann ​​seo ar an bhfreastalaí seo faoi láthair. invites: + deactivate_all: Díghníomhachtaigh go léir filter: all: Uile available: Ar fáil + expired: Imithe in éag title: Scag title: Cuirí ip_blocks: add_new: Cruthaigh riail + created_msg: Cuireadh riail IP nua leis go rathúil delete: Scrios expires_in: '1209600': Coicís @@ -228,111 +591,609 @@ ga: '31556952': Bliain amháin '86400': Lá amháin '94670856': 3 bhliain + new: + title: Cruthaigh riail IP nua + no_ip_block_selected: Níor athraíodh aon rialacha IP mar níor roghnaíodh ceann ar bith title: Rialacha IP + relationships: + title: Caidrimh %{acct} relays: + add_new: Cuir sealaíochta nua leis delete: Scrios + description_html: Is freastalaí idirghabhálaí é athsheachadán cónaidhme a mhalartaíonn líon mór postálacha poiblí idir freastalaithe a shíníonn dó agus a fhoilsíonn é. Is féidir leis cabhrú le freastalaithe beaga agus meánmhéide inneachar a aimsiú ón bhfeideas, rud a d'éileodh ar úsáideoirí áitiúla daoine eile a leanúint de láimh ar fhreastalaithe cianda. disable: Díchumasaigh disabled: Díchumasaithe enable: Cumasaigh + enable_hint: Nuair a bheidh sé cumasaithe, liostóidh do fhreastalaí le gach postáil phoiblí ón athsheoladh seo, agus tosóidh sé ag seoladh postálacha poiblí an fhreastalaí seo chuige. enabled: Ar chumas + inbox_url: URL Athsheolta + pending: Ag fanacht le ceadú sealaíochta save_and_enable: Sábháil agus cumasaigh + setup: Socraigh nasc sealaíochta + signatures_not_enabled: Seans nach n-oibreoidh athsheachadáin i gceart agus mód slán nó modh cónaidhmthe teoranta cumasaithe status: Stádas + title: Athsheachadáin + report_notes: + created_msg: Cruthaíodh nóta tuairisce go rathúil! + destroyed_msg: D'éirigh leis an nóta tuairisce a scriosadh! reports: + account: + notes: + few: "%{count} nótaí" + many: "%{count} nótaí" + one: "%{count} nóta" + other: "%{count} nótaí" + two: "%{count} nótaí" + action_log: Loga iniúchta + action_taken_by: Gníomh arna ghlacadh ag + actions: + delete_description_html: Scriosfar na postálacha tuairiscithe agus déanfar stailc a thaifeadadh chun cabhrú leat dul in airde ar sháruithe sa todhchaí tríd an gcuntas céanna. + mark_as_sensitive_description_html: Déanfar na meáin sna poist tuairiscithe a mharcáil mar íogair agus déanfar stailc a thaifeadadh chun cabhrú leat sárú a dhéanamh ar sháruithe sa todhchaí tríd an gcuntas céanna. + other_description_html: Féach ar a thuilleadh roghanna chun iompar an chuntais a rialú agus cumarsáid a shaincheapadh chuig an gcuntas tuairiscithe. + resolve_description_html: Ní dhéanfar aon ghníomhaíocht i gcoinne an chuntais thuairiscithe, ní dhéanfar aon stailc a thaifeadadh, agus dúnfar an tuarascáil. + silence_description_html: Ní bheidh an cuntas le feiceáil ach amháin dóibh siúd a leanann é cheana féin nó a bhreathnaíonn suas de láimh air, rud a chuirfidh srian mór ar a rochtain. Is féidir é a chur ar ais i gcónaí. Dúnann sé gach tuairisc i gcoinne an chuntais seo. + suspend_description_html: Beidh an cuntas agus a bhfuil ann go léir dorochtana agus scriosfar iad ar deireadh, agus beidh sé dodhéanta idirghníomhú leis. Inchúlaithe laistigh de 30 lá. Dúnann sé gach tuairisc i gcoinne an chuntais seo. + actions_description_html: Déan cinneadh ar an ngníomh atá le déanamh chun an tuarascáil seo a réiteach. Má dhéanann tú beart pionósach in aghaidh an chuntais tuairiscithe, seolfar fógra ríomhphoist chucu, ach amháin nuair a roghnaítear an chatagóir Turscar. + actions_description_remote_html: Déan cinneadh ar an ngníomh atá le déanamh chun an tuarascáil seo a réiteach. Ní bheidh tionchar aige seo ach ar an gcaoi a ndéanann do fhreastalaí cumarsáid leis an gcianchuntas seo agus a láimhseálann sé a ábhar. + add_to_report: Cuir tuilleadh leis an tuairisc + already_suspended_badges: + local: Ar fionraí cheana féin ar an bhfreastalaí seo + remote: Ar fionraí cheana féin ar a bhfreastalaí are_you_sure: An bhfuil tú cinnte? + assign_to_self: Sann dom + assigned: Modhnóir sannta + by_target_domain: Fearann ​​an chuntais tuairiscithe cancel: Cealaigh category: Catagóir + category_description_html: Luafar an chúis ar tuairiscíodh an cuntas seo agus/nó an t-ábhar seo i gcumarsáid leis an gcuntas tuairiscithe + comment: + none: Dada + comment_description_html: 'Chun tuilleadh eolais a sholáthar, scríobh %{name}:' + confirm: Deimhnigh + confirm_action: Deimhnigh gníomh modhnóireachta i gcoinne @%{acct} created_at: Tuairiscithe delete_and_resolve: Scrios postálacha + forwarded: Ar aghaidh + forwarded_replies_explanation: Is ó chianúsáideoir an tuairisc seo agus faoi chianábhar. Tá sé curtha ar aghaidh chugat toisc go bhfuil an t-ábhar tuairiscithe mar fhreagra ar cheann de na húsáideoirí atá agat. + forwarded_to: Ar aghaidh chuig %{domain} mark_as_resolved: Marcáil mar réitithe mark_as_sensitive: Marcáil mar íogair + mark_as_unresolved: Marcáil mar gan réiteach no_one_assigned: Duine ar bith notes: create: Cruthaigh nóta create_and_resolve: Réitigh le nóta + create_and_unresolve: Oscail arís le nóta delete: Scrios + placeholder: Déan cur síos ar na bearta a rinneadh, nó ar aon nuashonruithe gaolmhara eile... title: Nótaí + notes_description_html: Féach ar agus fág nótaí do mhodhnóirí eile agus duit féin amach anseo + processed_msg: 'D''éirigh le próiseáil an tuairisc # %{id}' + quick_actions_description_html: 'Déan gníomh tapa nó scrollaigh síos chun ábhar tuairiscithe a fheiceáil:' + remote_user_placeholder: an cianúsáideoir ó %{instance} + reopen: Tuairisc a athoscailt + report: 'Tuairiscigh # %{id}' + reported_account: Cuntas tuairiscithe + reported_by: Tuairiscithe ag + reported_with_application: Tuairiscíodh leis an iarratas + resolved: Réitithe + resolved_msg: D'éirigh le réiteach an tuairisc! + skip_to_actions: Léim ar ghníomhartha status: Stádas + statuses: Ábhar tuairiscithe + statuses_description_html: Luafar ábhar ciontach i gcumarsáid leis an gcuntas tuairiscithe + summary: + action_preambles: + delete_html: 'Tá tú ar tí cuid de phostálacha @%{acct} a bhaint. Déanfaidh sé seo:' + mark_as_sensitive_html: 'Tá tú ar tí marcáil ar chuid de phostálacha @%{acct} mar íogair. Déanfaidh sé seo:' + silence_html: 'Tá tú ar tí teorannú a dhéanamh ar chuntas @%{acct}. Déanfaidh sé seo:' + suspend_html: 'Tá tú ar tí cuntas a chur ar fionraí @%{acct}. Déanfaidh sé seo:' + actions: + delete_html: Bain na postálacha ciontaithe + mark_as_sensitive_html: Marcáil meáin na bpost ciontaithe mar íogair + silence_html: Cuir teorainn mhór le rochtain @%{acct} trí a bpróifíl agus a n-inneachar a dhéanamh infheicthe ag daoine atá á leanúint cheana féin nó ag breathnú uirthi de láimh + suspend_html: Cuir @%{acct} ar fionraí, rud a fhágann go bhfuil a bpróifíl agus a bhfuil iontu dorochtana agus dodhéanta idirghníomhú leo + close_report: 'Marcáil an tuairisc #%{id} mar réitithe' + close_reports_html: Marcáil gach tuairisc in aghaidh @%{acct} mar réitithe + delete_data_html: Scrios próifíl agus inneachar @%{acct} 30 lá ó anois mura mbeidh siad curtha ar fionraí idir an dá linn + preview_preamble_html: 'Gheobhaidh @%{acct} rabhadh leis an ábhar seo a leanas:' + record_strike_html: Taifead stailc in aghaidh @%{acct} chun cabhrú leat dul i ngleic le sáruithe amach anseo ón gcuntas seo + send_email_html: Seol ríomhphost rabhaidh chuig @%{acct} + warning_placeholder: Réasúnaíocht bhreise roghnach don ghníomh modhnóireachta. + target_origin: Bunús an chuntais tuairiscithe title: Tuairiscí + unassign: Díshannadh + unknown_action_msg: 'Gníomh anaithnid: %{action}' + unresolved: Gan réiteach + updated_at: Nuashonraithe + view_profile: Féach ar phróifíl roles: add_new: Cruthaigh ról + assigned_users: + few: "%{count} úsáideoirí" + many: "%{count} úsáideoirí" + one: "%{count} úsáideoir" + other: "%{count} úsáideoirí" + two: "%{count} úsáideoirí" categories: administration: Riar + devops: DevOps invites: Cuirí + moderation: Measarthacht + special: Speisialta delete: Scrios + description_html: Le róil úsáideora, is féidir leat na feidhmeanna agus na réimsí de Mastodon ar féidir le d'úsáideoirí rochtain a fháil orthu a shaincheapadh. + edit: Cuir ról '%{name}' in eagar + everyone: Ceadanna réamhshocraithe + everyone_full_description_html: Seo é an bunról a théann i bhfeidhm ar gach úsáideoir, fiú iad siúd nach bhfuil ról sannta acu. Faigheann gach ról eile cead uaidh. + permissions_count: + few: "%{count} ceadanna" + many: "%{count} ceadanna" + one: "%{count} cead" + other: "%{count} ceadanna" + two: "%{count} ceadanna" privileges: administrator: Riarthóir + administrator_description: Seachnóidh úsáideoirí a bhfuil an cead seo acu gach cead delete_user_data: Scrios Sonraí Úsáideora + delete_user_data_description: Ligeann sé d'úsáideoirí sonraí úsáideoirí eile a scriosadh gan mhoill + invite_users: Tabhair cuireadh d'Úsáideoirí + invite_users_description: Ligeann sé d'úsáideoirí cuireadh a thabhairt do dhaoine nua chuig an bhfreastalaí + manage_announcements: Bainistigh Fógraí + manage_announcements_description: Ligeann sé d'úsáideoirí fógraí ar an bhfreastalaí a bhainistiú + manage_appeals: Achomharc a bhainistiú + manage_appeals_description: Ligeann sé d'úsáideoirí athbhreithniú a dhéanamh ar achomhairc i gcoinne gníomhartha modhnóireachta + manage_blocks: Bainistigh Bloic + manage_blocks_description: Ligeann sé d'úsáideoirí bac a chur ar sholáthraithe ríomhphoist agus seoltaí IP + manage_custom_emojis: Bainistigh Emojis Saincheaptha + manage_custom_emojis_description: Ligeann sé d'úsáideoirí emojis saincheaptha a bhainistiú ar an bhfreastalaí + manage_federation: Cónaidhm a bhainistiú + manage_federation_description: Ligeann sé d’úsáideoirí cónaidhm a bhlocáil nó a cheadú le fearainn eile, agus inseachadacht a rialú + manage_invites: Bainistigh Cuirí + manage_invites_description: Ligeann sé d'úsáideoirí naisc cuireadh a bhrabhsáil agus a dhíghníomhachtú + manage_reports: Tuarascálacha a bhainistiú + manage_reports_description: Ligeann sé d’úsáideoirí tuarascálacha a athbhreithniú agus gníomhartha modhnóireachta a dhéanamh ina gcoinne + manage_roles: Bainistigh Róil + manage_roles_description: Ligeann sé d'úsáideoirí róil faoina gcuid féin a bhainistiú agus a shannadh + manage_rules: Rialacha a bhainistiú + manage_rules_description: Ligeann sé d'úsáideoirí rialacha freastalaí a athrú + manage_settings: Bainistigh Socruithe + manage_settings_description: Ligeann sé d'úsáideoirí socruithe suímh a athrú + manage_taxonomies: Tacsanomaíochtaí a bhainistiú + manage_taxonomies_description: Ligeann sé d'úsáideoirí athbhreithniú a dhéanamh ar inneachar treochta agus socruithe hashtag a nuashonrú + manage_user_access: Bainistigh Rochtain Úsáideoir + manage_user_access_description: Ligeann sé d'úsáideoirí fíordheimhniú dhá fhachtóir úsáideoirí eile a dhíchumasú, a seoladh r-phoist a athrú, agus a bpasfhocal a athshocrú + manage_users: Bainistigh Úsáideoirí + manage_users_description: Ligeann sé d'úsáideoirí sonraí úsáideoirí eile a fheiceáil agus gníomhartha modhnóireachta a dhéanamh ina gcoinne + manage_webhooks: Bainistigh cuacha Gréasáin + manage_webhooks_description: Ligeann sé d'úsáideoirí cuacha gréasáin a shocrú le haghaidh imeachtaí riaracháin + view_audit_log: Féach ar Loga Iniúchta + view_audit_log_description: Ligeann sé d'úsáideoirí stair gníomhartha riaracháin a fheiceáil ar an bhfreastalaí + view_dashboard: Amharc ar an Deais + view_dashboard_description: Ligeann sé d’úsáideoirí rochtain a fháil ar an deais agus ar mhéadrachtaí éagsúla + view_devops: DevOps + view_devops_description: Ligeann sé d’úsáideoirí rochtain a fháil ar dheais Sidekiq agus pgHero title: Róil rules: add_new: Cruthaigh riail delete: Scrios + description_html: Cé go maíonn a bhformhór gur léigh siad agus go n-aontaíonn siad leis na téarmaí seirbhíse, de ghnáth ní léann daoine tríd go dtí go dtagann fadhb chun cinn. Déan rialacha do fhreastalaí a fheiceáil go sracfhéachaint trí iad a sholáthar i liosta comhréidh de phointe urchair. Déan iarracht rialacha aonair a choinneáil gearr simplí, ach déan iarracht gan iad a roinnt ina go leor míreanna ar leith ach an oiread. + edit: Cuir riail in eagar + empty: Níl aon rialacha freastalaí sainmhínithe fós. + title: Rialacha freastalaí settings: + about: + manage_rules: Bainistigh rialacha freastalaí + preamble: Cuir eolas domhain ar fáil faoin gcaoi a n-oibrítear, a ndéantar modhnóireacht agus maoiniú ar an bhfreastalaí. + rules_hint: Tá réimse tiomnaithe rialacha ann a bhfuiltear ag súil go gcloífidh d’úsáideoirí leis. + title: Faoi appearance: + preamble: Saincheap comhéadan gréasáin Mastodon. title: Cuma + branding: + preamble: Déanann brandáil do fhreastalaí é a idirdhealú ó fhreastalaithe eile sa líonra. Féadfar an fhaisnéis seo a thaispeáint ar fud timpeallachtaí éagsúla, mar shampla comhéadan gréasáin Mastodon, feidhmchláir dhúchasacha, i réamhamhairc naisc ar láithreáin ghréasáin eile agus laistigh d’aipeanna teachtaireachtaí, agus mar sin de. Ar an ábhar sin, is fearr an fhaisnéis seo a choinneáil soiléir, gearr agus gonta. + title: Brandáil + captcha_enabled: + desc_html: Braitheann sé seo ar scripteanna seachtracha ó hCaptcha, rud a d’fhéadfadh a bheith ina ábhar imní maidir le slándáil agus príobháideacht. Ina theannta sin, is féidir leis seo an próiseas clárúcháin a dhéanamh i bhfad níos lú inrochtana ag roinnt daoine (go háirithe faoi mhíchumas). Ar na cúiseanna seo, smaoinigh le do thoil ar bhearta eile amhail clárú bunaithe ar fhormheas nó ar chuireadh. + title: A cheangal ar úsáideoirí nua CAPTCHA a réiteach chun a gcuntas a dhearbhú + content_retention: + danger_zone: Crios contúirte + preamble: Rialú conas a stóráiltear ábhar a ghintear ag úsáideoirí i Mastodon. + title: Coinneáil ábhair default_noindex: desc_html: I bhfeidhm do ghach úsáideoir nár athraigh an socrú seo iad féin title: Diúltaigh d'innéacsú inneall cuardaigh mar réamhshocrú d'úsáideoirí + discovery: + follow_recommendations: Lean na moltaí + preamble: Tá sé ríthábhachtach dromchla a chur ar ábhar suimiúil chun úsáideoirí nua a chur ar bord nach bhfuil aithne acu ar dhuine ar bith Mastodon. Rialú conas a oibríonn gnéithe fionnachtana éagsúla ar do fhreastalaí. + profile_directory: Eolaire próifíle + public_timelines: Amlínte poiblí + publish_discovered_servers: Foilsigh freastalaithe aimsithe + publish_statistics: Staitisticí a fhoilsiú + title: Fionnachtain + trends: Treochtaí + domain_blocks: + all: Do chách + disabled: Do dhuine ar bith + users: Chun úsáideoirí áitiúla logáilte isteach registrations: + moderation_recommandation: Cinntigh le do thoil go bhfuil foireann mhodhnóireachta imoibríoch leordhóthanach agat sula n-osclaíonn tú clárúcháin do gach duine! + preamble: Rialú cé atá in ann cuntas a chruthú ar do fhreastalaí. title: Clárúcháin + registrations_mode: + modes: + approved: Teastaíonn ceadú le clárú + none: Ní féidir le duine ar bith clárú + open: Is féidir le duine ar bith clárú + warning_hint: Molaimid úsáid a bhaint as “Faomhadh riachtanach chun clárú” ach amháin má tá tú muiníneach gur féidir le d’fhoireann mhodhnóireachta clárú turscair agus mailíseach a láimhseáil go tráthúil. + security: + authorized_fetch: Teastaíonn fíordheimhniú ó fhreastalaithe cónasctha + authorized_fetch_hint: Toisc go n-éilítear fíordheimhniú ó fhreastalaithe cónasctha, is féidir bloic ar leibhéal an úsáideora agus ar leibhéal an fhreastalaí araon a fhorfheidhmiú níos déine. Mar sin féin, tagann sé seo ar chostas pionós feidhmíochta, laghdaítear an teacht ar do chuid freagraí, agus d'fhéadfadh saincheisteanna comhoiriúnachta a thabhairt isteach le roinnt seirbhísí cónasctha. Ina theannta sin, ní chuirfidh sé seo cosc ​​ar aisteoirí tiomnaithe do phoist phoiblí agus do chuntais phoiblí a fháil. + authorized_fetch_overridden_hint: Ní féidir leat an socrú seo a athrú faoi láthair toisc go bhfuil sé sáraithe ag athróg timpeallachta. + federation_authentication: Forghníomhú fíordheimhnithe Cónaidhm + title: Socruithe freastalaí site_uploads: delete: Scrios comhad uaslódáilte + destroyed_msg: D'éirigh le huaslódáil an tsuímh a scriosadh! + software_updates: + critical_update: Criticiúil - nuashonraigh go tapa le do thoil + description: Moltar do shuiteáil Mastodon a choinneáil cothrom le dáta chun leas a bhaint as na socruithe agus na gnéithe is déanaí. Ina theannta sin, tá sé ríthábhachtach uaireanta Mastodon a nuashonrú go tráthúil chun saincheisteanna slándála a sheachaint. Ar na cúiseanna seo, seiceálann Mastodon nuashonruithe gach 30 nóiméad, agus tabharfaidh sé fógra duit de réir do shainroghanna fógra ríomhphoist. + documentation_link: Foghlaim níos mó + release_notes: Nótaí scaoilte + title: Nuashonruithe ar fáil + type: Cineál + types: + major: Mórscaoileadh + minor: Mionscaoileadh + patch: Scaoileadh paiste - ceartúcháin agus athruithe atá éasca a chur i bhfeidhm + version: Leagan statuses: account: Údar + application: Iarratas back_to_account: Ar ais go leathanach cuntais + back_to_report: Ar ais go leathanach tuairisce + batch: + remove_from_report: Bain den tuairisc + report: Tuairisc deleted: Scriosta favourites: Toghanna + history: Stair leagan + in_reply_to: Ag freagairt do language: Teanga media: title: Meáin metadata: Meiteashonraí + no_status_selected: Níor athraíodh aon phostáil mar níor roghnaíodh ceann ar bith open: Oscail postáil original_status: Bunphostáil reblogs: Athbhlaganna status_changed: Athraíodh postáil + title: Poist chuntais trending: Ag treochtáil + visibility: Infheictheacht with_media: Le meáin strikes: actions: delete_statuses: Scrios %{name} postálacha de chuid %{target} + disable: Reoite %{name} cuntas %{target} + mark_statuses_as_sensitive: Mharcáil %{name} postálacha %{target} mar íogair + none: Sheol %{name} rabhadh chuig %{target} + sensitive: Mharcáil %{name} cuntas %{target} mar íogair + silence: Chuir %{name} teorainn le cuntas %{target} + suspend: Chuir %{name} cuntas %{target} ar fionraí + appeal_approved: Achomharc + appeal_pending: Achomharc ar feitheamh + appeal_rejected: Diúltaíodh don achomharc + system_checks: + database_schema_check: + message_html: Tá aistrithe bunachar sonraí ar feitheamh. Rith iad le do thoil chun a chinntiú go n-iompraíonn an t-iarratas mar a bhíothas ag súil leis + elasticsearch_health_red: + message_html: Tá braisle Elasticsearch míshláintiúil (stádas dearg), níl gnéithe cuardaigh ar fáil + elasticsearch_health_yellow: + message_html: Tá braisle Elasticsearch míshláintiúil (stádas buí), b'fhéidir gur mhaith leat an chúis a fhiosrú + elasticsearch_index_mismatch: + message_html: Tá mapálacha innéacs Elasticsearch as dáta. Rith tootctl search deploy --only=%{value} le do thoil + elasticsearch_preset: + action: Féach doiciméadú + message_html: Tá níos mó ná nód amháin ag do bhraisle Elasticsearch, ach níl Mastodon cumraithe chun iad a úsáid. + elasticsearch_preset_single_node: + action: Féach doiciméadú + message_html: Níl ach nód amháin ag do bhraisle Elasticsearch, ba cheart ES_PRESET a shocrú go single_node_cluster. + elasticsearch_reset_chewy: + message_html: Tá d'innéacs córais Elasticsearch as dáta mar gheall ar athrú socruithe. Rith tootctl search deploy --reset-chewy chun é a nuashonrú. + elasticsearch_running_check: + message_html: Níorbh fhéidir ceangal le Elasticsearch. Cinntigh go bhfuil sé ar siúl, nó díchumasaigh cuardach téacs iomlán + elasticsearch_version_check: + message_html: 'Leagan neamh-chomhoiriúnach le Elasticsearch: %{value}' + version_comparison: Tá Elasticsearch %{running_version} ag rith agus %{required_version} ag teastáil + rules_check: + action: Bainistigh rialacha freastalaí + message_html: Níl aon rialacha freastalaí sainmhínithe agat. + sidekiq_process_check: + message_html: Níl próiseas Sidekiq ag rith don scuaine/(scuainí) %{value}. Déan athbhreithniú ar do chumraíocht Sidekiq + software_version_critical_check: + action: Féach nuashonruithe atá ar fáil + message_html: Tá nuashonrú ríthábhachtach Mastodon ar fáil, nuashonraigh chomh tapa agus is féidir le do thoil. + software_version_patch_check: + action: Féach nuashonruithe atá ar fáil + message_html: Tá nuashonrú bugfix Mastodon ar fáil. + upload_check_privacy_error: + action: Seiceáil anseo le haghaidh tuilleadh eolais + message_html: "Tá do fhreastalaí gréasáin míchumraithe. Tá príobháideacht d'úsáideoirí i mbaol." + upload_check_privacy_error_object_storage: + action: Seiceáil anseo le haghaidh tuilleadh eolais + message_html: "Tá do stór oibiachtaí míchumraithe. Tá príobháideacht d'úsáideoirí i mbaol." tags: review: Stádas athbhreithnithe + updated_msg: D'éirigh le socruithe hashtag a nuashonrú title: Riar trends: allow: Ceadaigh + approved: Ceadaithe disallow: Dícheadaigh + links: + allow: Ceadaigh nasc + allow_provider: Ceadaigh foilsitheoir + description_html: Is naisc iad seo atá á roinnt go mór faoi láthair ag cuntais a bhfeiceann do fhreastalaí postálacha uathu. Is féidir leis cabhrú le d’úsáideoirí a fháil amach cad atá ar siúl ar fud an domhain. Ní thaispeánfar naisc ar bith go poiblí go dtí go gceadaíonn tú an foilsitheoir. Is féidir leat naisc aonair a cheadú nó a dhiúltú freisin. + disallow: Nasc a dhícheadú + disallow_provider: Dícheadaigh foilsitheoir + no_link_selected: Níor athraíodh aon nasc mar níor roghnaíodh ceann ar bith + publishers: + no_publisher_selected: Níor athraíodh aon fhoilsitheoir mar níor roghnaíodh ceann ar bith + shared_by_over_week: + few: Roinnte ag %{count} duine le seachtain anuas + many: Roinnte ag %{count} duine le seachtain anuas + one: Roinnte ag duine amháin le seachtain anuas + other: Roinnte ag %{count} duine le seachtain anuas + two: Roinnte ag %{count} duine le seachtain anuas + title: Naisc treochta + usage_comparison: Roinnte %{today} uair inniu, i gcomparáid le %{yesterday} inné + not_allowed_to_trend: Ní cheadaítear treocht + only_allowed: Ní cheadaítear ach + pending_review: Athbhreithniú ar feitheamh preview_card_providers: + allowed: Is féidir le naisc ón bhfoilsitheoir seo treocht + description_html: Is fearainn iad seo óna roinntear naisc go minic ar do fhreastalaí. Ní threochtóidh naisc go poiblí mura gceadaítear fearann ​​an naisc. Síneann do cheadú (nó diúltú) chuig fofhearainn. + rejected: Ní bheidh treocht ag naisc ón bhfoilsitheoir seo title: Foilsitheoirí rejected: Diúltaithe statuses: allow: Ceadaigh postáil allow_account: Ceadaigh údar + description_html: Is postálacha iad seo a bhfuil do fhreastalaí ar an eolas fúthu agus atá á roinnt faoi láthair agus is fearr leat go mór faoi láthair. Is féidir leis cabhrú le d’úsáideoirí nua agus úsáideoirí atá ag filleadh níos mó daoine a aimsiú le leanúint. Ní thaispeánfar postálacha ar bith go poiblí go dtí go gceadaíonn tú an t-údar, agus ceadaíonn an t-údar a gcuntas a mholadh do dhaoine eile. Is féidir leat postálacha aonair a cheadú nó a dhiúltú freisin. + disallow: Dícheadaigh postáil + disallow_account: Údar a dhícheadú + no_status_selected: Níor athraíodh aon phostáil treochta mar níor roghnaíodh ceann ar bith + not_discoverable: Níor roghnaigh an t-údar a bheith in-aimsithe + shared_by: + few: Roinnte agus ansa leat %{friendly_count} uair + many: Roinnte agus ansa leat %{friendly_count} uair + one: Roinnte nó is fearr leat am amháin + other: Roinnte agus ansa leat %{friendly_count} uair + two: Roinnte agus ansa leat %{friendly_count} uair + title: Poist trending + tags: + current_score: Scór reatha %{score} + dashboard: + tag_accounts_measure: úsáidí uathúla + tag_languages_dimension: Barrtheangacha + tag_servers_dimension: Barrtheangacha + tag_servers_measure: freastalaithe éagsúla + tag_uses_measure: úsáidí iomlána + description_html: Is hashtags iad seo atá le feiceáil faoi láthair i go leor postálacha a fheiceann do fhreastalaí. Is féidir leis cabhrú le d’úsáideoirí a fháil amach cad é is mó atá ag caint faoi dhaoine faoi láthair. Ní thaispeánfar aon hashtags go poiblí go dtí go gceadaíonn tú iad. + listable: Is féidir a mholadh + no_tag_selected: Níor athraíodh aon chlib mar níor roghnaíodh ceann ar bith + not_listable: Ní mholfar + not_trendable: Ní bheidh le feiceáil faoi threochtaí + not_usable: Ní féidir é a úsáid + peaked_on_and_decaying: Buaicphointe ar %{date}, ag lobhadh anois + title: Haischlib treochta + trendable: Is féidir le feiceáil faoi threochtaí + trending_rank: 'Ag treochtáil # %{rank}' + usable: Is féidir é a úsáid + usage_comparison: Úsáidte %{today} uair inniu, i gcomparáid le %{yesterday} inné + used_by_over_week: + few: Úsáidte ag %{count} duine le seachtain anuas + many: Úsáidte ag %{count} duine le seachtain anuas + one: Úsáidte ag duine amháin le seachtain anuas + other: Úsáidte ag %{count} duine le seachtain anuas + two: Úsáidte ag %{count} duine le seachtain anuas + title: Treochtaí + trending: Treocht warning_presets: + add_new: Cuir nua leis delete: Scrios + edit_preset: Cuir réamhshocrú rabhaidh in eagar + empty: Níl aon réamhshocruithe rabhaidh sainithe agat fós. + title: Réamhshocruithe rabhaidh webhooks: + add_new: Cuir críochphointe leis delete: Scrios + description_html: Cuireann cuadóg gréasáin ar chumas Mastodon fógraí fíor-ama faoi imeachtaí roghnaithe a bhrú le d'iarratas féin, ionas gur féidir le d'iarratas imoibrithe a spreagadh go huathoibríoch. disable: Díchumasaigh disabled: Díchumasaithe + edit: Cuir críochphointe in eagar + empty: Níl aon chríochphointí cuaille gréasáin agat fós. enable: Cumasaigh enabled: Gníomhach + enabled_events: + few: "%{count} imeacht cumasaithe" + many: "%{count} imeacht cumasaithe" + one: 1 imeacht cumasaithe + other: "%{count} imeacht cumasaithe" + two: "%{count} imeacht cumasaithe" events: Eachtraí + new: Cuaille gréasáin nua + rotate_secret: Rothlaigh an rún + secret: Rún a shíniú status: Stádas + title: Crúcaí gréasáin + webhook: Crúca gréasáin admin_mailer: + auto_close_registrations: + body: De bharr easpa gníomhaíochta modhnóra le déanaí, aistríodh clárúcháin ar %{instance} go huathoibríoch chuig athbhreithniú de láimh chun nach n-úsáidfear %{instance} mar ardán do dhrochghníomhaithe féideartha. Is féidir leat é a athrú ar ais chuig clárúcháin a oscailt am ar bith. + subject: Athraíodh clárúcháin le haghaidh %{instance} go huathoibríoch chuig a dteastaíonn ceadú uathu new_appeal: actions: delete_statuses: a gcuid postálacha a scrios + disable: a gcuntas a reo + mark_statuses_as_sensitive: a bpoist a mharcáil mar íogair none: rabhadh + sensitive: a gcuntas a mharcáil mar íogair + silence: a gcuntas a theorannú + suspend: a gcuntas a chur ar fionraí + body: 'Tá %{target} ag achomharc in aghaidh cinneadh modhnóireachta ó %{action_taken_by} ó %{date}, arbh é %{type} é. Scríobh siad:' + next_steps: Is féidir leat an t-achomharc a cheadú chun an cinneadh modhnóireachta a chealú, nó neamhaird a dhéanamh air. + subject: Tá %{username} ag achomharc in aghaidh cinneadh modhnóireachta ar %{instance} + new_critical_software_updates: + body: Tá leaganacha criticiúla nua de Mastodon eisithe, b'fhéidir gur mhaith leat a nuashonrú chomh luath agus is féidir! + subject: Tá nuashonruithe Critical Mastodon ar fáil do %{instance}! + new_pending_account: + body: Tá sonraí an chuntais nua thíos. Is féidir leat an t-iarratas seo a cheadú nó a dhiúltú. + subject: Cuntas nua le léirmheas ar %{instance} (%{username}) + new_report: + body: Thuairiscigh %{reporter} %{target} + body_remote: Thuairiscigh duine éigin ó %{domain} %{target} + subject: Tuairisc nua do %{instance} (#%{id}) + new_software_updates: + body: Tá leaganacha nua Mastodon eisithe, b'fhéidir gur mhaith leat a nuashonrú! + subject: Tá leaganacha nua Mastodon ar fáil do %{instance}! + new_trends: + body: 'Is gá na míreanna seo a leanas a athbhreithniú sular féidir iad a thaispeáint go poiblí:' + new_trending_links: + title: Naisc treochta + new_trending_statuses: + title: Poist trending + new_trending_tags: + title: Hashtags treochta + subject: Treochtaí nua le hathbhreithniú ar %{instance} + aliases: + add_new: Cruthaigh ailias + created_msg: D'éirigh le hailias nua a chruthú. Is féidir leat an t-aistriú ón seanchuntas a thionscnamh anois. + deleted_msg: D'éirigh leis an ailias a bhaint. Ní bheidh sé indéanta a thuilleadh bogadh ón gcuntas sin chuig an gceann seo. + empty: Níl aon ailiasanna agat. + hint_html: Más mian leat bogadh ó chuntas eile go dtí an ceann seo, anseo is féidir leat ailias a chruthú, a theastaíonn sular féidir leat leanúint ar aghaidh le leantóirí a bhogadh ón seanchuntas go dtí an ceann seo. Tá an gníomh seo ann féin neamhdhíobhálach agus inchúlaithe. Cuirtear tús leis an aistriú cuntais ón seanchuntas. + remove: Dícheangail ailias + appearance: + advanced_web_interface: Comhéadan gréasáin chun cinn + advanced_web_interface_hint: 'Más mian leat úsáid a bhaint as do leithead scáileáin ar fad, ceadaíonn an comhéadan gréasáin ardleibhéil duit go leor colúin éagsúla a chumrú chun an oiread faisnéise a fheiceáil ag an am céanna agus is mian leat: Baile, fógraí, amlíne chónaidhme, aon líon liostaí agus hashtags.' + animations_and_accessibility: Beochan agus inrochtaineacht + confirmation_dialogs: Dialóga deimhnithe + discovery: Fionnachtain + localization: + body: Aistríonn oibrithe deonacha Mastodon. + guide_link: https://crowdin.com/project/mastodon + guide_link_text: Is féidir le gach duine rannchuidiú. + sensitive_content: Ábhar íogair + application_mailer: + notification_preferences: Athraigh roghanna ríomhphoist + salutation: "%{name}," + settings: 'Athraigh sainroghanna ríomhphoist: %{link}' + unsubscribe: Díliostáil + view: 'Amharc:' + view_profile: Féach ar phróifíl + view_status: Féach ar phostáil + applications: + created: D'éirigh leis an bhfeidhmchlár a chruthú + destroyed: D'éirigh leis an bhfeidhmchlár a scriosadh + logout: Logáil Amach + regenerate_token: Athghin comhartha rochtana + token_regenerated: D'éirigh le hathghiniúint an comhartha rochtana + warning: Bí an-chúramach leis na sonraí seo. Ná roinn é le haon duine riamh! + your_token: Do chomhartha rochtana auth: + apply_for_account: Iarr cuntas + captcha_confirmation: + help_html: Má tá fadhbanna agat ag réiteach an CAPTCHA, is féidir leat dul i dteagmháil linn trí %{email} agus is féidir linn cabhrú leat. + hint_html: Ach rud amháin eile! Ní mór dúinn a dhearbhú gur duine daonna thú (tá sé seo ionas gur féidir linn an turscar a choinneáil amach!). Réitigh an CAPTCHA thíos agus cliceáil "Ar aghaidh". + title: Seiceáil slándála + confirmations: + awaiting_review: Tá do sheoladh r-phoist deimhnithe! Tá do chlárúchán á athbhreithniú ag foireann %{domain} anois. Gheobhaidh tú r-phost má fhaomhann siad do chuntas! + awaiting_review_title: Tá do chlárú á athbhreithniú + clicking_this_link: ag cliceáil ar an nasc seo + login_link: logáil isteach + proceed_to_login_html: Is féidir leat dul ar aghaidh chuig %{login_link} anois. + redirect_to_app_html: Ba cheart go ndearnadh tú a atreorú chuig an aip %{app_name}. Murar tharla sin, bain triail as %{clicking_this_link} nó fill ar an aip de láimh. + registration_complete: Tá do chlárú ar %{domain} críochnaithe anois! + welcome_title: Fáilte, %{name}! + wrong_email_hint: Mura bhfuil an seoladh ríomhphoist sin ceart, is féidir leat é a athrú i socruithe cuntais. delete_account: Scrios cuntas + delete_account_html: Más mian leat do chuntas a scriosadh, is féidir leat leanúint ar aghaidh anseo. Iarrfar ort deimhniú. + description: + prefix_invited_by_user: Tugann @%{name} cuireadh duit páirt a ghlacadh sa fhreastalaí seo de Mastodon! + prefix_sign_up: Cláraigh ar Mastodon inniu! + suffix: Le cuntas, beidh tú in ann daoine a leanúint, nuashonruithe a phostáil agus teachtaireachtaí a mhalartú le húsáideoirí ó aon fhreastalaí Mastodon agus níos mó! + didnt_get_confirmation: Nach bhfuair tú nasc deimhnithe? + dont_have_your_security_key: Nach bhfuil d'eochair shlándála agat? + forgot_password: Ar rinne tú dearmad ar do Phásfhocail? + invalid_reset_password_token: Tá comhartha athshocraithe pasfhocail neamhbhailí nó imithe in éag. Iarr ceann nua le do thoil. + link_to_otp: Cuir isteach cód dhá fhachtóir ó do ghuthán nó cód aisghabhála + link_to_webauth: Úsáid d'eochair shlándála gléas + log_in_with: Logáil isteach le login: Logáil isteach logout: Logáil Amach + migrate_account: Bog chuig cuntas eile + migrate_account_html: Más mian leat an cuntas seo a atreorú chuig ceann eile, is féidir leat é a chumrú anseo. or_log_in_with: Nó logáil isteach le + privacy_policy_agreement_html: Léigh mé agus aontaím leis an polasaí príobháideachais + progress: + confirm: Deimhnigh Ríomhphost + details: Do chuid sonraí + review: Ár léirmheas + rules: Glac le rialacha + providers: + cas: CAS + saml: SAML register: Clárú + registration_closed: Níl %{instance} ag glacadh le baill nua + resend_confirmation: Seol an nasc deimhnithe arís + reset_password: Athshocraigh pasfhocal + rules: + accept: Glac + back: Ar ais + invited_by: 'Is féidir leat páirt a ghlacadh i %{domain} a bhuíochas leis an gcuireadh a fuair tú ó:' + preamble: Socraíonn agus cuireann na modhnóirí %{domain} iad seo i bhfeidhm. + preamble_invited: Sula dtéann tú ar aghaidh, smaoinigh le do thoil ar na bunrialacha atá socraithe ag modhnóirí %{domain}. + title: Roinnt bunrialacha. + title_invited: Tá cuireadh faighte agat. security: Slándáil + set_new_password: Socraigh pasfhocal nua + setup: + email_below_hint_html: Seiceáil d'fhillteán turscair, nó iarr ceann eile. Is féidir leat do sheoladh r-phoist a cheartú má tá sé mícheart. + email_settings_hint_html: Cliceáil ar an nasc a sheol muid chugat chun %{email} a fhíorú. Beidh muid ag fanacht ar dheis anseo. + link_not_received: Nach bhfuair tú nasc? + new_confirmation_instructions_sent: Gheobhaidh tú r-phost nua leis an nasc deimhnithe i gceann cúpla bomaite! + title: Seiceáil do bhosca isteach + sign_in: + preamble_html: Logáil isteach le do dhintiúir %{domain}. Má tá do chuntas á óstáil ar fhreastalaí eile, ní bheidh tú in ann logáil isteach anseo. + title: Logáil isteach go %{domain} + sign_up: + manual_review: Téann clárúcháin ar %{domain} trí athbhreithniú láimhe ag ár modhnóirí. Chun cabhrú linn do chlárúchán a phróiseáil, scríobh beagán fút féin agus cén fáth a bhfuil cuntas uait ar %{domain}. + preamble: Le cuntas ar an bhfreastalaí Mastodon seo, beidh tú in ann aon duine eile ar an líonra a leanúint, beag beann ar an áit a bhfuil a gcuntas á óstáil. + title: Déanaimis tú a shocrú ar %{domain}. status: account_status: Stádas cuntais + confirming: Ag fanacht le deimhniú r-phoist a bheith críochnaithe. + functional: Tá do chuntas ag feidhmiú go hiomlán. + pending: Tá d’iarratas ar feitheamh athbhreithnithe ag ár bhfoireann. Seans go dtógfaidh sé seo roinnt ama. Gheobhaidh tú ríomhphost má cheadaítear d’iarratas. + redirecting_to: Tá do chuntas neamhghníomhach toisc go bhfuil sé á atreorú chuig %{acct} faoi láthair. + self_destruct: Toisc go bhfuil %{domain} ag dúnadh síos, ní bhfaighidh tú ach rochtain theoranta ar do chuntas. + view_strikes: Féach ar stailceanna san am atá caite i gcoinne do chuntais too_fast: Cuireadh an fhoirm isteach róthapa, triail arís. + use_security_key: Úsáid eochair shlándála challenge: confirm: Lean ar aghaidh + hint_html: "Leid: Ní iarrfaimid do phasfhocal ort arís go ceann uair an chloig eile." + invalid_password: Pasfhocal neamhbhailí + prompt: Deimhnigh an pasfhocal chun leanúint ar aghaidh + crypto: + errors: + invalid_key: nach eochair bhailí Ed25519 nó Curve25519 í + invalid_signature: nach síniú bailí Ed25519 é + date: + formats: + default: "%b %d, %Y" + with_month_name: "%B %d, %Y" datetime: distance_in_words: about_x_hours: "%{count}u" @@ -340,6 +1201,7 @@ ga: about_x_years: "%{count}b" almost_x_years: "%{count}b" half_a_minute: Díreach anois + less_than_x_minutes: "%{count}m" less_than_x_seconds: Díreach anois over_x_years: "%{count}b" x_days: "%{count}l" @@ -347,45 +1209,243 @@ ga: x_months: "%{count}m" x_seconds: "%{count}s" deletes: + challenge_not_passed: Ní raibh an fhaisnéis a d'iontráil tú ceart + confirm_password: Cuir isteach do phasfhocal reatha chun d'aitheantas a fhíorú + confirm_username: Cuir isteach d'ainm úsáideora chun an nós imeachta a dhearbhú proceed: Scrios cuntas + success_msg: Scriosadh do chuntas go rathúil + warning: + before: 'Sula dtéann tú ar aghaidh, léigh na nótaí seo go cúramach le do thoil:' + caches: Seans go seasfaidh inneachar atá i dtaisce ag freastalaithe eile + data_removal: Bainfear do phostálacha agus sonraí eile go buan + email_change_html: Is féidir leat do sheoladh ríomhphoist a athrú gan do chuntas a scriosadh + email_contact_html: Mura dtagann sé fós, is féidir leat ríomhphost a chur chuig %{email} chun cabhair a fháil + email_reconfirmation_html: Mura bhfuil an ríomhphost deimhnithe á fháil agat, is féidir é a iarraidh arís + irreversible: Ní bheidh tú in ann do chuntas a aischur nó a athghníomhachtú + more_details_html: Le haghaidh tuilleadh sonraí, féach an polasaí príobháideachais. + username_available: Cuirfear d'ainm úsáideora ar fáil arís + username_unavailable: Ní bheidh d'ainm úsáideora ar fáil fós disputes: strikes: + action_taken: Gníomh déanta + appeal: Achomharc + appeal_approved: D’éirigh le hachomharc a dhéanamh ar an stailc seo agus níl sé bailí a thuilleadh + appeal_rejected: Diúltaíodh don achomharc appeal_submitted_at: Achomharc curtha isteach appealed_msg: Cuireadh isteach d'achomharc. Má ceadófar é, cuirfear ar an eolas tú. appeals: submit: Cuir achomharc isteach + approve_appeal: Achomharc a cheadú + associated_report: Tuarascáil ghaolmhar + created_at: Dátaithe + description_html: Is gníomhartha iad seo a rinneadh i gcoinne do chuntais agus rabhaidh a sheol foireann %{instance} chugat. + recipient: Seolta chuig + reject_appeal: Diúltú achomharc + status: 'Postáil # %{id}' + status_removed: Baineadh an postáil den chóras cheana féin + title: "%{action} ó %{date}" title_actions: + delete_statuses: Post a bhaint + disable: Reo cuntais + mark_statuses_as_sensitive: Postálacha a mharcáil mar íogair none: Rabhadh + sensitive: An cuntas a mharcáil mar íogair + silence: Teorainn le cuntas + suspend: Cuntas a fhionraí + your_appeal_approved: Tá d’achomharc ceadaithe your_appeal_pending: Chuir tú achomharc isteach + your_appeal_rejected: Diúltaíodh do d'achomharc + domain_validator: + invalid_domain: nach ainm fearainn bailí é + edit_profile: + basic_information: Eolas bunúsach + hint_html: "Saincheap a bhfeiceann daoine ar do phróifíl phoiblí agus in aice le do phostálacha. Is dóichí go leanfaidh daoine eile ar ais tú agus go n-idirghníomhóidh siad leat nuair a bhíonn próifíl líonta agus pictiúr próifíle agat." + other: Eile + errors: + '400': Bhí an t-iarratas a chuir tú isteach neamhbhailí nó míchumtha. + '403': Níl cead agat an leathanach seo a fheiceáil. + '404': Níl an leathanach atá uait anseo. + '406': Níl an leathanach seo ar fáil san fhormáid iarrtha. + '410': Níl an leathanach a bhí á lorg agat ann a thuilleadh. + '422': + content: Theip ar fhíorú slándála. An bhfuil tú ag cur bac ar fhianáin? + title: Theip ar fhíorú slándála + '429': An iomarca iarratas + '500': + content: Tá brón orainn, ach chuaigh rud éigin mícheart ar ár deireadh. + title: Níl an leathanach seo ceart + '503': Níorbh fhéidir an leathanach a sheirbheáil mar gheall ar theip shealadach ar an bhfreastalaí. + noscript_html: Chun feidhmchlár gréasáin Mastodon a úsáid, cumasaigh JavaScript le do thoil. Nó, bain triail as ceann de na aipeanna dúchasacha do Mastodon do d'ardán. + existing_username_validator: + not_found: níorbh fhéidir úsáideoir áitiúil a aimsiú leis an ainm úsáideora sin + not_found_multiple: níorbh fhéidir %{usernames} a aimsiú exports: archive_takeout: date: Dáta + download: Íosluchtaigh cartlann do rang + hint_html: Is féidir leat cartlann de do postálacha agus meáin uaslódáilte a iarraidh. Beidh na sonraí easpórtáilte i bhformáid ActivityPub, inléite ag aon bhogearraí comhlíontacha. Is féidir leat cartlann a iarraidh gach 7 lá. + in_progress: Do chartlann á tiomsú... + request: Iarr do chartlann size: Méid + blocks: Bac leat + bookmarks: Leabharmharcanna + csv: CSV + domain_blocks: Bloic fearainn lists: Liostaí + mutes: Balbhaíonn tú + storage: Stóráil meáin + featured_tags: + add_new: Cuir nua leis + errors: + limit: Tá uaslíon na hashtags le feiceáil agat cheana féin + hint_html: "Áirigh na haischlibeanna is tábhachtaí ar do phróifíl. Uirlis iontach chun súil a choinneáil ar do shaothair chruthaitheacha agus do thionscadail fhadtéarmacha, taispeántar haischlibeanna faoi thrácht in áit fheiceálach ar do phróifíl agus ceadaíonn siad rochtain thapa ar do phostálacha féin." filters: contexts: + account: Próifílí home: Baile agus liostaí notifications: Fógraí + public: Amlínte poiblí thread: Comhráite edit: add_keyword: Cruthaigh eochairfhocal keywords: Eochairfhocal + statuses: Poist aonair + statuses_hint_html: Baineann an scagaire seo le postálacha aonair a roghnú is cuma má mheaitseálann siad leis na heochairfhocail thíos. Déan postálacha a athbhreithniú nó a bhaint den scagaire. + title: Cuir an scagaire in eagar + errors: + deprecated_api_multiple_keywords: Ní féidir na paraiméadair seo a athrú ón bhfeidhmchlár seo toisc go mbaineann siad le níos mó ná eochairfhocal scagaire amháin. Úsáid feidhmchlár níos déanaí nó an comhéadan gréasáin. + invalid_context: Níor soláthraíodh aon cheann nó comhthéacs neamhbhailí index: + contexts: Scagairí i %{contexts} delete: Scrios empty: Níl aon scagairí agat. + expires_in: In éag i %{distance} + expires_on: Rachaidh sé in éag ar %{date} + keywords: + few: "%{count} eochairfhocal" + many: "%{count} eochairfhocal" + one: "%{count} eochairfhocal" + other: "%{count} eochairfhocal" + two: "%{count} eochairfhocal" + statuses: + few: "%{count} postáil" + many: "%{count} postáil" + one: "%{count} postáil" + other: "%{count} postáil" + two: "%{count} postáil" + statuses_long: + few: "%{count} postáil aonair folaithe" + many: "%{count} postáil aonair folaithe" + one: "%{count} postáil aonair i bhfolach" + other: "%{count} postáil aonair folaithe" + two: "%{count} postáil aonair folaithe" title: Scagairí + new: + save: Sábháil scagaire nua + title: Cuir scagaire nua leis + statuses: + back_to_filter: Ar ais go dtí an scagaire + batch: + remove: Bain as an scagaire + index: + hint: Baineann an scagaire seo le poist aonair a roghnú beag beann ar chritéir eile. Is féidir leat tuilleadh postálacha a chur leis an scagaire seo ón gcomhéadan gréasáin. + title: Postálacha scagtha generic: + all: Gach + all_items_on_page_selected_html: + few: Tá gach %{count} mír ar an leathanach seo roghnaithe. + many: Tá gach %{count} mír ar an leathanach seo roghnaithe. + one: Tá %{count} mír ar an leathanach seo roghnaithe. + other: Tá gach %{count} mír ar an leathanach seo roghnaithe. + two: Tá gach %{count} mír ar an leathanach seo roghnaithe. + all_matching_items_selected_html: + few: Tá gach %{count} mír a thagann le do chuardach roghnaithe. + many: Tá gach %{count} mír a thagann le do chuardach roghnaithe. + one: Tá %{count} mír a thagann le do chuardach roghnaithe. + other: Tá gach %{count} mír a thagann le do chuardach roghnaithe. + two: Tá gach %{count} mír a thagann le do chuardach roghnaithe. + cancel: Cealaigh + changes_saved_msg: Sábháladh na hathruithe! + confirm: Deimhnigh copy: Cóipeáil delete: Scrios + deselect: Díroghnaigh go léir + none: Dada + order_by: Ordú le + save_changes: Sabháil na hathruithe + select_all_matching_items: + few: Roghnaigh gach %{count} mír a thagann le do chuardach. + many: Roghnaigh gach %{count} mír a thagann le do chuardach. + one: Roghnaigh %{count} mír a thagann le do chuardach. + other: Roghnaigh gach %{count} mír a thagann le do chuardach. + two: Roghnaigh gach %{count} mír a thagann le do chuardach. today: inniu + validation_errors: + few: Níl rud éigin ceart go leor fós! Déan athbhreithniú ar %{count} earráid thíos + many: Níl rud éigin ceart go leor fós! Déan athbhreithniú ar %{count} earráid thíos + one: Níl rud éigin ceart go leor fós! Déan athbhreithniú ar an earráid thíos + other: Níl rud éigin ceart go leor fós! Déan athbhreithniú ar %{count} earráid thíos + two: Níl rud éigin ceart go leor fós! Déan athbhreithniú ar %{count} earráid thíos imports: + errors: + empty: Comhad CSV folamh + incompatible_type: Neamh-chomhoiriúnach leis an gcineál iompórtála roghnaithe + invalid_csv_file: 'Comhad CSV neamhbhailí. Earráid: %{error}' + over_rows_processing_limit: níos mó ná %{count} sraitheanna + too_large: Tá an comhad ró-mhór + failures: Teipeanna + imported: Iompórtáilte + mismatched_types_warning: Is cosúil gur roghnaigh tú an cineál mícheart don iompórtáil seo, seiceáil arís le do thoil. modes: merge: Cumaisc + merge_long: Coinnigh taifid atá ann cheana féin agus cuir cinn nua leis overwrite: Forscríobh + overwrite_long: Cuir na cinn nua in ionad na dtaifead reatha + overwrite_preambles: + blocking_html: Tá tú ar tí do liosta bloc a chur in ionad suas le %{total_items} cuntas ó %{filename}. + bookmarks_html: Tá tú ar tí do leabharmharcanna a chur in ionad suas le %{total_items} postáil ó %{filename}. + domain_blocking_html: Tá tú ar tí do liosta bloc fearainn a chur in ionad suas le %{total_items} fearainn ó %{filename}. + following_html: Tá tú ar tí leanúint suas go dtí %{total_items} cuntas ó %{filename} agus stop a leanúint aon duine eile. + lists_html: Tá tú ar tí do liostaí a chur in ionad inneachair %{filename}. Cuirfear suas le %{total_items} cuntas le liostaí nua. + muting_html: Tá tú ar tí do liosta cuntas balbhaithe a chur in ionad suas le %{total_items} cuntas ó %{filename}. + preambles: + blocking_html: Tá tú ar tí bloc suas le %{total_items} cuntas ó %{filename}. + bookmarks_html: Tá tú ar tí %{total_items} postáil ó %{filename} a chur le do leabharmharcanna. + domain_blocking_html: Tá tú ar tí bloc suas le %{total_items} fearainn ó %{filename}. + following_html: Tá tú ar tí leanúint suas go dtí %{total_items} cuntas ó %{filename}. + lists_html: Tá tú ar tí %{total_items} cuntas ó %{filename} a chur le do liostaí. Cruthófar liostaí nua mura bhfuil aon liosta le cur leis. + muting_html: Tá tú ar tí balbhú suas le %{total_items} cuntas ó %{filename}. + preface: Is féidir leat sonraí a d’easpórtáil tú a allmhairiú ó fhreastalaí eile, mar shampla liosta de na daoine a bhfuil tú ag leanúint nó ag cur bac orthu. + recent_imports: Allmhairí le déanaí + states: + finished: Críochnaithe + in_progress: Ar siúl + scheduled: Sceidealta + unconfirmed: Neamhdhearbhaithe + status: Stádas + success: Uaslódáladh do shonraí go rathúil agus próiseálfar iad in am trátha + time_started: Thosaigh ag + titles: + blocking: Cuntais bhlocáilte á n-iompórtáil + bookmarks: Leabharmharcanna á n-iompórtáil + domain_blocking: Fearainn blocáilte á n-iompórtáil + following: Cuntais leanta á n-iompórtáil + lists: Liostaí á n-iompórtáil + muting: Cuntais bhalbhaithe á n-iompórtáil + type: Cineál iompórtála + type_groups: + constructive: Seo a leanas & Leabharmharcanna + destructive: Bloic agus balbhaigh types: + blocking: Liosta blocála bookmarks: Leabharmharcanna + domain_blocking: Liosta blocála fearainn + following: Liosta ina dhiaidh sin + lists: Liostaí + muting: Liosta muting upload: Uaslódáil invites: + delete: Díghníomhachtaigh expired: As feidhm expires_in: '1800': 30 nóiméid @@ -395,43 +1455,230 @@ ga: '604800': Seachtain amháin '86400': Lá amháin expires_in_prompt: In am ar bith + generate: Gin nasc cuireadh + invalid: Níl an cuireadh seo bailí + invited_by: 'Fuair ​​tú cuireadh ó:' + max_uses: + few: Úsáideann %{count} + many: Úsáideann %{count} + one: 1 úsáid + other: Úsáideann %{count} + two: Úsáideann %{count} + max_uses_prompt: Gan teorainn + prompt: Gin agus roinn naisc le daoine eile chun rochtain a dheonú ar an bhfreastalaí seo + table: + expires_at: In éag + uses: Úsáidí + title: Tabhair cuireadh do dhaoine + lists: + errors: + limit: Tá uaslíon na liostaí sroichte agat login_activities: authentication_methods: + otp: app fíordheimhnithe dhá-fachtóir password: pasfhocal + sign_in_token: cód slándála ríomhphoist webauthn: eochracha slándála + description_html: Má fheiceann tú gníomhaíocht nach n-aithníonn tú, smaoinigh ar do phasfhocal a athrú agus fíordheimhniú dhá fhachtóir a chumasú. + empty: Níl aon stair fíordheimhnithe ar fáil + failed_sign_in_html: Theip ar iarracht síniú isteach le %{method} ó %{ip} (%{browser}) + successful_sign_in_html: D'éirigh le síniú isteach le %{method} ó %{ip} (%{browser}) + title: Stair fíordheimhnithe + mail_subscriptions: + unsubscribe: + action: Sea, díliostáil + complete: Gan liostáil + confirmation_html: An bhfuil tú cinnte gur mhaith leat díliostáil ó %{type} a fháil do Mastodon ar %{domain} chuig do ríomhphost ag %{email}? Is féidir leat liostáil arís i gcónaí ó do socruithe fógra ríomhphoist. + emails: + notification_emails: + favourite: r-phoist fógra is fearr leat + follow: r-phoist fógra a leanúint + follow_request: r-phoist iarratais a leanúint + mention: trácht ar r-phoist fógra + reblog: r-phoist fógra a threisiú + resubscribe_html: Má dhíliostáil tú de dhearmad, is féidir leat liostáil arís ó do socruithe fógra ríomhphoist. + success_html: Ní bhfaighidh tú %{type} le haghaidh Mastodon ar %{domain} chuig do ríomhphost ag %{email} a thuilleadh. + title: Díliostáil + media_attachments: + validations: + images_and_video: Ní féidir físeán a cheangal le postáil a bhfuil íomhánna ann cheana féin + not_ready: Ní féidir comhaid nach bhfuil próiseáil críochnaithe acu a cheangal. Bain triail eile as i gceann nóiméad! + too_many: Ní féidir níos mó ná 4 chomhad a cheangal + migrations: + acct: Bogtha go + cancel: Cealaigh atreorú + cancel_explanation: Má chuirtear an t-atreorú ar ceal, déanfar do chuntas reatha a athghníomhú, ach ní thabharfaidh sé seo leantóirí a aistríodh chuig an gcuntas sin ar ais. + cancelled_msg: D'éirigh leis an atreorú a chealú. + errors: + already_moved: an cuntas céanna ar bhog tú chuige cheana + missing_also_known_as: nach ailias den chuntas seo é + move_to_self: ní féidir é a bheith ina chuntas reatha + not_found: níorbh fhéidir a fháil + on_cooldown: Tá tú ar fuarú + followers_count: Leantóirí ag am aistrithe + incoming_migrations: Ag bogadh ó chuntas eile + incoming_migrations_html: Chun bogadh ó chuntas eile go dtí an ceann seo, ní mór duit ailias cuntais a chruthú ar dtús. + moved_msg: Tá do chuntas á atreorú chuig %{acct} anois agus tá do leantóirí á bhogadh anonn. + not_redirecting: Níl do chuntas á atreorú chuig aon chuntas eile faoi láthair. + on_cooldown: D'aistrigh tú do chuntas le déanaí. Beidh an fheidhm seo ar fáil arís i gceann %{count} lá. + past_migrations: Imirce san am a chuaigh thart + proceed_with_move: Bog leantóirí + redirected_msg: Tá do chuntas á atreorú chuig %{acct} anois. + redirecting_to: Tá do chuntas á atreorú chuig %{acct}. + set_redirect: Socraigh atreorú + warning: + backreference_required: Ní mór an cuntas nua a chumrú ar dtús chun cúltagairt a dhéanamh don cheann seo + before: 'Sula dtéann tú ar aghaidh, léigh na nótaí seo go cúramach le do thoil:' + cooldown: Tar éis bogadh tá tréimhse feithimh ann nach mbeidh tú in ann bogadh arís + disabled_account: Ní bheidh do chuntas reatha inúsáidte go hiomlán ina dhiaidh sin. Mar sin féin, beidh rochtain agat ar onnmhairiú sonraí chomh maith le hathghníomhú. + followers: Bogfaidh an gníomh seo gach leantóir ón gcuntas reatha go dtí an cuntas nua + only_redirect_html: Nó, ní féidir leat ach atreorú a chur suas ar do phróifíl. + other_data: Ní bhogfar aon sonraí eile go huathoibríoch + redirect: Déanfar próifíl do chuntais reatha a nuashonrú le fógra atreoraithe agus fágfar as an áireamh é ó chuardaigh + moderation: + title: Measarthacht + move_handler: + carry_blocks_over_text: Bhog an t-úsáideoir seo ó %{acct}, rud a chuir tú bac air. + carry_mutes_over_text: Bhog an t-úsáideoir seo ó %{acct}, rud a bhalbhaigh tú. + copy_account_note_text: 'Bhog an úsáideoir seo ó %{acct}, seo do nótaí roimhe seo fúthu:' + navigation: + toggle_menu: Scoránaigh roghchlár notification_mailer: admin: report: subject: Chuir %{name} tuairisc isteach + sign_up: + subject: Chláraigh %{name} + favourite: + body: 'B''fhearr le %{name} do phostáil:' + subject: B'fhearr le %{name} do phostáil + title: Nua is fearr leat follow: body: Tá %{name} do do leanúint anois! subject: Tá %{name} do do leanúint anois title: Leantóirí nua + follow_request: + action: Bainistigh iarratais leantacha + body: D'iarr %{name} tú a leanúint + subject: 'Leantóir ar feitheamh: %{name}' + title: Iarratas leantach nua mention: action: Freagair + body: 'Luaigh %{name} thú i:' + subject: Luaigh %{name} thú + title: Lua nua + poll: + subject: Tháinig deireadh le vótaíocht le %{name} reblog: + body: 'Treisíodh do phostáil le %{name}:' subject: Mhol %{name} do phostáil title: Moladh nua + status: + subject: Tá %{name} díreach postáilte + update: + subject: Chuir %{name} postáil in eagar + notifications: + administration_emails: Fógraí r-phoist admin + email_events: Imeachtaí le haghaidh fógraí ríomhphoist + email_events_hint: 'Roghnaigh imeachtaí ar mhaith leat fógraí a fháil ina leith:' + number: + human: + decimal_units: + format: "%n%u" + units: + billion: B + million: M + quadrillion: Q + thousand: K + trillion: T otp_authentication: + code_hint: Cuir isteach an cód ginte ag d'aip fíordheimhnitheora le deimhniú + description_html: Má chumasaíonn tú fíordheimhniú dhá fhachtóir ag baint úsáide as aip fíordheimhneora, beidh ort do ghuthán a bheith i seilbh logáil isteach, rud a ghinfidh comharthaí chun tú a chur isteach. enable: Cumasaigh + instructions_html: "Scan an cód QR seo isteach i Google Authenticator nó aip eile TOTP ar do ghuthán. As seo amach, ginfidh an aip sin comharthaí a chaithfidh tú a chur isteach agus tú ag logáil isteach." + manual_instructions: 'Mura féidir leat an cód QR a scanadh agus más gá duit é a chur isteach de láimh, seo an rún gnáth-théacs:' + setup: Socraigh suas + wrong_code: Bhí an cód a iontráladh neamhbhailí! An bhfuil am freastalaí agus am gléis ceart? pagination: newer: Níos nuaí next: An céad eile older: Níos sine prev: Ceann roimhe seo + truncate: "…" + polls: + errors: + already_voted: Tá tú tar éis vótáil ar an vótaíocht seo cheana féin + duplicate_options: go bhfuil míreanna dúblacha + duration_too_long: rófhada amach anseo + duration_too_short: ró-luath + expired: Tá deireadh leis an vótaíocht cheana féin + invalid_choice: Níl an rogha vótála roghnaithe ann + over_character_limit: ní féidir leis a bheith níos faide ná %{max} carachtar an ceann + self_vote: Ní féidir leat vótáil i do phobalbhreith féin + too_few_options: caithfidh níos mó ná mír amháin a bheith ann + too_many_options: ní féidir níos mó ná %{max} mír a bheith ann preferences: other: Eile + posting_defaults: Réamhshocruithe á bpostáil + public_timelines: Amlínte poiblí + privacy: + hint_html: "Saincheap conas is mian leat do phróifíl agus do phostálacha a fháil. Is féidir le gnéithe éagsúla i Mastodon cabhrú leat teacht ar lucht féachana níos leithne nuair atá tú cumasaithe. Tóg nóiméad chun athbhreithniú a dhéanamh ar na socruithe seo chun a chinntiú go n-oireann siad do do chás úsáide." + privacy: Príobháideacht + privacy_hint_html: Rialú ar an méid is mian leat a nochtadh ar mhaithe le daoine eile. Aimsíonn daoine próifílí suimiúla agus aipeanna fionnuara trí na haipeanna seo a leanas a bhrabhsáil agus a fheiceáil cé na haipeanna a bpostálann siad, ach b’fhéidir gurbh fhearr leat é a choinneáil faoi cheilt. + reach: Shroich + reach_hint_html: Smacht a fháil ar cé acu is mian leat a fháil amach agus daoine nua a leanúint. Ar mhaith leat do phostálacha a thaispeáint ar an scáileán Explore? Ar mhaith leat go bhfeicfeadh daoine eile tú sna moltaí a leanann siad? Ar mhaith leat glacadh le gach leantóir nua go huathoibríoch, nó smacht gráinneach a bheith agat ar gach leantóir? + search: Cuardach + search_hint_html: Rialú conas ba mhaith leat a fháil. Ar mhaith leat go bhfaighidh daoine tú tríd an rud a chuir tú suas go poiblí faoi? Ar mhaith leat go bhfaighidh daoine lasmuigh de Mastodon do phróifíl agus iad ag cuardach an ghréasáin? Tabhair faoi deara le do thoil nach féidir eisiamh iomlán ó gach inneall cuardaigh a chinntiú mar fhaisnéis don phobal. + title: Príobháideacht agus teacht + privacy_policy: + title: Beartas Príobháideachais + reactions: + errors: + limit_reached: Teorainn frithghníomhartha éagsúla bainte amach + unrecognized_emoji: ní emoji aitheanta é + redirects: + prompt: Má tá muinín agat as an nasc seo, cliceáil air chun leanúint ar aghaidh. + title: Tá tú ag fágáil %{instance}. relationships: + activity: Gníomhaíocht chuntais + confirm_follow_selected_followers: An bhfuil tú cinnte gur mhaith leat na leantóirí roghnaithe a leanúint? + confirm_remove_selected_followers: An bhfuil tú cinnte gur mhaith leat na leantóirí roghnaithe a bhaint? + confirm_remove_selected_follows: An bhfuil tú cinnte gur mhaith leat na nithe seo a leanas roghnaithe a bhaint? + dormant: Díomhaoin + follow_failure: Níorbh fhéidir cuid de na cuntais roghnaithe a leanúint. follow_selected_followers: Lean leantóirí roghnaithe followers: Leantóirí following: Ag leanúint + invited: Cuireadh + last_active: Gníomhach seo caite + most_recent: Is déanaí + moved: Ar athraíodh a ionad + mutual: Frithpháirteach primary: Príomha + relationship: Gaol + remove_selected_domains: Remove all followers from the selected domains remove_selected_followers: Bain leantóirí roghnaithe remove_selected_follows: Dí-lean úsáideoirí roghnaithe status: Stádas cuntais + remote_follow: + missing_resource: Níorbh fhéidir an URL atreoraithe riachtanach do do chuntas a aimsiú + reports: + errors: + invalid_rules: ní thagraíonn sé do rialacha bailí rss: content_warning: 'Rabhadh ábhair:' + descriptions: + account: Postálacha poiblí ó @%{acct} + tag: 'Postálacha poiblí clibáilte # %{hashtag}' + scheduled_statuses: + over_daily_limit: Tá an teorainn de %{limit} postáil sceidealaithe sáraithe agat don lá atá inniu ann + over_total_limit: Tá an teorainn de %{limit} postáil sceidealaithe sáraithe agat + too_soon: Caithfidh an dáta sceidealta a bheith sa todhchaí + self_destruct: + lead_html: Ar an drochuair, tá %{domain} ag dúnadh síos go buan. Má bhí cuntas agat ann, ní bheidh tú in ann leanúint ar aghaidh á úsáid, ach is féidir leat cúltaca de do shonraí a iarraidh fós. + title: Tá an freastalaí seo ag dúnadh sessions: + activity: An ghníomhaíocht dheireanach browser: Brabhsálaí browsers: alipay: Alipay @@ -441,41 +1688,162 @@ ga: electron: Electron firefox: Firefox generic: Brabhsálaí anaithnid + huawei_browser: Brabhsálaí Huawei + ie: Internet Explorer + micro_messenger: Micreascannán + nokia: Nokia s40 Ovi Brabhsálaí + opera: Opera + otter: Dobharchú + phantom_js: PhantomJS + qq: Brabhsálaí QQ + safari: Safari + uc_browser: Brabhsálaí UC + unknown_browser: Brabhsálaí Anaithnid + weibo: Weibo + current_session: Seisiún reatha + date: Dáta + description: "%{browser} ar %{platform}" + explanation: Seo iad na brabhsálaithe gréasáin atá logáilte isteach i do chuntas Mastodon faoi láthair. + ip: IP platforms: + adobe_air: Adobe Air android: Android blackberry: BlackBerry chrome_os: ChromeOS firefox_os: Firefox OS ios: iOS + kai_os: KaiOS linux: Linux mac: macOS + unknown_platform: Ardán Anaithnid windows: Windows windows_mobile: Windows Mobile windows_phone: Windows Phone + revoke: Aisghair + revoke_success: Seisiún aisghairthe go rathúil title: Seisiúin + view_authentication_history: Féach ar stair fíordheimhnithe do chuntais settings: account: Cuntas account_settings: Socruithe cuntais + aliases: Ailiasanna cuntais appearance: Cuma + authorized_apps: Aipeanna údaraithe back: Ar ais go Mastodon + delete: Scriosadh cuntais development: Forbairt edit_profile: Cuir an phróifíl in eagar + export: Easpórtáil sonraí + featured_tags: Haischlib faoi thrácht import: Iompórtáil + import_and_export: Iompórtáil agus easpórtáil + migrate: Imirce cuntais + notifications: Fógraí ríomhphoist preferences: Sainroghanna pearsanta profile: Próifíl + relationships: Leantóirí agus leanúna + severed_relationships: Caidrimh dhearfa + statuses_cleanup: Uathscriosadh postála + strikes: Stailceanna measarthachta + two_factor_authentication: Údar dhá-fhachtóir webauthn_authentication: Eochracha slándála + severed_relationships: + download: Íoslódáil (%{count}) + event_type: + account_suspension: Fionraí cuntais (%{target_name}) + domain_block: Freastalaí ar fionraí (%{target_name}) + user_domain_block: Chuir tú bac ar %{target_name} + lost_followers: Leanúna caillte + lost_follows: Seo a leanas caillte + preamble: Seans go gcaillfidh tú seo a leanas agus a leantóirí nuair a bhacálann tú fearann ​​nó nuair a shocraíonn do mhodhnóirí cianfhreastalaí a chur ar fionraí. Nuair a tharlaíonn sé sin, beidh tú in ann liostaí de chaidreamh deighilte a íoslódáil, lena n-iniúchadh agus b'fhéidir iompórtáil ar fhreastalaí eile. + purged: Glanadh faisnéis faoin bhfreastalaí seo ag riarthóirí do fhreastalaí. + type: Imeacht statuses: + attached: + audio: + few: "%{count} fuaime" + many: "%{count} fuaime" + one: "%{count} fuaime" + other: "%{count} fuaime" + two: "%{count} fuaime" + description: 'Ceangailte: %{attached}' + image: + few: "%{count} híomhánna" + many: "%{count} híomhánna" + one: "%{count} íomhá" + other: "%{count} híomhánna" + two: "%{count} híomhánna" + video: + few: "%{count} físeáin" + many: "%{count} físeáin" + one: "%{count} físeán" + other: "%{count} físeáin" + two: "%{count} físeáin" boosted_from_html: Molta ó %{acct_link} content_warning: 'Rabhadh ábhair: %{warning}' + default_language: Mar an gcéanna le teanga an chomhéadain + disallowed_hashtags: + few: 'bhí na Haischlib dícheadaithe: %{tags}' + many: 'bhí na Haischlib dícheadaithe: %{tags}' + one: 'bhí haischlib dícheadaithe: %{tags}' + other: 'bhí na Haischlib dícheadaithe: %{tags}' + two: 'bhí na Haischlib dícheadaithe: %{tags}' + edited_at_html: "%{date} curtha in eagar" + errors: + in_reply_not_found: Is cosúil nach ann don phostáil a bhfuil tú ag iarraidh freagra a thabhairt air. + open_in_web: Oscail i ngréasán + over_character_limit: teorainn carachtar %{max} sáraithe + pin_errors: + direct: Ní féidir postálacha nach bhfuil le feiceáil ach ag úsáideoirí luaite a phinnáil + limit: Tá uaslíon na bpostálacha pinn agat cheana féin + ownership: Ní féidir postáil duine éigin eile a phionnáil + reblog: Ní féidir treisiú a phinnáil poll: + total_people: + few: "%{count} daoine" + many: "%{count} daoine" + one: "%{count} duine" + other: "%{count} daoine" + two: "%{count} daoine" + total_votes: + few: "%{count} vótaí" + many: "%{count} vótaí" + one: "%{count} vóta" + other: "%{count} vótaí" + two: "%{count} vótaí" vote: Vótáil show_more: Taispeáin níos mó show_thread: Taispeáin snáithe + title: '%{name}: "%{quote}"' visibilities: + direct: Díreach private: Leantóirí amháin private_long: Taispeáin do leantóirí amháin + public: Poiblí + public_long: Is féidir le gach duine a fheiceáil + unlisted: Neamhliostaithe + unlisted_long: Is féidir le gach duine a fheiceáil, ach nach bhfuil liostaithe ar amlínte poiblí statuses_cleanup: + enabled: Scrios seanphostálacha go huathoibríoch + enabled_hint: Scriostar do phostálacha go huathoibríoch nuair a shroicheann siad tairseach aoise sonraithe, ach amháin má thagann siad le ceann de na heisceachtaí thíos + exceptions: Eisceachtaí + explanation: Toisc gur oibríocht chostasach é postálacha a scriosadh, déantar é seo go mall le himeacht ama nuair nach mbíonn an freastalaí gnóthach ar bhealach eile. Ar an ábhar sin, d’fhéadfadh sé go scriosfar do phostálacha tamall tar éis dóibh an tairseach aoise a bhaint amach. ignore_favs: Tabhair neamhaird ar toghanna + ignore_reblogs: Déan neamhaird de boosts + interaction_exceptions: Eisceachtaí bunaithe ar idirghníomhaíochtaí + interaction_exceptions_explanation: Tabhair faoi deara nach bhfuil aon ráthaíocht go scriosfar postálacha má théann siad faoi bhun na tairsí is ansa leat nó an teanndáileog tar éis dóibh dul thar iad uair amháin. + keep_direct: Coinnigh teachtaireachtaí díreacha + keep_direct_hint: Ní scriosann sé aon cheann de do theachtaireachtaí díreacha + keep_media: Coinnigh postálacha le ceangaltáin meán + keep_media_hint: Ní scriosann sé aon cheann de do phostálacha a bhfuil ceangaltáin meán acu + keep_pinned: Coinnigh postálacha pinn + keep_pinned_hint: Ní scriosann sé aon cheann de do phostálacha pinn + keep_polls: Coinnigh pobalbhreith + keep_polls_hint: Ní scriosann sé aon cheann de do vótaíochtaí + keep_self_bookmark: Coinnigh postálacha leabharmharcáilte agat + keep_self_bookmark_hint: Ní scriosann sé do phostálacha féin má tá leabharmharcáilte agat + keep_self_fav: Coinnigh na poist a thaitin leat + keep_self_fav_hint: Ní scriosann sé do phostálacha féin más fearr leat iad min_age: '1209600': Coicís '15778476': 6 mhí @@ -485,20 +1853,182 @@ ga: '604800': Seachtain amháin '63113904': 2 bhliain '7889238': 3 mhí + min_age_label: Tairseach aoise + min_favs: Coinnigh na poist is fearr leat ar a laghad + min_favs_hint: Ní scriosann sé aon cheann de do phostálacha a fuair ar a laghad an líon ceanán seo. Fág bán chun postálacha a scriosadh beag beann ar líon na gceanán atá acu + min_reblogs: Coinnigh postálacha treisithe ar a laghad + min_reblogs_hint: Ní scriosann sé aon cheann de do phostálacha a cuireadh leis an líon seo uaireanta ar a laghad. Fág bán chun postálacha a scriosadh beag beann ar a líon teanndáileog stream_entries: sensitive_content: Ábhar íogair + strikes: + errors: + too_late: Tá sé ró-dhéanach achomharc a dhéanamh faoin stailc seo + tags: + does_not_match_previous_name: nach meaitseálann an t-ainm roimhe seo + themes: + contrast: Mastodon (Codarsnacht ard) + default: Mastodon (Dorcha) + mastodon-light: Mastodon (Solas) + system: Uathoibríoch (úsáid téama córais) + time: + formats: + default: "%b %d, %Y, %H:%M" + month: "%b %Y" + time: "%H:%M" + with_time_zone: "%b %d, %Y, %H:%M %Z" + translation: + errors: + quota_exceeded: Sáraíodh an cuóta úsáide uile-fhreastalaí don tseirbhís aistriúcháin. + too_many_requests: Tá an iomarca iarratas ar an tseirbhís aistriúcháin le déanaí. two_factor_authentication: + add: Cuir + disable: Díchumasaigh 2FA + disabled_success: D'éirigh le fíordheimhniú dhá-fhachtóir a dhíchumasú edit: Cuir in eagar + enabled: Tá fíordheimhniú dhá fhachtóir cumasaithe + enabled_success: D'éirigh le fíordheimhniú dhá fhachtóir a chumasú + generate_recovery_codes: Gin cóid athshlánaithe + lost_recovery_codes: Ligeann cóid athshlánaithe duit rochtain a fháil ar do chuntas arís má chailleann tú do ghuthán. Má tá do chóid athshlánaithe caillte agat, is féidir leat iad a athnuachan anseo. Déanfar do sheanchóid athshlánaithe a neamhbhailíochtú. + methods: Modhanna dhá-fhachtóir + otp: Aip fíordheimhnitheora + recovery_codes: Cóid aisghabhála cúltaca + recovery_codes_regenerated: D'éirigh le hathghiniúint cóid athshlánaithe + recovery_instructions_html: Má chailleann tú rochtain ar do ghuthán riamh, is féidir leat ceann de na cóid athshlánaithe thíos a úsáid chun rochtain a fháil ar do chuntas arís. Coinnigh na cóid athshlánaithe slán. Mar shampla, is féidir leat iad a phriontáil agus iad a stóráil le doiciméid thábhachtacha eile. webauthn: Eochracha slándála user_mailer: + appeal_approved: + action: Socruithe cuntas + explanation: Ceadaíodh achomharc na stailce i gcoinne do chuntais ar %{strike_date} a chuir tú isteach ar %{appeal_date}. Tá seasamh maith ag do chuntas arís. + subject: Ceadaíodh d'achomharc ó %{date} + subtitle: Tá seasamh maith ag do chuntas arís. + title: Achomharc ceadaithe + appeal_rejected: + explanation: Diúltaíodh d'achomharc na stailce in aghaidh do chuntais ar %{strike_date} a chuir tú isteach ar %{appeal_date}. + subject: Diúltaíodh do d'achomharc ó %{date} + subtitle: Diúltaíodh do d'achomharc. + title: Diúltaíodh don achomharc + backup_ready: + explanation: D'iarr tú cúltaca iomlán de do chuntas Mastodon. + extra: Tá sé réidh le híoslódáil anois! + subject: Tá do chartlann réidh le híoslódáil + title: Tógáil cartlainne + failed_2fa: + details: 'Seo sonraí na hiarrachta síniú isteach:' + explanation: Rinne duine éigin iarracht síniú isteach ar do chuntas ach sholáthair sé fachtóir fíordheimhnithe dara neamhbhailí. + further_actions_html: Mura tusa a bhí ann, molaimid duit %{action} a dhéanamh láithreach toisc go bhféadfadh sé a bheith i gcontúirt. + subject: Teip fíordheimhnithe dara fachtóir + title: Theip ar fhíordheimhniú an dara fachtóir + suspicious_sign_in: + change_password: do phasfhocal a athrú + details: 'Seo sonraí faoin síniú isteach:' + explanation: Bhraitheamar síniú isteach ar do chuntas ó sheoladh IP nua. + further_actions_html: Mura tusa a bhí ann, molaimid duit %{action} a dhéanamh láithreach agus fíordheimhniú dhá fhachtóir a chumasú chun do chuntas a choinneáil slán. + subject: Fuarthas rochtain ar do chuntas ó sheoladh IP nua + title: Síniú isteach nua warning: appeal: Cuir achomharc isteach + appeal_description: Má chreideann tú gur earráid é seo, is féidir leat achomharc a chur isteach chuig foireann %{instance}. categories: spam: Turscar + violation: Sáraíonn ábhar na treoirlínte pobail seo a leanas + explanation: + delete_statuses: Fuarthas amach gur sháraigh roinnt de do chuid postálacha treoirlínte pobail amháin nó níos mó agus bhain modhnóirí %{instance} iad ina dhiaidh sin. + disable: Ní féidir leat do chuntas a úsáid a thuilleadh, ach fanann do phróifíl agus sonraí eile slán. Is féidir leat cúltaca de do shonraí a iarraidh, socruithe cuntais a athrú nó do chuntas a scriosadh. + mark_statuses_as_sensitive: Tá cuid de do chuid postálacha marcáilte mar íogair ag modhnóirí %{instance}. Ciallaíonn sé seo go mbeidh ar dhaoine na meáin a thapáil sna poist sula dtaispeánfar réamhamharc. Is féidir leat na meáin a mharcáil mar íogair tú féin agus tú ag postáil amach anseo. + sensitive: As seo amach, beidh gach do chomhaid meán uaslódáilte a mharcáil mar íogair agus i bhfolach taobh thiar de rabhadh cliceáil-trí. + silence: Is féidir leat do chuntas a úsáid go fóill ach ní fheicfidh ach na daoine atá ag leanúint ort cheana féin do phostálacha ar an bhfreastalaí seo, agus seans go mbeidh tú eisiata ó ghnéithe éagsúla fionnachtana. Féadfaidh daoine eile tú a leanúint de láimh, áfach. + suspend: Ní féidir leat do chuntas a úsáid a thuilleadh, agus níl rochtain ar do phróifíl ná ar shonraí eile a thuilleadh. Is féidir leat logáil isteach fós chun cúltaca de do shonraí a iarraidh go dtí go mbaintear na sonraí go hiomlán i gceann thart ar 30 lá, ach coinneoimid roinnt sonraí bunúsacha chun cosc ​​a chur ort an fionraí a sheachaint. reason: 'Fáth:' + statuses: 'Postálacha a luadh:' subject: + delete_statuses: Baineadh do phostálacha ar %{acct} + disable: Tá do chuntas %{acct} reoite + mark_statuses_as_sensitive: Marcáladh do phostálacha ar %{acct} mar íogair none: Rabhadh do %{acct} + sensitive: Marcálfar do phostálacha ar %{acct} mar íogair as seo amach + silence: Tá do chuntas %{acct} teoranta + suspend: Tá do chuntas %{acct} curtha ar fionraí title: + delete_statuses: Baineadh postálacha + disable: Cuntas reoite + mark_statuses_as_sensitive: Postálacha marcáilte mar íogair none: Rabhadh + sensitive: Cuntas marcáilte mar íogair + silence: Cuntas teoranta + suspend: Cuntas ar fionraí + welcome: + apps_android_action: Faigh é ar Google Play + apps_ios_action: Íoslódáil ar an App Store + apps_step: Íoslódáil ár n-aipanna oifigiúla. + apps_title: Aipeanna Mastodon + checklist_subtitle: 'Cuirimis tús leat ar an teorainn shóisialta nua seo:' + checklist_title: Seicliosta Fáilte + edit_profile_action: Pearsanú + edit_profile_step: Cuir le d'idirghníomhaíochtaí trí phróifíl chuimsitheach a bheith agat. + edit_profile_title: Déan do phróifíl a phearsantú + explanation: Seo roinnt leideanna chun tú a chur ar bun + feature_action: Foghlaim níos mó + feature_audience: Soláthraíonn Mastodon deis uathúil duit do lucht féachana a bhainistiú gan fir lár. Ligeann Mastodon a imlonnaítear ar do bhonneagar féin duit leanúint agus leanúint ó aon fhreastalaí Mastodon eile ar líne agus níl sé faoi smacht aon duine ach mise. + feature_audience_title: Tóg do lucht féachana faoi rún + feature_control: Is fearr a fhios agat cad ba mhaith leat a fheiceáil ar do bheathú baile. Gan algartam nó fógraí chun do chuid ama a chur amú. Lean aon duine ar fud aon fhreastalaí Mastodon ó chuntas amháin agus faigh a gcuid post in ord croineolaíoch, agus déan do chúinne den idirlíon beagán níos mó cosúil leatsa. + feature_control_title: Coinnigh smacht ar d’amlíne féin + feature_creativity: Tacaíonn Mastodon le postálacha fuaime, físe agus pictiúr, tuairiscí inrochtaineachta, pobalbhreitheanna, rabhaidh inneachair, avatars beoite, emojis saincheaptha, rialú barr mionsamhlacha, agus níos mó, chun cabhrú leat tú féin a chur in iúl ar líne. Cibé an bhfuil do chuid ealaíne, do cheol nó do phodchraoladh á fhoilsiú agat, tá Mastodon ann duit. + feature_creativity_title: Cruthaitheacht gan sárú + feature_moderation: Cuireann Mastodon cinnteoireacht ar ais i do lámha. Cruthaíonn gach freastalaí a rialacha agus a rialacháin féin, a chuirtear i bhfeidhm go háitiúil agus nach bhfuil ó bharr anuas cosúil le meáin shóisialta chorparáideacha, rud a fhágann gurb é an ceann is solúbtha é chun freagairt do riachtanais grúpaí éagsúla daoine. Bí ar fhreastalaí leis na rialacha a n-aontaíonn tú leo, nó do chuid féin a óstáil. + feature_moderation_title: Ag maolú ar an mbealach ar cheart dó a bheith + follow_action: Lean + follow_step: Is éard atá i gceist le daoine suimiúla a leanúint ná Mastodon. + follow_title: Cuir do chuid fotha baile in oiriúint duit féin + follows_subtitle: Lean cuntais aitheanta + follows_title: Cé a leanúint + follows_view_more: Féach ar níos mó daoine a leanúint + hashtags_recent_count: + few: "%{people} daoine le 2 lá anuas" + many: "%{people} daoine le 2 lá anuas" + one: "%{people} duine le 2 lá anuas" + other: "%{people} daoine le 2 lá anuas" + two: "%{people} daoine le 2 lá anuas" + hashtags_subtitle: Déan iniúchadh ar a bhfuil ag dul chun cinn le 2 lá anuas + hashtags_title: Haischlib treochta + hashtags_view_more: Féach ar níos mó Haischlib treochta + post_action: Cum + post_step: Abair hello leis an domhan le téacs, grianghraif, físeáin, nó pobalbhreith. + post_title: Déan do chéad phostáil + share_action: Comhroinn + share_step: Cuir in iúl do do chairde conas tú a aimsiú ar Mastodon. + share_title: Roinn do phróifíl Mastodon + sign_in_action: Sínigh isteach + subject: Fáilte go Mastodon + title: Fáilte ar bord, %{name}! + users: + follow_limit_reached: Ní féidir leat níos mó ná %{limit} duine a leanúint + go_to_sso_account_settings: Téigh chuig socruithe cuntais do sholáthraí aitheantais + invalid_otp_token: Cód dhá-fhachtóir neamhbhailí + otp_lost_help_html: Má chaill tú rochtain ar an dá cheann, is féidir leat dul i dteagmháil le %{email} + rate_limited: An iomarca iarrachtaí fíordheimhnithe, bain triail eile as ar ball. + seamless_external_login: Tá tú logáilte isteach trí sheirbhís sheachtrach, mar sin níl socruithe pasfhocail agus ríomhphoist ar fáil. + signed_in_as: 'Sínithe isteach mar:' + verification: + extra_instructions_html: Leid: Is féidir an nasc ar do shuíomh Gréasáin a bheith dofheicthe. Is í an chuid thábhachtach ná rel="me" a chuireann cosc ​​ar phearsanú ar shuímh Ghréasáin a bhfuil inneachar a ghintear leis an úsáideoir. Is féidir leat fiú clib nasc a úsáid i gceanntásc an leathanaigh in ionad a, ach caithfidh an HTML a bheith inrochtana gan JavaScript a chur i gcrích. + here_is_how: Seo é an chaoi + hint_html: "Is do chách é d'aitheantas a fhíorú ar Mastodon. Bunaithe ar chaighdeáin oscailte gréasáin, saor in aisce anois agus go deo. Níl uait ach láithreán gréasáin pearsanta a aithníonn daoine thú. Nuair a nascann tú leis an suíomh Gréasáin seo ó do phróifíl, seiceóimid go bhfuil nasc idir an suíomh Gréasáin agus do phróifíl agus taispeánfaimid táscaire amhairc air." + instructions_html: Cóipeáil agus greamaigh an cód thíos isteach i HTML do shuíomh Gréasáin. Ansin cuir seoladh do shuíomh Gréasáin isteach i gceann de na réimsí breise ar do phróifíl ón gcluaisín "Cuir próifíl in eagar" agus sábháil athruithe. + verification: Fíorú + verified_links: Do naisc fhíoraithe webauthn_credentials: + add: Cuir eochair shlándála nua leis + create: + error: Bhí fadhb ann agus d'eochair shlándála á cur leis. Arís, le d'thoil. + success: Cuireadh d'eochair shlándála leis. delete: Scrios + delete_confirmation: An bhfuil tú cinnte gur mhaith leat an eochair shlándála seo a scriosadh? + description_html: Má chumasaíonn tú fíordheimhniú eochrach slándála, beidh ort ceann de na heochracha slándála a úsáid chun logáil isteach. + destroy: + error: Bhí fadhb ann agus d'eochair shlándála á scriosadh. Arís, le d'thoil. + success: Scriosadh d'eochair shlándála go rathúil. + invalid_credential: Eochair shlándála neamhbhailí + nickname_hint: Cuir isteach leasainm d'eochair shlándála nua + not_enabled: Níl WebAuthn cumasaithe agat fós + not_supported: This browser doesn't support security keys + otp_required: To use security keys please enable two-factor authentication first. + registered_on: Registered on %{date} diff --git a/config/locales/gl.yml b/config/locales/gl.yml index c9f08dcad74d3a..d1c8633badd602 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -226,6 +226,7 @@ gl: update_custom_emoji: Actualizar emoticona personalizada update_domain_block: Actualizar bloqueo do dominio update_ip_block: Actualizar regra IP + update_report: Actualización da denuncia update_status: Actualizar publicación update_user_role: Actualizar Rol actions: @@ -638,6 +639,7 @@ gl: report: 'Denuncia #%{id}' reported_account: Conta denunciada reported_by: Denunciado por + reported_with_application: Denunciado coa aplicación resolved: Resolto resolved_msg: Resolveuse con éxito a denuncia! skip_to_actions: Ir a accións diff --git a/config/locales/hi.yml b/config/locales/hi.yml index b67de192f2563e..0bfc30027abef7 100644 --- a/config/locales/hi.yml +++ b/config/locales/hi.yml @@ -32,6 +32,8 @@ hi: silence: सीमा silenced: सीमित title: खाते + reports: + reported_with_application: एप्लीकेशन से रिपोर्ट किया गया system_checks: upload_check_privacy_error: message_html: " आपके वेब सर्वर का कन्फिगरेशन सही नहीं है। उपयोगकर्ताओं की निजता खतरे में है। " diff --git a/config/locales/hu.yml b/config/locales/hu.yml index 5def03d902a367..df32bd39d116f7 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -226,7 +226,7 @@ hu: update_custom_emoji: Egyéni emodzsi frissítése update_domain_block: Domain tiltás frissítése update_ip_block: IP-szabály frissítése - update_report: Bejelentés Frissítése + update_report: Bejelentés frissítése update_status: Bejegyzés frissítése update_user_role: Szerepkör frissítése actions: diff --git a/config/locales/simple_form.bg.yml b/config/locales/simple_form.bg.yml index f14a21b0c70036..564f72e8c21231 100644 --- a/config/locales/simple_form.bg.yml +++ b/config/locales/simple_form.bg.yml @@ -211,6 +211,7 @@ bg: setting_default_privacy: Поверителност на публикуване setting_default_sensitive: Все да се бележи мултимедията като деликатна setting_delete_modal: Показване на прозорче за потвърждение преди изтриване на публикация + setting_disable_hover_cards: Изключване на прегледа на профила, премествайки показалеца отгоре setting_disable_swiping: Деактивиране на бързо плъзгащи движения setting_display_media: Показване на мултимедия setting_display_media_default: Стандартно @@ -242,11 +243,13 @@ bg: warn: Скриване зад предупреждение form_admin_settings: activity_api_enabled: Публикуване на агрегатна статистика относно потребителската дейност в API + app_icon: Икона на приложение backups_retention_period: Период за съхранение на потребителския архив bootstrap_timeline_accounts: Винаги да се препоръчват следните акаунти на нови потребители closed_registrations_message: Съобщение при неналична регистрация content_cache_retention_period: Период на запазване на отдалечено съдържание custom_css: Персонализиран CSS + favicon: Сайтоикона mascot: Плашило талисман по избор (остаряло) media_cache_retention_period: Период на запазване на мултимедийния кеш peers_api_enabled: Публикуване на списъка с открити сървъри в API diff --git a/config/locales/simple_form.ga.yml b/config/locales/simple_form.ga.yml index 3597544ce3a623..2effe1a10cc7a0 100644 --- a/config/locales/simple_form.ga.yml +++ b/config/locales/simple_form.ga.yml @@ -2,47 +2,239 @@ ga: simple_form: hints: + account: + discoverable: Seans go mbeidh do phostálacha poiblí agus do phróifíl le feiceáil nó molta i réimsí éagsúla de Mastodon agus is féidir do phróifíl a mholadh d’úsáideoirí eile. + display_name: D'ainm iomlán nó d'ainm spraoi. + fields: Do leathanach baile, forainmneacha, aois, rud ar bith is mian leat. + indexable: Seans go mbeidh do phostálacha poiblí le feiceáil sna torthaí cuardaigh ar Mastodon. Seans go mbeidh daoine a d’idirghníomhaigh le do phostálacha in ann iad a chuardach beag beann ar. + note: 'Is féidir leat @trá a dhéanamh ar dhaoine eile nó #hashtags.' + show_collections: Beidh daoine in ann brabhsáil trí do seo a leanas agus do leanúna. Feicfidh na daoine a leanann tú go leanann tú iad beag beann ar. + unlocked: Beidh daoine in ann tú a leanúint gan cead a iarraidh. Díthiceáil an dteastaíonn uait athbhreithniú a dhéanamh ar iarratais leantacha agus roghnaigh cé acu an nglacfaidh nó an diúltóidh tú do leantóirí nua. account_alias: acct: Sonraigh ainm@fearann don chuntas ar mhaith leat aistriú uaidh account_migration: acct: Sonraigh ainm@fearann don chuntas ar mhaith leat aistriú chuige + account_warning_preset: + text: Is féidir leat comhréir na bpost a úsáid, mar URLanna, hashtags agus lua + title: Roghnach. Níl sé le feiceáil ag an bhfaighteoir admin_account_action: + include_statuses: Feicfidh an t-úsáideoir cé na poist ba chúis leis an ngníomh modhnóireachta nó leis an rabhadh + send_email_notification: Gheobhaidh an t-úsáideoir míniú ar an méid a tharla lena chuntas + text_html: Roghnach. Is féidir leat comhréir phoist a úsáid. Is féidir leat réamhshocruithe rabhaidh a chur leis chun am a shábháil + type_html: Roghnaigh cad atá le déanamh le %{acct} types: disable: Cuir cosc ar an úsáideoir a chuntas a úsáid, ach ná scrios ná folaigh a bhfuil ann. + none: Bain úsáid as seo chun rabhadh a sheoladh chuig an úsáideoir, gan aon ghníomh eile a spreagadh. + sensitive: Iallach a chur ar cheangaltáin meán an úsáideora seo go léir a bheith íogair. + silence: Cosc a chur ar an úsáideoir ó bheith in ann postáil le hinfheictheacht phoiblí, a gcuid postálacha agus fógraí a cheilt ar dhaoine nach leanann iad. Dúnann sé gach tuairisc i gcoinne an chuntais seo. + suspend: Cosc ar aon idirghníomhaíocht ón gcuntas seo nó chuig an gcuntas seo agus scrios a bhfuil ann. Inchúlaithe laistigh de 30 lá. Dúnann sé gach tuairisc i gcoinne an chuntais seo. + warning_preset_id: Roghnach. Is féidir leat téacs saincheaptha a chur le deireadh an réamhshocraithe fós + announcement: + all_day: Nuair a dhéantar iad a sheiceáil, ní thaispeánfar ach dátaí an raon ama + ends_at: Roghnach. Beidh an fógra neamhfhoilsithe go huathoibríoch ag an am seo + scheduled_at: Fág bán chun an fógra a fhoilsiú láithreach + starts_at: Roghnach. I gcás go bhfuil d'fhógra ceangailte le raon ama ar leith + text: Is féidir leat comhréir phoist a úsáid. Tabhair aird ar an spás a ghlacfaidh an fógra ar scáileán an úsáideora + appeal: + text: Ní féidir leat achomharc a dhéanamh ach uair amháin ar stailc defaults: + autofollow: Leanfaidh daoine a chláraíonn tríd an gcuireadh thú go huathoibríoch + avatar: WEBP, PNG, GIF nó JPG. %{size} ar a mhéad. Íoslaghdófar é go %{dimensions}px + bot: Cuir in iúl do dhaoine eile go ndéanann an cuntas gníomhartha uathoibrithe den chuid is mó agus go mb’fhéidir nach ndéanfar monatóireacht air + context: Comhthéacs amháin nó comhthéacsanna iolracha inar cheart go mbeadh feidhm ag an scagaire + current_password: Chun críocha slándála cuir isteach pasfhocal an chuntais reatha + current_username: Le deimhniú, cuir isteach ainm úsáideora an chuntais reatha + digest: Seoltar é tar éis tréimhse fhada neamhghníomhaíochta amháin agus sa chás sin amháin go bhfuil aon teachtaireachtaí pearsanta faighte agat agus tú as láthair + email: Seolfar ríomhphost deimhnithe chugat + header: WEBP, PNG, GIF nó JPG. %{size} ar a mhéad. Íoslaghdófar é go %{dimensions}px + inbox_url: Cóipeáil an URL ó leathanach tosaigh an athsheachadáin is mian leat a úsáid + irreversible: Imeoidh postálacha scagtha go dochúlaithe, fiú má bhaintear an scagaire níos déanaí + locale: Teanga an chomhéadain úsáideora, r-phoist agus fógraí brú + password: Úsáid ar a laghad 8 gcarachtar + phrase: Déanfar é a mheaitseáil beag beann ar chásáil an téacs nó ar an ábhar atá ag tabhairt foláireamh do phostáil + scopes: Cé na APIanna a mbeidh cead ag an bhfeidhmchlár rochtain a fháil orthu. Má roghnaíonn tú raon feidhme barrleibhéil, ní gá duit cinn aonair a roghnú. + setting_aggregate_reblogs: Ná taispeáin treisithe nua do phoist a treisíodh le déanaí (ní dhéanann difear ach do threisithe nuafhaighte) + setting_always_send_emails: Go hiondúil ní sheolfar fógraí ríomhphoist agus tú ag úsáid Mastodon go gníomhach + setting_default_sensitive: Tá meáin íogair i bhfolach de réir réamhshocraithe agus is féidir iad a nochtadh le cliceáil + setting_display_media_default: Folaigh meáin atá marcáilte mar íogair setting_display_media_hide_all: Folaigh meáin i gcónaí setting_display_media_show_all: Taispeáin meáin i gcónaí + setting_use_blurhash: Tá grádáin bunaithe ar dhathanna na n-amharcanna ceilte ach cuireann siad salach ar aon mhionsonraí + setting_use_pending_items: Folaigh nuashonruithe amlíne taobh thiar de chlic seachas an fotha a scrollú go huathoibríoch + username: Is féidir leat litreacha, uimhreacha, agus béim a úsáid + whole_word: Nuair a bhíonn an eochairfhocal nó frása alfa-uimhriúil amháin, ní chuirfear i bhfeidhm é ach amháin má mheaitseálann sé an focal iomlán + domain_allow: + domain: Beidh an fearann ​​seo in ann sonraí a fháil ón bhfreastalaí seo agus déanfar sonraí a thagann isteach uaidh a phróiseáil agus a stóráil + email_domain_block: + domain: Is féidir gurb é seo an t-ainm fearainn a thaispeánann sa seoladh ríomhphoist nó sa taifead MX a úsáideann sé. Déanfar iad a sheiceáil nuair a chláraítear iad. + with_dns_records: Déanfar iarracht taifid DNS an fhearainn tugtha a réiteach agus cuirfear bac ar na torthaí freisin + featured_tag: + name: 'Seo cuid de na hashtags a d’úsáid tú le déanaí:' + filters: + action: Roghnaigh an gníomh ba cheart a dhéanamh nuair a mheaitseálann postáil an scagaire + actions: + hide: Cuir an t-ábhar scagtha i bhfolach go hiomlán, ag iompar amhail is nach raibh sé ann + warn: Folaigh an t-ábhar scagtha taobh thiar de rabhadh a luann teideal an scagaire + form_admin_settings: + activity_api_enabled: Áireamh na bpost a foilsíodh go háitiúil, úsáideoirí gníomhacha, agus clárúcháin nua i buicéid seachtainiúla + app_icon: WEBP, PNG, GIF nó JPG. Sáraíonn sé an deilbhín réamhshocraithe aipe ar ghléasanna soghluaiste le deilbhín saincheaptha. + backups_retention_period: Tá an cumas ag úsáideoirí cartlanna dá gcuid post a ghiniúint le híoslódáil níos déanaí. Nuair a bheidh luach dearfach socraithe, scriosfar na cartlanna seo go huathoibríoch ó do stór tar éis an líon sonraithe laethanta. + bootstrap_timeline_accounts: Cuirfear na cuntais seo ar bharr na moltaí a leanann úsáideoirí nua. + closed_registrations_message: Ar taispeáint nuair a dhúntar clárúcháin + content_cache_retention_period: Scriosfar gach postáil ó fhreastalaithe eile (lena n-áirítear treisithe agus freagraí) tar éis an líon sonraithe laethanta, gan aird ar aon idirghníomhaíocht úsáideora áitiúil leis na postálacha sin. Áirítear leis seo postálacha ina bhfuil úsáideoir áitiúil tar éis é a mharcáil mar leabharmharcanna nó mar cheanáin. Caillfear tagairtí príobháideacha idir úsáideoirí ó chásanna éagsúla freisin agus ní féidir iad a athchóiriú. Tá úsáid an tsocraithe seo beartaithe le haghaidh cásanna sainchuspóra agus sáraítear go leor ionchais úsáideoirí nuair a chuirtear i bhfeidhm é le haghaidh úsáid ghinearálta. + custom_css: Is féidir leat stíleanna saincheaptha a chur i bhfeidhm ar an leagan gréasáin de Mastodon. + favicon: WEBP, PNG, GIF nó JPG. Sáraíonn sé an favicon Mastodon réamhshocraithe le deilbhín saincheaptha. + mascot: Sáraíonn sé an léaráid san ardchomhéadan gréasáin. + media_cache_retention_period: Déantar comhaid meán ó phoist a dhéanann cianúsáideoirí a thaisceadh ar do fhreastalaí. Nuair a bheidh luach dearfach socraithe, scriosfar na meáin tar éis an líon sonraithe laethanta. Má iarrtar na sonraí meán tar éis é a scriosadh, déanfar é a ath-íoslódáil, má tá an t-ábhar foinse fós ar fáil. Mar gheall ar shrianta ar cé chomh minic is atá cártaí réamhamhairc ag vótaíocht do shuíomhanna tríú páirtí, moltar an luach seo a shocrú go 14 lá ar a laghad, nó ní dhéanfar cártaí réamhamhairc naisc a nuashonrú ar éileamh roimh an am sin. + peers_api_enabled: Liosta de na hainmneacha fearainn ar tháinig an freastalaí seo orthu sa choinbhleacht. Níl aon sonraí san áireamh anseo faoi cé acu an ndéanann tú cónascadh le freastalaí ar leith, díreach go bhfuil a fhios ag do fhreastalaí faoi. Úsáideann seirbhísí a bhailíonn staitisticí ar chónaidhm go ginearálta é seo. + profile_directory: Liostaíonn an t-eolaire próifíle na húsáideoirí go léir a roghnaigh isteach le bheith in-aimsithe. + require_invite_text: Nuair a bhíonn faomhadh láimhe ag teastáil le haghaidh clárúcháin, déan an "Cén fáth ar mhaith leat a bheith páirteach?" ionchur téacs éigeantach seachas roghnach + site_contact_email: Conas is féidir le daoine dul i dteagmháil leat le haghaidh fiosrúchán dlíthiúil nó tacaíochta. + site_contact_username: Conas is féidir le daoine dul i dteagmháil leat ar Mastodon. + site_extended_description: Aon fhaisnéis bhreise a d’fhéadfadh a bheith úsáideach do chuairteoirí agus d’úsáideoirí. Is féidir é a struchtúrú le comhréir Markdown. + site_short_description: Cur síos gairid chun cabhrú le do fhreastalaí a aithint go uathúil. Cé atá á rith, cé dó a bhfuil sé? + site_terms: Bain úsáid as do pholasaí príobháideachta féin nó fág bán é chun an réamhshocrú a úsáid. Is féidir é a struchtúrú le comhréir Markdown. + site_title: Conas is féidir le daoine tagairt a dhéanamh do do fhreastalaí seachas a ainm fearainn. + status_page_url: URL leathanach inar féidir le daoine stádas an fhreastalaí seo a fheiceáil le linn briseadh amach + theme: Téama a fheiceann cuairteoirí logáilte amach agus úsáideoirí nua. + thumbnail: Íomhá thart ar 2:1 ar taispeáint taobh le faisnéis do fhreastalaí. + timeline_preview: Beidh cuairteoirí logáilte amach in ann na postálacha poiblí is déanaí atá ar fáil ar an bhfreastalaí a bhrabhsáil. + trendable_by_default: Léim ar athbhreithniú láimhe ar ábhar treochta. Is féidir míreanna aonair a bhaint as treochtaí fós tar éis an fhíric. + trends: Léiríonn treochtaí cé na postálacha, hashtags agus scéalta nuachta atá ag tarraingt ar do fhreastalaí. + trends_as_landing_page: Taispeáin inneachar treochta d'úsáideoirí agus do chuairteoirí atá logáilte amach in ionad cur síos ar an bhfreastalaí seo. Éilíonn treochtaí a chumasú. + form_challenge: + current_password: Tá tú ag dul isteach i limistéar slán + imports: + data: Comhad CSV easpórtáilte ó fhreastalaí Mastodon eile + invite_request: + text: Cabhróidh sé seo linn d’iarratas a athbhreithniú + ip_block: + comment: Roghnach. Cuimhnigh cén fáth ar chuir tú an riail seo leis. + expires_in: Is acmhainn chríochta iad seoltaí IP, uaireanta roinntear iad agus is minic a athraíonn lámha. Ar an gcúis seo, ní mholtar bloic IP éiginnte. + ip: Cuir isteach seoladh IPv4 nó IPv6. Is féidir leat raonta iomlána a bhlocáil ag baint úsáide as an chomhréir CIDR. Bí cúramach gan tú féin a ghlasáil amach! + severities: + no_access: Cuir bac ar rochtain ar na hacmhainní go léir + sign_up_block: Ní bheidh clárú nua indéanta + sign_up_requires_approval: Beidh do cheadú ag teastáil le haghaidh clárúcháin nua + severity: Roghnaigh cad a tharlóidh le hiarratais ón IP seo + rule: + hint: Roghnach. Tabhair tuilleadh sonraí faoin riail + text: Déan cur síos ar riail nó riachtanas d'úsáideoirí ar an bhfreastalaí seo. Déan iarracht é a choinneáil gearr agus simplí + sessions: + otp: 'Cuir isteach an cód dhá fhachtóir ginte ag d''aip ghutháin nó úsáid ceann de do chóid athshlánaithe:' + webauthn: Más eochair USB atá ann déan cinnte é a chur isteach agus, más gá, tapáil í. + settings: + indexable: Seans go mbeidh do leathanach próifíle le feiceáil i dtorthaí cuardaigh ar Google, Bing agus eile. + show_application: Beidh tú in ann a fheiceáil i gcónaí cén aip a d’fhoilsigh do phostáil beag beann ar. + tag: + name: Ní féidir leat ach cásáil na litreacha a athrú, mar shampla, chun é a dhéanamh níos inléite + user: + chosen_languages: Nuair a dhéantar iad a sheiceáil, ní thaispeánfar ach postálacha i dteangacha roghnaithe in amlínte poiblí + role: Rialaíonn an ról na ceadanna atá ag an úsáideoir + user_role: + color: Dath le húsáid don ról ar fud an Chomhéadain, mar RGB i bhformáid heicsidheachúlach + highlighted: Déanann sé seo an ról le feiceáil go poiblí + name: Ainm poiblí an róil, má tá an ról socraithe le taispeáint mar shuaitheantas + permissions_as_keys: Beidh rochtain ag úsáideoirí a bhfuil an ról seo acu ar... + position: Cinneann ról níos airde réiteach coinbhleachta i gcásanna áirithe. Ní féidir gníomhartha áirithe a dhéanamh ach amháin ar róil a bhfuil tosaíocht níos ísle acu + webhook: + events: Roghnaigh imeachtaí le seoladh + template: Cum do phálasta JSON féin ag baint úsáide as idirshuíomh athróg. Fág bán le haghaidh JSON réamhshocraithe. + url: An áit a seolfar imeachtaí chuig labels: account: + discoverable: Próifíl gné agus postálacha in halgartaim fionnachtana fields: name: Lipéad + value: Ábhar + indexable: Cuir postálacha poiblí san áireamh sna torthaí cuardaigh + show_collections: Taispeáin seo a leanas agus leanúna ar phróifíl + unlocked: Glac le leantóirí nua go huathoibríoch + account_alias: + acct: Láimhseáil an seanchuntais + account_migration: + acct: Láimhseáil an chuntais nua account_warning_preset: + text: Téacs réamhshocraithe title: Teideal admin_account_action: + include_statuses: Cuir postálacha tuairiscithe san áireamh sa ríomhphost + send_email_notification: Cuir an t-úsáideoir ar an eolas trí ríomhphost text: Rabhadh saincheaptha + type: Gníomh types: disable: Reoigh none: Seol rabhadh + sensitive: Íogair silence: Teorannaigh suspend: Cuir ar fionraí + warning_preset_id: Bain úsáid as réamhshocrú rabhaidh announcement: + all_day: Imeacht uile-lae + ends_at: Deireadh an imeachta + scheduled_at: Foilsiú sceideal + starts_at: Tús na hócáide text: Fógra + appeal: + text: Mínigh cén fáth ar cheart an cinneadh seo a fhreaschur defaults: + autofollow: Tabhair cuireadh do chuntas a leanúint avatar: Abhatár + bot: Is cuntas uathoibrithe é seo + chosen_languages: Scag teangacha + confirm_new_password: Deimhnigh pasfhocal nua + confirm_password: Deimhnigh Pasfhocal + context: Comhthéacsanna a scagadh + current_password: Pasfhocal reatha data: Sonraí display_name: Ainm taispeána email: Seoladh ríomhphoist + expires_in: In éag tar éis + fields: Réimsí breise header: Ceanntásc + honeypot: "%{label} (ná líon isteach)" + inbox_url: URL an bhosca isteach sealaíochta + irreversible: Droim ar aghaidh in ionad bheith ag folaigh + locale: Teanga comhéadan + max_uses: Uaslíon úsáidí new_password: Pasfhocal nua note: Beathaisnéis + otp_attempt: Cód dhá-fhachtóir password: Pasfhocal + phrase: Eochairfhocal nó frása + setting_advanced_layout: Cumasaigh ardchomhéadan gréasáin + setting_aggregate_reblogs: Treisithe grúpa i línte ama + setting_always_send_emails: Seol fógraí ríomhphoist i gcónaí + setting_auto_play_gif: Gifs beoite go huathoibríoch a imirt + setting_boost_modal: Taispeáin dialóg deimhnithe roimh threisiú + setting_default_language: Teanga postála + setting_default_privacy: Postáil príobháideachta + setting_default_sensitive: Marcáil na meáin mar íogair i gcónaí + setting_delete_modal: Taispeáin dialóg deimhnithe sula scriostar postáil + setting_disable_hover_cards: Díchumasaigh réamhamharc próifíle ar ainlíon + setting_disable_swiping: Díchumasaigh gluaiseachtaí swiping + setting_display_media: Taispeáint meáin setting_display_media_default: Réamhshocrú setting_display_media_hide_all: Cuir uile i bhfolach setting_display_media_show_all: Taispeáin uile + setting_expand_spoilers: Méadaigh postálacha atá marcáilte le rabhaidh inneachair i gcónaí + setting_hide_network: Folaigh do ghraf sóisialta + setting_reduce_motion: Laghdú ar an tairiscint i beochan + setting_system_font_ui: Úsáid cló réamhshocraithe an chórais setting_theme: Téama suímh setting_trends: Taispeáin treochtaí an lae inniu + setting_unfollow_modal: Taispeáin dialóg deimhnithe sula ndíleanfaidh tú duine éigin + setting_use_blurhash: Taispeáin grádáin ildaite do mheáin fholaithe + setting_use_pending_items: Modh mall + severity: Déine + sign_in_token_attempt: Cód slándála title: Teideal + type: Cineál iompórtála username: Ainm úsáideora + username_or_email: Ainm Úsáideora nó Ríomhphost + whole_word: Focal ar fad + email_domain_block: + with_dns_records: Cuir taifid MX agus IPanna an fhearainn san áireamh featured_tag: name: Haischlib filters: @@ -50,27 +242,100 @@ ga: hide: Cuir i bhfolach go hiomlán warn: Cuir i bhfolach le rabhadh form_admin_settings: + activity_api_enabled: Foilsigh staitisticí comhiomlána faoi ghníomhaíocht úsáideoirí san API + app_icon: Deilbhín aip + backups_retention_period: Tréimhse choinneála cartlainne úsáideora + bootstrap_timeline_accounts: Mol na cuntais seo d'úsáideoirí nua i gcónaí + closed_registrations_message: Teachtaireacht saincheaptha nuair nach bhfuil sínithe suas ar fáil + content_cache_retention_period: Tréimhse choinneála inneachair cianda + custom_css: CSS saincheaptha + favicon: Favicon + mascot: Mascóg saincheaptha (oidhreacht) + media_cache_retention_period: Tréimhse choinneála taisce meán + peers_api_enabled: Foilsigh liosta de na freastalaithe aimsithe san API + profile_directory: Cumasaigh eolaire próifíle + registrations_mode: Cé atá in ann clárú + require_invite_text: A cheangal ar chúis a bheith páirteach + show_domain_blocks: Taispeáin bloic fearainn + show_domain_blocks_rationale: Taispeáin cén fáth ar cuireadh bac ar fhearann + site_contact_email: R-phost teagmhála + site_contact_username: Ainm úsáideora teagmhála site_extended_description: Cur síos fada site_short_description: Cur síos freastalaí site_terms: Polasaí príobháideachais site_title: Ainm freastalaí + status_page_url: URL an leathanaigh stádais + theme: Téama réamhshocraithe + thumbnail: Mionsamhail freastalaí + timeline_preview: Ceadaigh rochtain neamhdheimhnithe ar amlínte poiblí + trendable_by_default: Ceadaigh treochtaí gan athbhreithniú roimh ré + trends: Cumasaigh treochtaí + trends_as_landing_page: Úsáid treochtaí mar an leathanach tuirlingthe + interactions: + must_be_follower: Cuir bac ar fhógraí ó dhaoine nach leantóirí iad + must_be_following: Cuir bac ar fhógraí ó dhaoine nach leanann tú + must_be_following_dm: Cuir bac ar theachtaireachtaí díreacha ó dhaoine nach leanann tú invite: comment: Ráiteas + invite_request: + text: Cén fáth ar mhaith leat a bheith páirteach? ip_block: comment: Ráiteas ip: IP + severities: + no_access: Rochtain a bhlocáil + sign_up_block: Cuir bac ar chlárúcháin + sign_up_requires_approval: Teorainn le clárú severity: Riail notification_emails: + appeal: Déanann duine éigin achomharc i gcoinne chinneadh modhnóra + digest: Seol r-phoist achoimre + favourite: Is fearr le duine éigin do phostáil follow: Lean duine éigin tú + follow_request: D'iarr duine éigin tú a leanúint + mention: Luaigh duine éigin tú + pending_account: Ní mór athbhreithniú a dhéanamh ar chuntas nua reblog: Mhol duine éigin do phostáil + report: Tá tuairisc nua curtha isteach + software_updates: + all: Fógra a thabhairt ar gach nuashonrú + critical: Fógra a thabhairt ar nuashonruithe ríthábhachtacha amháin + label: Tá leagan nua Mastodon ar fáil + none: Ná cuir nuashonruithe ar an eolas choíche (ní mholtar é) + patch: Fógra ar nuashonruithe bugfix + trending_tag: Teastaíonn athbhreithniú ar threocht nua rule: + hint: Eolas breise text: Riail + settings: + indexable: Cuir leathanach próifíle san innill chuardaigh + show_application: Taispeáin cén aip ónar sheol tú postáil tag: + listable: Lig don hashchlib seo a bheith le feiceáil i gcuardach agus i moltaí name: Haischlib + trendable: Lig don haischlib seo a bheith le feiceáil faoi threochtaí + usable: Lig do phostálacha an hashchlib seo a úsáid user: role: Ról + time_zone: Crios ama user_role: + color: Dath suaitheantas + highlighted: Taispeáin ról mar shuaitheantas ar phróifílí úsáideora name: Ainm + permissions_as_keys: Ceadanna + position: Tosaíocht + webhook: + events: Imeachtaí cumasaithe + template: Teimpléad pá-ualach + url: URL críochphointe + 'no': Níl + not_recommended: Ní mholtar + overridden: Sáraithe recommended: Molta required: mark: "*" + text: ag teastáil + title: + sessions: + webauthn: Úsáid ceann de d'eochracha slándála chun síniú isteach + 'yes': Tá diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 344368a08a8776..f63aadd6c8f794 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -663,6 +663,7 @@ sl: report: 'Prijavi #%{id}' reported_account: Prijavljeni račun reported_by: Prijavil/a + reported_with_application: Prijavljeno s programom resolved: Razrešeni resolved_msg: Prijava je uspešno razrešena! skip_to_actions: Preskoči na dejanja diff --git a/config/locales/tr.yml b/config/locales/tr.yml index 6b28e17441ad4c..9855b569241153 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -639,6 +639,7 @@ tr: report: 'Şikayet #%{id}' reported_account: Şikayet edilen hesap reported_by: Şikayet eden + reported_with_application: Uygulamayla bildirildi resolved: Giderildi resolved_msg: Şikayet başarıyla çözümlendi! skip_to_actions: İşlemlere atla From f587ff643f552a32a1c43e103a474a5065cd3657 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Thu, 18 Jul 2024 16:36:09 +0200 Subject: [PATCH 095/101] Grouped Notifications UI (#30440) Co-authored-by: Eugen Rochko Co-authored-by: Claire --- .../api/v2_alpha/notifications_controller.rb | 55 +- app/javascript/mastodon/actions/markers.ts | 14 +- .../mastodon/actions/notification_groups.ts | 144 +++++ .../mastodon/actions/notifications.js | 13 +- .../actions/notifications_migration.tsx | 18 + .../mastodon/actions/notifications_typed.ts | 9 +- app/javascript/mastodon/actions/streaming.js | 11 +- app/javascript/mastodon/api/notifications.ts | 18 + .../mastodon/api_types/notifications.ts | 145 +++++ app/javascript/mastodon/api_types/reports.ts | 16 + .../mastodon/components/load_gap.tsx | 12 +- app/javascript/mastodon/components/status.jsx | 8 +- .../mastodon/components/status_list.jsx | 2 +- .../compose/components/edit_indicator.jsx | 10 +- .../compose/components/reply_indicator.jsx | 10 +- .../components/column_settings.jsx | 11 + .../filtered_notifications_banner.tsx | 4 +- .../components/moderation_warning.tsx | 51 +- .../notifications/components/notification.jsx | 4 +- .../relationships_severance_event.jsx | 15 +- .../containers/column_settings_container.js | 8 +- .../mastodon/features/notifications/index.jsx | 2 +- .../components/avatar_group.tsx | 31 ++ .../components/embedded_status.tsx | 93 ++++ .../components/embedded_status_content.tsx | 165 ++++++ .../components/names_list.tsx | 51 ++ .../components/notification_admin_report.tsx | 132 +++++ .../components/notification_admin_sign_up.tsx | 31 ++ .../components/notification_favourite.tsx | 45 ++ .../components/notification_follow.tsx | 31 ++ .../notification_follow_request.tsx | 78 +++ .../components/notification_group.tsx | 134 +++++ .../notification_group_with_status.tsx | 91 ++++ .../components/notification_mention.tsx | 55 ++ .../notification_moderation_warning.tsx | 13 + .../components/notification_poll.tsx | 41 ++ .../components/notification_reblog.tsx | 45 ++ .../notification_severed_relationships.tsx | 15 + .../components/notification_status.tsx | 31 ++ .../components/notification_update.tsx | 31 ++ .../components/notification_with_status.tsx | 73 +++ .../features/notifications_v2/filter_bar.tsx | 145 +++++ .../features/notifications_v2/index.tsx | 354 ++++++++++++ .../features/notifications_wrapper.jsx | 13 + .../features/ui/components/columns_area.jsx | 4 +- .../ui/components/navigation_panel.jsx | 9 +- app/javascript/mastodon/features/ui/index.jsx | 9 +- .../features/ui/util/async-components.js | 10 +- app/javascript/mastodon/locales/en.json | 15 +- .../mastodon/models/notification_group.ts | 207 +++++++ app/javascript/mastodon/reducers/index.ts | 2 + app/javascript/mastodon/reducers/markers.ts | 22 +- .../mastodon/reducers/notification_groups.ts | 508 ++++++++++++++++++ .../mastodon/reducers/notifications.js | 4 +- .../mastodon/selectors/notifications.ts | 34 ++ app/javascript/mastodon/selectors/settings.ts | 40 ++ .../styles/mastodon/components.scss | 293 +++++++++- app/models/notification.rb | 1 + app/models/notification_group.rb | 8 +- .../rest/notification_group_serializer.rb | 1 + .../rest/notification_serializer.rb | 1 + app/services/notify_service.rb | 4 +- config/routes.rb | 1 + package.json | 1 + yarn.lock | 10 + 65 files changed, 3330 insertions(+), 132 deletions(-) create mode 100644 app/javascript/mastodon/actions/notification_groups.ts create mode 100644 app/javascript/mastodon/actions/notifications_migration.tsx create mode 100644 app/javascript/mastodon/api/notifications.ts create mode 100644 app/javascript/mastodon/api_types/notifications.ts create mode 100644 app/javascript/mastodon/api_types/reports.ts create mode 100644 app/javascript/mastodon/features/notifications_v2/components/avatar_group.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/embedded_status.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/embedded_status_content.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/names_list.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_admin_report.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_admin_sign_up.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_favourite.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_follow.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_follow_request.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_group.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_group_with_status.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_mention.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_moderation_warning.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_poll.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_reblog.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_severed_relationships.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_status.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_update.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_with_status.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/filter_bar.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/index.tsx create mode 100644 app/javascript/mastodon/features/notifications_wrapper.jsx create mode 100644 app/javascript/mastodon/models/notification_group.ts create mode 100644 app/javascript/mastodon/reducers/notification_groups.ts create mode 100644 app/javascript/mastodon/selectors/notifications.ts create mode 100644 app/javascript/mastodon/selectors/settings.ts diff --git a/app/controllers/api/v2_alpha/notifications_controller.rb b/app/controllers/api/v2_alpha/notifications_controller.rb index edba23ab4a823c..83d40a0886f52f 100644 --- a/app/controllers/api/v2_alpha/notifications_controller.rb +++ b/app/controllers/api/v2_alpha/notifications_controller.rb @@ -12,10 +12,27 @@ def index with_read_replica do @notifications = load_notifications @group_metadata = load_group_metadata + @grouped_notifications = load_grouped_notifications @relationships = StatusRelationshipsPresenter.new(target_statuses_from_notifications, current_user&.account_id) + @sample_accounts = @grouped_notifications.flat_map(&:sample_accounts) + + # Preload associations to avoid N+1s + ActiveRecord::Associations::Preloader.new(records: @sample_accounts, associations: [:account_stat, { user: :role }]).call end - render json: @notifications.map { |notification| NotificationGroup.from_notification(notification, max_id: @group_metadata.dig(notification.group_key, :max_id)) }, each_serializer: REST::NotificationGroupSerializer, relationships: @relationships, group_metadata: @group_metadata + MastodonOTELTracer.in_span('Api::V2Alpha::NotificationsController#index rendering') do |span| + statuses = @grouped_notifications.filter_map { |group| group.target_status&.id } + + span.add_attributes( + 'app.notification_grouping.count' => @grouped_notifications.size, + 'app.notification_grouping.sample_account.count' => @sample_accounts.size, + 'app.notification_grouping.sample_account.unique_count' => @sample_accounts.pluck(:id).uniq.size, + 'app.notification_grouping.status.count' => statuses.size, + 'app.notification_grouping.status.unique_count' => statuses.uniq.size + ) + + render json: @grouped_notifications, each_serializer: REST::NotificationGroupSerializer, relationships: @relationships, group_metadata: @group_metadata + end end def show @@ -36,25 +53,35 @@ def dismiss private def load_notifications - notifications = browserable_account_notifications.includes(from_account: [:account_stat, :user]).to_a_grouped_paginated_by_id( - limit_param(DEFAULT_NOTIFICATIONS_LIMIT), - params_slice(:max_id, :since_id, :min_id) - ) - - Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses| - preload_collection(target_statuses, Status) + MastodonOTELTracer.in_span('Api::V2Alpha::NotificationsController#load_notifications') do + notifications = browserable_account_notifications.includes(from_account: [:account_stat, :user]).to_a_grouped_paginated_by_id( + limit_param(DEFAULT_NOTIFICATIONS_LIMIT), + params_slice(:max_id, :since_id, :min_id) + ) + + Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses| + preload_collection(target_statuses, Status) + end end end def load_group_metadata return {} if @notifications.empty? - browserable_account_notifications - .where(group_key: @notifications.filter_map(&:group_key)) - .where(id: (@notifications.last.id)..(@notifications.first.id)) - .group(:group_key) - .pluck(:group_key, 'min(notifications.id) as min_id', 'max(notifications.id) as max_id', 'max(notifications.created_at) as latest_notification_at') - .to_h { |group_key, min_id, max_id, latest_notification_at| [group_key, { min_id: min_id, max_id: max_id, latest_notification_at: latest_notification_at }] } + MastodonOTELTracer.in_span('Api::V2Alpha::NotificationsController#load_group_metadata') do + browserable_account_notifications + .where(group_key: @notifications.filter_map(&:group_key)) + .where(id: (@notifications.last.id)..(@notifications.first.id)) + .group(:group_key) + .pluck(:group_key, 'min(notifications.id) as min_id', 'max(notifications.id) as max_id', 'max(notifications.created_at) as latest_notification_at') + .to_h { |group_key, min_id, max_id, latest_notification_at| [group_key, { min_id: min_id, max_id: max_id, latest_notification_at: latest_notification_at }] } + end + end + + def load_grouped_notifications + MastodonOTELTracer.in_span('Api::V2Alpha::NotificationsController#load_grouped_notifications') do + @notifications.map { |notification| NotificationGroup.from_notification(notification, max_id: @group_metadata.dig(notification.group_key, :max_id)) } + end end def browserable_account_notifications diff --git a/app/javascript/mastodon/actions/markers.ts b/app/javascript/mastodon/actions/markers.ts index 03f577c540fc5d..77d91d9b9c3411 100644 --- a/app/javascript/mastodon/actions/markers.ts +++ b/app/javascript/mastodon/actions/markers.ts @@ -75,9 +75,17 @@ interface MarkerParam { } function getLastNotificationId(state: RootState): string | undefined { - // @ts-expect-error state.notifications is not yet typed - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call - return state.getIn(['notifications', 'lastReadId']); + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access + const enableBeta = state.settings.getIn( + ['notifications', 'groupingBeta'], + false, + ) as boolean; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return enableBeta + ? state.notificationGroups.lastReadId + : // @ts-expect-error state.notifications is not yet typed + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + state.getIn(['notifications', 'lastReadId']); } const buildPostMarkersParams = (state: RootState) => { diff --git a/app/javascript/mastodon/actions/notification_groups.ts b/app/javascript/mastodon/actions/notification_groups.ts new file mode 100644 index 00000000000000..8fdec6e48bb4df --- /dev/null +++ b/app/javascript/mastodon/actions/notification_groups.ts @@ -0,0 +1,144 @@ +import { createAction } from '@reduxjs/toolkit'; + +import { + apiClearNotifications, + apiFetchNotifications, +} from 'mastodon/api/notifications'; +import type { ApiAccountJSON } from 'mastodon/api_types/accounts'; +import type { + ApiNotificationGroupJSON, + ApiNotificationJSON, +} from 'mastodon/api_types/notifications'; +import { allNotificationTypes } from 'mastodon/api_types/notifications'; +import type { ApiStatusJSON } from 'mastodon/api_types/statuses'; +import type { NotificationGap } from 'mastodon/reducers/notification_groups'; +import { + selectSettingsNotificationsExcludedTypes, + selectSettingsNotificationsQuickFilterActive, +} from 'mastodon/selectors/settings'; +import type { AppDispatch } from 'mastodon/store'; +import { + createAppAsyncThunk, + createDataLoadingThunk, +} from 'mastodon/store/typed_functions'; + +import { importFetchedAccounts, importFetchedStatuses } from './importer'; +import { NOTIFICATIONS_FILTER_SET } from './notifications'; +import { saveSettings } from './settings'; + +function excludeAllTypesExcept(filter: string) { + return allNotificationTypes.filter((item) => item !== filter); +} + +function dispatchAssociatedRecords( + dispatch: AppDispatch, + notifications: ApiNotificationGroupJSON[] | ApiNotificationJSON[], +) { + const fetchedAccounts: ApiAccountJSON[] = []; + const fetchedStatuses: ApiStatusJSON[] = []; + + notifications.forEach((notification) => { + if ('sample_accounts' in notification) { + fetchedAccounts.push(...notification.sample_accounts); + } + + if (notification.type === 'admin.report') { + fetchedAccounts.push(notification.report.target_account); + } + + if (notification.type === 'moderation_warning') { + fetchedAccounts.push(notification.moderation_warning.target_account); + } + + if ('status' in notification) { + fetchedStatuses.push(notification.status); + } + }); + + if (fetchedAccounts.length > 0) + dispatch(importFetchedAccounts(fetchedAccounts)); + + if (fetchedStatuses.length > 0) + dispatch(importFetchedStatuses(fetchedStatuses)); +} + +export const fetchNotifications = createDataLoadingThunk( + 'notificationGroups/fetch', + async (_params, { getState }) => { + const activeFilter = + selectSettingsNotificationsQuickFilterActive(getState()); + + return apiFetchNotifications({ + exclude_types: + activeFilter === 'all' + ? selectSettingsNotificationsExcludedTypes(getState()) + : excludeAllTypesExcept(activeFilter), + }); + }, + ({ notifications }, { dispatch }) => { + dispatchAssociatedRecords(dispatch, notifications); + const payload: (ApiNotificationGroupJSON | NotificationGap)[] = + notifications; + + // TODO: might be worth not using gaps for that… + // if (nextLink) payload.push({ type: 'gap', loadUrl: nextLink.uri }); + if (notifications.length > 1) + payload.push({ type: 'gap', maxId: notifications.at(-1)?.page_min_id }); + + return payload; + // dispatch(submitMarkers()); + }, +); + +export const fetchNotificationsGap = createDataLoadingThunk( + 'notificationGroups/fetchGap', + async (params: { gap: NotificationGap }) => + apiFetchNotifications({ max_id: params.gap.maxId }), + + ({ notifications }, { dispatch }) => { + dispatchAssociatedRecords(dispatch, notifications); + + return { notifications }; + }, +); + +export const processNewNotificationForGroups = createAppAsyncThunk( + 'notificationGroups/processNew', + (notification: ApiNotificationJSON, { dispatch }) => { + dispatchAssociatedRecords(dispatch, [notification]); + + return notification; + }, +); + +export const loadPending = createAction('notificationGroups/loadPending'); + +export const updateScrollPosition = createAction<{ top: boolean }>( + 'notificationGroups/updateScrollPosition', +); + +export const setNotificationsFilter = createAppAsyncThunk( + 'notifications/filter/set', + ({ filterType }: { filterType: string }, { dispatch }) => { + dispatch({ + type: NOTIFICATIONS_FILTER_SET, + path: ['notifications', 'quickFilter', 'active'], + value: filterType, + }); + // dispatch(expandNotifications({ forceLoad: true })); + void dispatch(fetchNotifications()); + dispatch(saveSettings()); + }, +); + +export const clearNotifications = createDataLoadingThunk( + 'notifications/clear', + () => apiClearNotifications(), +); + +export const markNotificationsAsRead = createAction( + 'notificationGroups/markAsRead', +); + +export const mountNotifications = createAction('notificationGroups/mount'); +export const unmountNotifications = createAction('notificationGroups/unmount'); diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js index 6a59d5624e80c4..7e4320c27b7d5b 100644 --- a/app/javascript/mastodon/actions/notifications.js +++ b/app/javascript/mastodon/actions/notifications.js @@ -32,7 +32,6 @@ export const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL'; export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET'; -export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR'; export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP'; export const NOTIFICATIONS_LOAD_PENDING = 'NOTIFICATIONS_LOAD_PENDING'; @@ -174,7 +173,7 @@ const noOp = () => {}; let expandNotificationsController = new AbortController(); -export function expandNotifications({ maxId, forceLoad } = {}, done = noOp) { +export function expandNotifications({ maxId, forceLoad = false } = {}, done = noOp) { return (dispatch, getState) => { const activeFilter = getState().getIn(['settings', 'notifications', 'quickFilter', 'active']); const notifications = getState().get('notifications'); @@ -257,16 +256,6 @@ export function expandNotificationsFail(error, isLoadingMore) { }; } -export function clearNotifications() { - return (dispatch) => { - dispatch({ - type: NOTIFICATIONS_CLEAR, - }); - - api().post('/api/v1/notifications/clear'); - }; -} - export function scrollTopNotifications(top) { return { type: NOTIFICATIONS_SCROLL_TOP, diff --git a/app/javascript/mastodon/actions/notifications_migration.tsx b/app/javascript/mastodon/actions/notifications_migration.tsx new file mode 100644 index 00000000000000..f856e56828f11f --- /dev/null +++ b/app/javascript/mastodon/actions/notifications_migration.tsx @@ -0,0 +1,18 @@ +import { createAppAsyncThunk } from 'mastodon/store'; + +import { fetchNotifications } from './notification_groups'; +import { expandNotifications } from './notifications'; + +export const initializeNotifications = createAppAsyncThunk( + 'notifications/initialize', + (_, { dispatch, getState }) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access + const enableBeta = getState().settings.getIn( + ['notifications', 'groupingBeta'], + false, + ) as boolean; + + if (enableBeta) void dispatch(fetchNotifications()); + else dispatch(expandNotifications()); + }, +); diff --git a/app/javascript/mastodon/actions/notifications_typed.ts b/app/javascript/mastodon/actions/notifications_typed.ts index 176362f4b1edd4..88d942d45e54b5 100644 --- a/app/javascript/mastodon/actions/notifications_typed.ts +++ b/app/javascript/mastodon/actions/notifications_typed.ts @@ -1,11 +1,6 @@ import { createAction } from '@reduxjs/toolkit'; -import type { ApiAccountJSON } from '../api_types/accounts'; -// To be replaced once ApiNotificationJSON type exists -interface FakeApiNotificationJSON { - type: string; - account: ApiAccountJSON; -} +import type { ApiNotificationJSON } from 'mastodon/api_types/notifications'; export const notificationsUpdate = createAction( 'notifications/update', @@ -13,7 +8,7 @@ export const notificationsUpdate = createAction( playSound, ...args }: { - notification: FakeApiNotificationJSON; + notification: ApiNotificationJSON; usePendingItems: boolean; playSound: boolean; }) => ({ diff --git a/app/javascript/mastodon/actions/streaming.js b/app/javascript/mastodon/actions/streaming.js index e7fe1c53ed09ed..f50f41b0d9cd4b 100644 --- a/app/javascript/mastodon/actions/streaming.js +++ b/app/javascript/mastodon/actions/streaming.js @@ -10,6 +10,7 @@ import { deleteAnnouncement, } from './announcements'; import { updateConversations } from './conversations'; +import { processNewNotificationForGroups } from './notification_groups'; import { updateNotifications, expandNotifications } from './notifications'; import { updateStatus } from './statuses'; import { @@ -98,10 +99,16 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti case 'delete': dispatch(deleteFromTimelines(data.payload)); break; - case 'notification': + case 'notification': { // @ts-expect-error - dispatch(updateNotifications(JSON.parse(data.payload), messages, locale)); + const notificationJSON = JSON.parse(data.payload); + dispatch(updateNotifications(notificationJSON, messages, locale)); + // TODO: remove this once the groups feature replaces the previous one + if(getState().notificationGroups.groups.length > 0) { + dispatch(processNewNotificationForGroups(notificationJSON)); + } break; + } case 'conversation': // @ts-expect-error dispatch(updateConversations(JSON.parse(data.payload))); diff --git a/app/javascript/mastodon/api/notifications.ts b/app/javascript/mastodon/api/notifications.ts new file mode 100644 index 00000000000000..c1ab6f70cafbde --- /dev/null +++ b/app/javascript/mastodon/api/notifications.ts @@ -0,0 +1,18 @@ +import api, { apiRequest, getLinks } from 'mastodon/api'; +import type { ApiNotificationGroupJSON } from 'mastodon/api_types/notifications'; + +export const apiFetchNotifications = async (params?: { + exclude_types?: string[]; + max_id?: string; +}) => { + const response = await api().request({ + method: 'GET', + url: '/api/v2_alpha/notifications', + params, + }); + + return { notifications: response.data, links: getLinks(response) }; +}; + +export const apiClearNotifications = () => + apiRequest('POST', 'v1/notifications/clear'); diff --git a/app/javascript/mastodon/api_types/notifications.ts b/app/javascript/mastodon/api_types/notifications.ts new file mode 100644 index 00000000000000..d7cbbca73b9ec3 --- /dev/null +++ b/app/javascript/mastodon/api_types/notifications.ts @@ -0,0 +1,145 @@ +// See app/serializers/rest/notification_group_serializer.rb + +import type { AccountWarningAction } from 'mastodon/models/notification_group'; + +import type { ApiAccountJSON } from './accounts'; +import type { ApiReportJSON } from './reports'; +import type { ApiStatusJSON } from './statuses'; + +// See app/model/notification.rb +export const allNotificationTypes = [ + 'follow', + 'follow_request', + 'favourite', + 'reblog', + 'mention', + 'poll', + 'status', + 'update', + 'admin.sign_up', + 'admin.report', + 'moderation_warning', + 'severed_relationships', +]; + +export type NotificationWithStatusType = + | 'favourite' + | 'reblog' + | 'status' + | 'mention' + | 'poll' + | 'update'; + +export type NotificationType = + | NotificationWithStatusType + | 'follow' + | 'follow_request' + | 'moderation_warning' + | 'severed_relationships' + | 'admin.sign_up' + | 'admin.report'; + +export interface BaseNotificationJSON { + id: string; + type: NotificationType; + created_at: string; + group_key: string; + account: ApiAccountJSON; +} + +export interface BaseNotificationGroupJSON { + group_key: string; + notifications_count: number; + type: NotificationType; + sample_accounts: ApiAccountJSON[]; + latest_page_notification_at: string; // FIXME: This will only be present if the notification group is returned in a paginated list, not requested directly + most_recent_notification_id: string; + page_min_id?: string; + page_max_id?: string; +} + +interface NotificationGroupWithStatusJSON extends BaseNotificationGroupJSON { + type: NotificationWithStatusType; + status: ApiStatusJSON; +} + +interface NotificationWithStatusJSON extends BaseNotificationJSON { + type: NotificationWithStatusType; + status: ApiStatusJSON; +} + +interface ReportNotificationGroupJSON extends BaseNotificationGroupJSON { + type: 'admin.report'; + report: ApiReportJSON; +} + +interface ReportNotificationJSON extends BaseNotificationJSON { + type: 'admin.report'; + report: ApiReportJSON; +} + +type SimpleNotificationTypes = 'follow' | 'follow_request' | 'admin.sign_up'; +interface SimpleNotificationGroupJSON extends BaseNotificationGroupJSON { + type: SimpleNotificationTypes; +} + +interface SimpleNotificationJSON extends BaseNotificationJSON { + type: SimpleNotificationTypes; +} + +export interface ApiAccountWarningJSON { + id: string; + action: AccountWarningAction; + text: string; + status_ids: string[]; + created_at: string; + target_account: ApiAccountJSON; + appeal: unknown; +} + +interface ModerationWarningNotificationGroupJSON + extends BaseNotificationGroupJSON { + type: 'moderation_warning'; + moderation_warning: ApiAccountWarningJSON; +} + +interface ModerationWarningNotificationJSON extends BaseNotificationJSON { + type: 'moderation_warning'; + moderation_warning: ApiAccountWarningJSON; +} + +export interface ApiAccountRelationshipSeveranceEventJSON { + id: string; + type: 'account_suspension' | 'domain_block' | 'user_domain_block'; + purged: boolean; + target_name: string; + followers_count: number; + following_count: number; + created_at: string; +} + +interface AccountRelationshipSeveranceNotificationGroupJSON + extends BaseNotificationGroupJSON { + type: 'severed_relationships'; + event: ApiAccountRelationshipSeveranceEventJSON; +} + +interface AccountRelationshipSeveranceNotificationJSON + extends BaseNotificationJSON { + type: 'severed_relationships'; + event: ApiAccountRelationshipSeveranceEventJSON; +} + +export type ApiNotificationJSON = + | SimpleNotificationJSON + | ReportNotificationJSON + | AccountRelationshipSeveranceNotificationJSON + | NotificationWithStatusJSON + | ModerationWarningNotificationJSON; + +export type ApiNotificationGroupJSON = + | SimpleNotificationGroupJSON + | ReportNotificationGroupJSON + | AccountRelationshipSeveranceNotificationGroupJSON + | NotificationGroupWithStatusJSON + | ModerationWarningNotificationGroupJSON; diff --git a/app/javascript/mastodon/api_types/reports.ts b/app/javascript/mastodon/api_types/reports.ts new file mode 100644 index 00000000000000..b11cfdd2eb59c8 --- /dev/null +++ b/app/javascript/mastodon/api_types/reports.ts @@ -0,0 +1,16 @@ +import type { ApiAccountJSON } from './accounts'; + +export type ReportCategory = 'other' | 'spam' | 'legal' | 'violation'; + +export interface ApiReportJSON { + id: string; + action_taken: unknown; + action_taken_at: unknown; + category: ReportCategory; + comment: string; + forwarded: boolean; + created_at: string; + status_ids: string[]; + rule_ids: string[]; + target_account: ApiAccountJSON; +} diff --git a/app/javascript/mastodon/components/load_gap.tsx b/app/javascript/mastodon/components/load_gap.tsx index 1d4193a35951ab..544b5e1461b17f 100644 --- a/app/javascript/mastodon/components/load_gap.tsx +++ b/app/javascript/mastodon/components/load_gap.tsx @@ -9,18 +9,18 @@ const messages = defineMessages({ load_more: { id: 'status.load_more', defaultMessage: 'Load more' }, }); -interface Props { +interface Props { disabled: boolean; - maxId: string; - onClick: (maxId: string) => void; + param: T; + onClick: (params: T) => void; } -export const LoadGap: React.FC = ({ disabled, maxId, onClick }) => { +export const LoadGap = ({ disabled, param, onClick }: Props) => { const intl = useIntl(); const handleClick = useCallback(() => { - onClick(maxId); - }, [maxId, onClick]); + onClick(param); + }, [param, onClick]); return ( + ); +}; + +export const FilterBar: React.FC = () => { + const intl = useIntl(); + + const selectedFilter = useAppSelector( + selectSettingsNotificationsQuickFilterActive, + ); + const advancedMode = useAppSelector( + selectSettingsNotificationsQuickFilterAdvanced, + ); + + if (advancedMode) + return ( +
+ + + + + + + + + + + + + + + + + + + + + +
+ ); + else + return ( +
+ + + + + + +
+ ); +}; diff --git a/app/javascript/mastodon/features/notifications_v2/index.tsx b/app/javascript/mastodon/features/notifications_v2/index.tsx new file mode 100644 index 00000000000000..fc20f0583631ed --- /dev/null +++ b/app/javascript/mastodon/features/notifications_v2/index.tsx @@ -0,0 +1,354 @@ +import { useCallback, useEffect, useMemo, useRef } from 'react'; + +import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; + +import { Helmet } from 'react-helmet'; + +import { createSelector } from '@reduxjs/toolkit'; + +import { useDebouncedCallback } from 'use-debounce'; + +import DoneAllIcon from '@/material-icons/400-24px/done_all.svg?react'; +import NotificationsIcon from '@/material-icons/400-24px/notifications-fill.svg?react'; +import { + fetchNotificationsGap, + updateScrollPosition, + loadPending, + markNotificationsAsRead, + mountNotifications, + unmountNotifications, +} from 'mastodon/actions/notification_groups'; +import { compareId } from 'mastodon/compare_id'; +import { Icon } from 'mastodon/components/icon'; +import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator'; +import { useIdentity } from 'mastodon/identity_context'; +import type { NotificationGap } from 'mastodon/reducers/notification_groups'; +import { + selectUnreadNotificationGroupsCount, + selectPendingNotificationGroupsCount, +} from 'mastodon/selectors/notifications'; +import { + selectNeedsNotificationPermission, + selectSettingsNotificationsExcludedTypes, + selectSettingsNotificationsQuickFilterActive, + selectSettingsNotificationsQuickFilterShow, + selectSettingsNotificationsShowUnread, +} from 'mastodon/selectors/settings'; +import { useAppDispatch, useAppSelector } from 'mastodon/store'; +import type { RootState } from 'mastodon/store'; + +import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; +import { submitMarkers } from '../../actions/markers'; +import Column from '../../components/column'; +import { ColumnHeader } from '../../components/column_header'; +import { LoadGap } from '../../components/load_gap'; +import ScrollableList from '../../components/scrollable_list'; +import { FilteredNotificationsBanner } from '../notifications/components/filtered_notifications_banner'; +import NotificationsPermissionBanner from '../notifications/components/notifications_permission_banner'; +import ColumnSettingsContainer from '../notifications/containers/column_settings_container'; + +import { NotificationGroup } from './components/notification_group'; +import { FilterBar } from './filter_bar'; + +const messages = defineMessages({ + title: { id: 'column.notifications', defaultMessage: 'Notifications' }, + markAsRead: { + id: 'notifications.mark_as_read', + defaultMessage: 'Mark every notification as read', + }, +}); + +const getNotifications = createSelector( + [ + selectSettingsNotificationsQuickFilterShow, + selectSettingsNotificationsQuickFilterActive, + selectSettingsNotificationsExcludedTypes, + (state: RootState) => state.notificationGroups.groups, + ], + (showFilterBar, allowedType, excludedTypes, notifications) => { + if (!showFilterBar || allowedType === 'all') { + // used if user changed the notification settings after loading the notifications from the server + // otherwise a list of notifications will come pre-filtered from the backend + // we need to turn it off for FilterBar in order not to block ourselves from seeing a specific category + return notifications.filter( + (item) => item.type === 'gap' || !excludedTypes.includes(item.type), + ); + } + return notifications.filter( + (item) => item.type === 'gap' || allowedType === item.type, + ); + }, +); + +export const Notifications: React.FC<{ + columnId?: string; + multiColumn?: boolean; +}> = ({ columnId, multiColumn }) => { + const intl = useIntl(); + const notifications = useAppSelector(getNotifications); + const dispatch = useAppDispatch(); + const isLoading = useAppSelector((s) => s.notificationGroups.isLoading); + const hasMore = notifications.at(-1)?.type === 'gap'; + + const lastReadId = useAppSelector((s) => + selectSettingsNotificationsShowUnread(s) + ? s.notificationGroups.lastReadId + : '0', + ); + + const numPending = useAppSelector(selectPendingNotificationGroupsCount); + + const unreadNotificationsCount = useAppSelector( + selectUnreadNotificationGroupsCount, + ); + + const isUnread = unreadNotificationsCount > 0; + + const canMarkAsRead = + useAppSelector(selectSettingsNotificationsShowUnread) && + unreadNotificationsCount > 0; + + const needsNotificationPermission = useAppSelector( + selectNeedsNotificationPermission, + ); + + const columnRef = useRef(null); + + const selectChild = useCallback((index: number, alignTop: boolean) => { + const container = columnRef.current?.node as HTMLElement | undefined; + + if (!container) return; + + const element = container.querySelector( + `article:nth-of-type(${index + 1}) .focusable`, + ); + + if (element) { + if (alignTop && container.scrollTop > element.offsetTop) { + element.scrollIntoView(true); + } else if ( + !alignTop && + container.scrollTop + container.clientHeight < + element.offsetTop + element.offsetHeight + ) { + element.scrollIntoView(false); + } + element.focus(); + } + }, []); + + // Keep track of mounted components for unread notification handling + useEffect(() => { + dispatch(mountNotifications()); + + return () => { + dispatch(unmountNotifications()); + dispatch(updateScrollPosition({ top: false })); + }; + }, [dispatch]); + + const handleLoadGap = useCallback( + (gap: NotificationGap) => { + void dispatch(fetchNotificationsGap({ gap })); + }, + [dispatch], + ); + + const handleLoadOlder = useDebouncedCallback( + () => { + const gap = notifications.at(-1); + if (gap?.type === 'gap') void dispatch(fetchNotificationsGap({ gap })); + }, + 300, + { leading: true }, + ); + + const handleLoadPending = useCallback(() => { + dispatch(loadPending()); + }, [dispatch]); + + const handleScrollToTop = useDebouncedCallback(() => { + dispatch(updateScrollPosition({ top: true })); + }, 100); + + const handleScroll = useDebouncedCallback(() => { + dispatch(updateScrollPosition({ top: false })); + }, 100); + + useEffect(() => { + return () => { + handleLoadOlder.cancel(); + handleScrollToTop.cancel(); + handleScroll.cancel(); + }; + }, [handleLoadOlder, handleScrollToTop, handleScroll]); + + const handlePin = useCallback(() => { + if (columnId) { + dispatch(removeColumn(columnId)); + } else { + dispatch(addColumn('NOTIFICATIONS', {})); + } + }, [columnId, dispatch]); + + const handleMove = useCallback( + (dir: unknown) => { + dispatch(moveColumn(columnId, dir)); + }, + [dispatch, columnId], + ); + + const handleHeaderClick = useCallback(() => { + columnRef.current?.scrollTop(); + }, []); + + const handleMoveUp = useCallback( + (id: string) => { + const elementIndex = + notifications.findIndex( + (item) => item.type !== 'gap' && item.group_key === id, + ) - 1; + selectChild(elementIndex, true); + }, + [notifications, selectChild], + ); + + const handleMoveDown = useCallback( + (id: string) => { + const elementIndex = + notifications.findIndex( + (item) => item.type !== 'gap' && item.group_key === id, + ) + 1; + selectChild(elementIndex, false); + }, + [notifications, selectChild], + ); + + const handleMarkAsRead = useCallback(() => { + dispatch(markNotificationsAsRead()); + void dispatch(submitMarkers({ immediate: true })); + }, [dispatch]); + + const pinned = !!columnId; + const emptyMessage = ( + + ); + + const { signedIn } = useIdentity(); + + const filterBar = signedIn ? : null; + + const scrollableContent = useMemo(() => { + if (notifications.length === 0 && !hasMore) return null; + + return notifications.map((item) => + item.type === 'gap' ? ( + + ) : ( + 0 + } + /> + ), + ); + }, [ + notifications, + isLoading, + hasMore, + lastReadId, + handleLoadGap, + handleMoveUp, + handleMoveDown, + ]); + + const prepend = ( + <> + {needsNotificationPermission && } + + + ); + + const scrollContainer = signedIn ? ( + + {scrollableContent} + + ) : ( + + ); + + const extraButton = canMarkAsRead ? ( + + ) : null; + + return ( + + + + + + {filterBar} + + {scrollContainer} + + + {intl.formatMessage(messages.title)} + + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export default Notifications; diff --git a/app/javascript/mastodon/features/notifications_wrapper.jsx b/app/javascript/mastodon/features/notifications_wrapper.jsx new file mode 100644 index 00000000000000..057ed1b395f8ec --- /dev/null +++ b/app/javascript/mastodon/features/notifications_wrapper.jsx @@ -0,0 +1,13 @@ +import Notifications from 'mastodon/features/notifications'; +import Notifications_v2 from 'mastodon/features/notifications_v2'; +import { useAppSelector } from 'mastodon/store'; + +export const NotificationsWrapper = (props) => { + const optedInGroupedNotifications = useAppSelector((state) => state.getIn(['settings', 'notifications', 'groupingBeta'], false)); + + return ( + optedInGroupedNotifications ? : + ); +}; + +export default NotificationsWrapper; \ No newline at end of file diff --git a/app/javascript/mastodon/features/ui/components/columns_area.jsx b/app/javascript/mastodon/features/ui/components/columns_area.jsx index 19c2f40ac61c8f..063ac28d96fd26 100644 --- a/app/javascript/mastodon/features/ui/components/columns_area.jsx +++ b/app/javascript/mastodon/features/ui/components/columns_area.jsx @@ -10,7 +10,7 @@ import { scrollRight } from '../../../scroll'; import BundleContainer from '../containers/bundle_container'; import { Compose, - Notifications, + NotificationsWrapper, HomeTimeline, CommunityTimeline, PublicTimeline, @@ -32,7 +32,7 @@ import NavigationPanel from './navigation_panel'; const componentMap = { 'COMPOSE': Compose, 'HOME': HomeTimeline, - 'NOTIFICATIONS': Notifications, + 'NOTIFICATIONS': NotificationsWrapper, 'PUBLIC': PublicTimeline, 'REMOTE': PublicTimeline, 'COMMUNITY': CommunityTimeline, diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx index ff90eef359fb6d..2648923bfc4161 100644 --- a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx +++ b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx @@ -34,6 +34,7 @@ import { NavigationPortal } from 'mastodon/components/navigation_portal'; import { identityContextPropShape, withIdentity } from 'mastodon/identity_context'; import { timelinePreview, trendsEnabled } from 'mastodon/initial_state'; import { transientSingleColumn } from 'mastodon/is_mobile'; +import { selectUnreadNotificationGroupsCount } from 'mastodon/selectors/notifications'; import ColumnLink from './column_link'; import DisabledAccountBanner from './disabled_account_banner'; @@ -59,15 +60,19 @@ const messages = defineMessages({ }); const NotificationsLink = () => { + const optedInGroupedNotifications = useSelector((state) => state.getIn(['settings', 'notifications', 'groupingBeta'], false)); const count = useSelector(state => state.getIn(['notifications', 'unread'])); const intl = useIntl(); + const newCount = useSelector(selectUnreadNotificationGroupsCount); + return ( } - activeIcon={} + icon={} + activeIcon={} text={intl.formatMessage(messages.notifications)} /> ); diff --git a/app/javascript/mastodon/features/ui/index.jsx b/app/javascript/mastodon/features/ui/index.jsx index d9f609620cde7b..f36e0cf501fdbf 100644 --- a/app/javascript/mastodon/features/ui/index.jsx +++ b/app/javascript/mastodon/features/ui/index.jsx @@ -13,6 +13,7 @@ import { HotKeys } from 'react-hotkeys'; import { focusApp, unfocusApp, changeLayout } from 'mastodon/actions/app'; import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'mastodon/actions/markers'; +import { initializeNotifications } from 'mastodon/actions/notifications_migration'; import { INTRODUCTION_VERSION } from 'mastodon/actions/onboarding'; import { HoverCardController } from 'mastodon/components/hover_card_controller'; import { PictureInPicture } from 'mastodon/features/picture_in_picture'; @@ -22,7 +23,6 @@ import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { uploadCompose, resetCompose, changeComposeSpoilerness } from '../../actions/compose'; import { clearHeight } from '../../actions/height_cache'; -import { expandNotifications } from '../../actions/notifications'; import { fetchServer, fetchServerTranslationLanguages } from '../../actions/server'; import { expandHomeTimeline } from '../../actions/timelines'; import initialState, { me, owner, singleUserMode, trendsEnabled, trendsAsLanding, disableHoverCards } from '../../initial_state'; @@ -49,7 +49,7 @@ import { Favourites, DirectTimeline, HashtagTimeline, - Notifications, + NotificationsWrapper, NotificationRequests, NotificationRequest, FollowRequests, @@ -71,6 +71,7 @@ import { } from './util/async-components'; import { ColumnsContextProvider } from './util/columns_context'; import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers'; + // Dummy import, to make sure that ends up in the application bundle. // Without this it ends up in ~8 very commonly used bundles. import '../../components/status'; @@ -205,7 +206,7 @@ class SwitchingColumnsArea extends PureComponent { - + @@ -405,7 +406,7 @@ class UI extends PureComponent { if (signedIn) { this.props.dispatch(fetchMarkers()); this.props.dispatch(expandHomeTimeline()); - this.props.dispatch(expandNotifications()); + this.props.dispatch(initializeNotifications()); this.props.dispatch(fetchServerTranslationLanguages()); setTimeout(() => this.props.dispatch(fetchServer()), 3000); diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js index b8a2359d92cd3f..7c4372d5a6442b 100644 --- a/app/javascript/mastodon/features/ui/util/async-components.js +++ b/app/javascript/mastodon/features/ui/util/async-components.js @@ -7,7 +7,15 @@ export function Compose () { } export function Notifications () { - return import(/* webpackChunkName: "features/notifications" */'../../notifications'); + return import(/* webpackChunkName: "features/notifications_v1" */'../../notifications'); +} + +export function Notifications_v2 () { + return import(/* webpackChunkName: "features/notifications_v2" */'../../notifications_v2'); +} + +export function NotificationsWrapper () { + return import(/* webpackChunkName: "features/notifications" */'../../notifications_wrapper'); } export function HomeTimeline () { diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 13296e1d204479..60bdfd1d445471 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -443,6 +443,8 @@ "mute_modal.title": "Mute user?", "mute_modal.you_wont_see_mentions": "You won't see posts that mention them.", "mute_modal.you_wont_see_posts": "They can still see your posts, but you won't see theirs.", + "name_and_others": "{name} and {count, plural, one {# other} other {# others}}", + "name_and_others_with_link": "{name} and {count, plural, one {# other} other {# others}}", "navigation_bar.about": "About", "navigation_bar.advanced_interface": "Open in advanced web interface", "navigation_bar.blocks": "Blocked users", @@ -470,6 +472,10 @@ "navigation_bar.security": "Security", "not_signed_in_indicator.not_signed_in": "You need to login to access this resource.", "notification.admin.report": "{name} reported {target}", + "notification.admin.report_account": "{name} reported {count, plural, one {one post} other {# posts}} from {target} for {category}", + "notification.admin.report_account_other": "{name} reported {count, plural, one {one post} other {# posts}} from {target}", + "notification.admin.report_statuses": "{name} reported {target} for {category}", + "notification.admin.report_statuses_other": "{name} reported {target}", "notification.admin.sign_up": "{name} signed up", "notification.favourite": "{name} favorited your post", "notification.follow": "{name} followed you", @@ -485,7 +491,8 @@ "notification.moderation_warning.action_silence": "Your account has been limited.", "notification.moderation_warning.action_suspend": "Your account has been suspended.", "notification.own_poll": "Your poll has ended", - "notification.poll": "A poll you have voted in has ended", + "notification.poll": "A poll you voted in has ended", + "notification.private_mention": "{name} privately mentioned you", "notification.reblog": "{name} boosted your post", "notification.relationships_severance_event": "Lost connections with {name}", "notification.relationships_severance_event.account_suspension": "An admin from {from} has suspended {target}, which means you can no longer receive updates from them or interact with them.", @@ -503,6 +510,8 @@ "notifications.column_settings.admin.report": "New reports:", "notifications.column_settings.admin.sign_up": "New sign-ups:", "notifications.column_settings.alert": "Desktop notifications", + "notifications.column_settings.beta.category": "Experimental features", + "notifications.column_settings.beta.grouping": "Group notifications", "notifications.column_settings.favourite": "Favorites:", "notifications.column_settings.filter_bar.advanced": "Display all categories", "notifications.column_settings.filter_bar.category": "Quick filter bar", @@ -666,9 +675,13 @@ "report.unfollow_explanation": "You are following this account. To not see their posts in your home feed anymore, unfollow them.", "report_notification.attached_statuses": "{count, plural, one {{count} post} other {{count} posts}} attached", "report_notification.categories.legal": "Legal", + "report_notification.categories.legal_sentence": "illegal content", "report_notification.categories.other": "Other", + "report_notification.categories.other_sentence": "other", "report_notification.categories.spam": "Spam", + "report_notification.categories.spam_sentence": "spam", "report_notification.categories.violation": "Rule violation", + "report_notification.categories.violation_sentence": "rule violation", "report_notification.open": "Open report", "search.no_recent_searches": "No recent searches", "search.placeholder": "Search", diff --git a/app/javascript/mastodon/models/notification_group.ts b/app/javascript/mastodon/models/notification_group.ts new file mode 100644 index 00000000000000..5fe1e6f2e43dee --- /dev/null +++ b/app/javascript/mastodon/models/notification_group.ts @@ -0,0 +1,207 @@ +import type { + ApiAccountRelationshipSeveranceEventJSON, + ApiAccountWarningJSON, + BaseNotificationGroupJSON, + ApiNotificationGroupJSON, + ApiNotificationJSON, + NotificationType, + NotificationWithStatusType, +} from 'mastodon/api_types/notifications'; +import type { ApiReportJSON } from 'mastodon/api_types/reports'; + +// Maximum number of avatars displayed in a notification group +// This corresponds to the max lenght of `group.sampleAccountIds` +export const NOTIFICATIONS_GROUP_MAX_AVATARS = 8; + +interface BaseNotificationGroup + extends Omit { + sampleAccountIds: string[]; +} + +interface BaseNotificationWithStatus + extends BaseNotificationGroup { + type: Type; + statusId: string; +} + +interface BaseNotification + extends BaseNotificationGroup { + type: Type; +} + +export type NotificationGroupFavourite = + BaseNotificationWithStatus<'favourite'>; +export type NotificationGroupReblog = BaseNotificationWithStatus<'reblog'>; +export type NotificationGroupStatus = BaseNotificationWithStatus<'status'>; +export type NotificationGroupMention = BaseNotificationWithStatus<'mention'>; +export type NotificationGroupPoll = BaseNotificationWithStatus<'poll'>; +export type NotificationGroupUpdate = BaseNotificationWithStatus<'update'>; +export type NotificationGroupFollow = BaseNotification<'follow'>; +export type NotificationGroupFollowRequest = BaseNotification<'follow_request'>; +export type NotificationGroupAdminSignUp = BaseNotification<'admin.sign_up'>; + +export type AccountWarningAction = + | 'none' + | 'disable' + | 'mark_statuses_as_sensitive' + | 'delete_statuses' + | 'sensitive' + | 'silence' + | 'suspend'; +export interface AccountWarning + extends Omit { + targetAccountId: string; +} + +export interface NotificationGroupModerationWarning + extends BaseNotification<'moderation_warning'> { + moderationWarning: AccountWarning; +} + +type AccountRelationshipSeveranceEvent = + ApiAccountRelationshipSeveranceEventJSON; +export interface NotificationGroupSeveredRelationships + extends BaseNotification<'severed_relationships'> { + event: AccountRelationshipSeveranceEvent; +} + +interface Report extends Omit { + targetAccountId: string; +} + +export interface NotificationGroupAdminReport + extends BaseNotification<'admin.report'> { + report: Report; +} + +export type NotificationGroup = + | NotificationGroupFavourite + | NotificationGroupReblog + | NotificationGroupStatus + | NotificationGroupMention + | NotificationGroupPoll + | NotificationGroupUpdate + | NotificationGroupFollow + | NotificationGroupFollowRequest + | NotificationGroupModerationWarning + | NotificationGroupSeveredRelationships + | NotificationGroupAdminSignUp + | NotificationGroupAdminReport; + +function createReportFromJSON(reportJSON: ApiReportJSON): Report { + const { target_account, ...report } = reportJSON; + return { + targetAccountId: target_account.id, + ...report, + }; +} + +function createAccountWarningFromJSON( + warningJSON: ApiAccountWarningJSON, +): AccountWarning { + const { target_account, ...warning } = warningJSON; + return { + targetAccountId: target_account.id, + ...warning, + }; +} + +function createAccountRelationshipSeveranceEventFromJSON( + eventJson: ApiAccountRelationshipSeveranceEventJSON, +): AccountRelationshipSeveranceEvent { + return eventJson; +} + +export function createNotificationGroupFromJSON( + groupJson: ApiNotificationGroupJSON, +): NotificationGroup { + const { sample_accounts, ...group } = groupJson; + const sampleAccountIds = sample_accounts.map((account) => account.id); + + switch (group.type) { + case 'favourite': + case 'reblog': + case 'status': + case 'mention': + case 'poll': + case 'update': { + const { status, ...groupWithoutStatus } = group; + return { + statusId: status.id, + sampleAccountIds, + ...groupWithoutStatus, + }; + } + case 'admin.report': { + const { report, ...groupWithoutTargetAccount } = group; + return { + report: createReportFromJSON(report), + sampleAccountIds, + ...groupWithoutTargetAccount, + }; + } + case 'severed_relationships': + return { + ...group, + event: createAccountRelationshipSeveranceEventFromJSON(group.event), + sampleAccountIds, + }; + + case 'moderation_warning': { + const { moderation_warning, ...groupWithoutModerationWarning } = group; + return { + ...groupWithoutModerationWarning, + moderationWarning: createAccountWarningFromJSON(moderation_warning), + sampleAccountIds, + }; + } + default: + return { + sampleAccountIds, + ...group, + }; + } +} + +export function createNotificationGroupFromNotificationJSON( + notification: ApiNotificationJSON, +) { + const group = { + sampleAccountIds: [notification.account.id], + group_key: notification.group_key, + notifications_count: 1, + type: notification.type, + most_recent_notification_id: notification.id, + page_min_id: notification.id, + page_max_id: notification.id, + latest_page_notification_at: notification.created_at, + } as NotificationGroup; + + switch (notification.type) { + case 'favourite': + case 'reblog': + case 'status': + case 'mention': + case 'poll': + case 'update': + return { ...group, statusId: notification.status.id }; + case 'admin.report': + return { ...group, report: createReportFromJSON(notification.report) }; + case 'severed_relationships': + return { + ...group, + event: createAccountRelationshipSeveranceEventFromJSON( + notification.event, + ), + }; + case 'moderation_warning': + return { + ...group, + moderationWarning: createAccountWarningFromJSON( + notification.moderation_warning, + ), + }; + default: + return group; + } +} diff --git a/app/javascript/mastodon/reducers/index.ts b/app/javascript/mastodon/reducers/index.ts index 6296ef202690a0..b92de0dbcda766 100644 --- a/app/javascript/mastodon/reducers/index.ts +++ b/app/javascript/mastodon/reducers/index.ts @@ -24,6 +24,7 @@ import { markersReducer } from './markers'; import media_attachments from './media_attachments'; import meta from './meta'; import { modalReducer } from './modal'; +import { notificationGroupsReducer } from './notification_groups'; import { notificationPolicyReducer } from './notification_policy'; import { notificationRequestsReducer } from './notification_requests'; import notifications from './notifications'; @@ -65,6 +66,7 @@ const reducers = { search, media_attachments, notifications, + notificationGroups: notificationGroupsReducer, height_cache, custom_emojis, lists, diff --git a/app/javascript/mastodon/reducers/markers.ts b/app/javascript/mastodon/reducers/markers.ts index ec85d0f17328cb..b1f10b5fa23a92 100644 --- a/app/javascript/mastodon/reducers/markers.ts +++ b/app/javascript/mastodon/reducers/markers.ts @@ -1,6 +1,7 @@ import { createReducer } from '@reduxjs/toolkit'; -import { submitMarkersAction } from 'mastodon/actions/markers'; +import { submitMarkersAction, fetchMarkers } from 'mastodon/actions/markers'; +import { compareId } from 'mastodon/compare_id'; const initialState = { home: '0', @@ -15,4 +16,23 @@ export const markersReducer = createReducer(initialState, (builder) => { if (notifications) state.notifications = notifications; }, ); + builder.addCase( + fetchMarkers.fulfilled, + ( + state, + { + payload: { + markers: { home, notifications }, + }, + }, + ) => { + if (home && compareId(home.last_read_id, state.home) > 0) + state.home = home.last_read_id; + if ( + notifications && + compareId(notifications.last_read_id, state.notifications) > 0 + ) + state.notifications = notifications.last_read_id; + }, + ); }); diff --git a/app/javascript/mastodon/reducers/notification_groups.ts b/app/javascript/mastodon/reducers/notification_groups.ts new file mode 100644 index 00000000000000..e59f3e7ca11b57 --- /dev/null +++ b/app/javascript/mastodon/reducers/notification_groups.ts @@ -0,0 +1,508 @@ +import { createReducer, isAnyOf } from '@reduxjs/toolkit'; + +import { + authorizeFollowRequestSuccess, + blockAccountSuccess, + muteAccountSuccess, + rejectFollowRequestSuccess, +} from 'mastodon/actions/accounts_typed'; +import { focusApp, unfocusApp } from 'mastodon/actions/app'; +import { blockDomainSuccess } from 'mastodon/actions/domain_blocks_typed'; +import { fetchMarkers } from 'mastodon/actions/markers'; +import { + clearNotifications, + fetchNotifications, + fetchNotificationsGap, + processNewNotificationForGroups, + loadPending, + updateScrollPosition, + markNotificationsAsRead, + mountNotifications, + unmountNotifications, +} from 'mastodon/actions/notification_groups'; +import { + disconnectTimeline, + timelineDelete, +} from 'mastodon/actions/timelines_typed'; +import type { ApiNotificationJSON } from 'mastodon/api_types/notifications'; +import { compareId } from 'mastodon/compare_id'; +import { usePendingItems } from 'mastodon/initial_state'; +import { + NOTIFICATIONS_GROUP_MAX_AVATARS, + createNotificationGroupFromJSON, + createNotificationGroupFromNotificationJSON, +} from 'mastodon/models/notification_group'; +import type { NotificationGroup } from 'mastodon/models/notification_group'; + +const NOTIFICATIONS_TRIM_LIMIT = 50; + +export interface NotificationGap { + type: 'gap'; + maxId?: string; + sinceId?: string; +} + +interface NotificationGroupsState { + groups: (NotificationGroup | NotificationGap)[]; + pendingGroups: (NotificationGroup | NotificationGap)[]; + scrolledToTop: boolean; + isLoading: boolean; + lastReadId: string; + mounted: number; + isTabVisible: boolean; +} + +const initialState: NotificationGroupsState = { + groups: [], + pendingGroups: [], // holds pending groups in slow mode + scrolledToTop: false, + isLoading: false, + // The following properties are used to track unread notifications + lastReadId: '0', // used for unread notifications + mounted: 0, // number of mounted notification list components, usually 0 or 1 + isTabVisible: true, +}; + +function filterNotificationsForAccounts( + groups: NotificationGroupsState['groups'], + accountIds: string[], + onlyForType?: string, +) { + groups = groups + .map((group) => { + if ( + group.type !== 'gap' && + (!onlyForType || group.type === onlyForType) + ) { + const previousLength = group.sampleAccountIds.length; + + group.sampleAccountIds = group.sampleAccountIds.filter( + (id) => !accountIds.includes(id), + ); + + const newLength = group.sampleAccountIds.length; + const removed = previousLength - newLength; + + group.notifications_count -= removed; + } + + return group; + }) + .filter( + (group) => group.type === 'gap' || group.sampleAccountIds.length > 0, + ); + mergeGaps(groups); + return groups; +} + +function filterNotificationsForStatus( + groups: NotificationGroupsState['groups'], + statusId: string, +) { + groups = groups.filter( + (group) => + group.type === 'gap' || + !('statusId' in group) || + group.statusId !== statusId, + ); + mergeGaps(groups); + return groups; +} + +function removeNotificationsForAccounts( + state: NotificationGroupsState, + accountIds: string[], + onlyForType?: string, +) { + state.groups = filterNotificationsForAccounts( + state.groups, + accountIds, + onlyForType, + ); + state.pendingGroups = filterNotificationsForAccounts( + state.pendingGroups, + accountIds, + onlyForType, + ); +} + +function removeNotificationsForStatus( + state: NotificationGroupsState, + statusId: string, +) { + state.groups = filterNotificationsForStatus(state.groups, statusId); + state.pendingGroups = filterNotificationsForStatus( + state.pendingGroups, + statusId, + ); +} + +function isNotificationGroup( + groupOrGap: NotificationGroup | NotificationGap, +): groupOrGap is NotificationGroup { + return groupOrGap.type !== 'gap'; +} + +// Merge adjacent gaps in `groups` in-place +function mergeGaps(groups: NotificationGroupsState['groups']) { + for (let i = 0; i < groups.length; i++) { + const firstGroupOrGap = groups[i]; + + if (firstGroupOrGap?.type === 'gap') { + let lastGap = firstGroupOrGap; + let j = i + 1; + + for (; j < groups.length; j++) { + const groupOrGap = groups[j]; + if (groupOrGap?.type === 'gap') lastGap = groupOrGap; + else break; + } + + if (j - i > 1) { + groups.splice(i, j - i, { + type: 'gap', + maxId: firstGroupOrGap.maxId, + sinceId: lastGap.sinceId, + }); + } + } + } +} + +// Checks if `groups[index-1]` and `groups[index]` are gaps, and merge them in-place if they are +function mergeGapsAround( + groups: NotificationGroupsState['groups'], + index: number, +) { + if (index > 0) { + const potentialFirstGap = groups[index - 1]; + const potentialSecondGap = groups[index]; + + if ( + potentialFirstGap?.type === 'gap' && + potentialSecondGap?.type === 'gap' + ) { + groups.splice(index - 1, 2, { + type: 'gap', + maxId: potentialFirstGap.maxId, + sinceId: potentialSecondGap.sinceId, + }); + } + } +} + +function processNewNotification( + groups: NotificationGroupsState['groups'], + notification: ApiNotificationJSON, +) { + const existingGroupIndex = groups.findIndex( + (group) => + group.type !== 'gap' && group.group_key === notification.group_key, + ); + + // In any case, we are going to add a group at the top + // If there is currently a gap at the top, now is the time to update it + if (groups.length > 0 && groups[0]?.type === 'gap') { + groups[0].maxId = notification.id; + } + + if (existingGroupIndex > -1) { + const existingGroup = groups[existingGroupIndex]; + + if ( + existingGroup && + existingGroup.type !== 'gap' && + !existingGroup.sampleAccountIds.includes(notification.account.id) // This can happen for example if you like, then unlike, then like again the same post + ) { + // Update the existing group + if ( + existingGroup.sampleAccountIds.unshift(notification.account.id) > + NOTIFICATIONS_GROUP_MAX_AVATARS + ) + existingGroup.sampleAccountIds.pop(); + + existingGroup.most_recent_notification_id = notification.id; + existingGroup.page_max_id = notification.id; + existingGroup.latest_page_notification_at = notification.created_at; + existingGroup.notifications_count += 1; + + groups.splice(existingGroupIndex, 1); + mergeGapsAround(groups, existingGroupIndex); + + groups.unshift(existingGroup); + } + } else { + // Create a new group + groups.unshift(createNotificationGroupFromNotificationJSON(notification)); + } +} + +function trimNotifications(state: NotificationGroupsState) { + if (state.scrolledToTop) { + state.groups.splice(NOTIFICATIONS_TRIM_LIMIT); + } +} + +function shouldMarkNewNotificationsAsRead( + { + isTabVisible, + scrolledToTop, + mounted, + lastReadId, + groups, + }: NotificationGroupsState, + ignoreScroll = false, +) { + const isMounted = mounted > 0; + const oldestGroup = groups.findLast(isNotificationGroup); + const hasMore = groups.at(-1)?.type === 'gap'; + const oldestGroupReached = + !hasMore || + lastReadId === '0' || + (oldestGroup?.page_min_id && + compareId(oldestGroup.page_min_id, lastReadId) <= 0); + + return ( + isTabVisible && + (ignoreScroll || scrolledToTop) && + isMounted && + oldestGroupReached + ); +} + +function updateLastReadId( + state: NotificationGroupsState, + group: NotificationGroup | undefined = undefined, +) { + if (shouldMarkNewNotificationsAsRead(state)) { + group = group ?? state.groups.find(isNotificationGroup); + if ( + group?.page_max_id && + compareId(state.lastReadId, group.page_max_id) < 0 + ) + state.lastReadId = group.page_max_id; + } +} + +export const notificationGroupsReducer = createReducer( + initialState, + (builder) => { + builder + .addCase(fetchNotifications.fulfilled, (state, action) => { + state.groups = action.payload.map((json) => + json.type === 'gap' ? json : createNotificationGroupFromJSON(json), + ); + state.isLoading = false; + updateLastReadId(state); + }) + .addCase(fetchNotificationsGap.fulfilled, (state, action) => { + const { notifications } = action.payload; + + // find the gap in the existing notifications + const gapIndex = state.groups.findIndex( + (groupOrGap) => + groupOrGap.type === 'gap' && + groupOrGap.sinceId === action.meta.arg.gap.sinceId && + groupOrGap.maxId === action.meta.arg.gap.maxId, + ); + + if (gapIndex < 0) + // We do not know where to insert, let's return + return; + + // Filling a disconnection gap means we're getting historical data + // about groups we may know or may not know about. + + // The notifications timeline is split in two by the gap, with + // group information newer than the gap, and group information older + // than the gap. + + // Filling a gap should not touch anything before the gap, so any + // information on groups already appearing before the gap should be + // discarded, while any information on groups appearing after the gap + // can be updated and re-ordered. + + const oldestPageNotification = notifications.at(-1)?.page_min_id; + + // replace the gap with the notifications + a new gap + + const newerGroupKeys = state.groups + .slice(0, gapIndex) + .filter(isNotificationGroup) + .map((group) => group.group_key); + + const toInsert: NotificationGroupsState['groups'] = notifications + .map((json) => createNotificationGroupFromJSON(json)) + .filter( + (notification) => !newerGroupKeys.includes(notification.group_key), + ); + + const apiGroupKeys = (toInsert as NotificationGroup[]).map( + (group) => group.group_key, + ); + + const sinceId = action.meta.arg.gap.sinceId; + if ( + notifications.length > 0 && + !( + oldestPageNotification && + sinceId && + compareId(oldestPageNotification, sinceId) <= 0 + ) + ) { + // If we get an empty page, it means we reached the bottom, so we do not need to insert a new gap + // Similarly, if we've fetched more than the gap's, this means we have completely filled it + toInsert.push({ + type: 'gap', + maxId: notifications.at(-1)?.page_max_id, + sinceId, + } as NotificationGap); + } + + // Remove older groups covered by the API + state.groups = state.groups.filter( + (groupOrGap) => + groupOrGap.type !== 'gap' && + !apiGroupKeys.includes(groupOrGap.group_key), + ); + + // Replace the gap with API results (+ the new gap if needed) + state.groups.splice(gapIndex, 1, ...toInsert); + + // Finally, merge any adjacent gaps that could have been created by filtering + // groups earlier + mergeGaps(state.groups); + + state.isLoading = false; + + updateLastReadId(state); + }) + .addCase(processNewNotificationForGroups.fulfilled, (state, action) => { + const notification = action.payload; + processNewNotification( + usePendingItems ? state.pendingGroups : state.groups, + notification, + ); + updateLastReadId(state); + trimNotifications(state); + }) + .addCase(disconnectTimeline, (state, action) => { + if (action.payload.timeline === 'home') { + if (state.groups.length > 0 && state.groups[0]?.type !== 'gap') { + state.groups.unshift({ + type: 'gap', + sinceId: state.groups[0]?.page_min_id, + }); + } + } + }) + .addCase(timelineDelete, (state, action) => { + removeNotificationsForStatus(state, action.payload.statusId); + }) + .addCase(clearNotifications.pending, (state) => { + state.groups = []; + state.pendingGroups = []; + }) + .addCase(blockAccountSuccess, (state, action) => { + removeNotificationsForAccounts(state, [action.payload.relationship.id]); + }) + .addCase(muteAccountSuccess, (state, action) => { + if (action.payload.relationship.muting_notifications) + removeNotificationsForAccounts(state, [ + action.payload.relationship.id, + ]); + }) + .addCase(blockDomainSuccess, (state, action) => { + removeNotificationsForAccounts( + state, + action.payload.accounts.map((account) => account.id), + ); + }) + .addCase(loadPending, (state) => { + // First, remove any existing group and merge data + state.pendingGroups.forEach((group) => { + if (group.type !== 'gap') { + const existingGroupIndex = state.groups.findIndex( + (groupOrGap) => + isNotificationGroup(groupOrGap) && + groupOrGap.group_key === group.group_key, + ); + if (existingGroupIndex > -1) { + const existingGroup = state.groups[existingGroupIndex]; + if (existingGroup && existingGroup.type !== 'gap') { + group.notifications_count += existingGroup.notifications_count; + group.sampleAccountIds = group.sampleAccountIds + .concat(existingGroup.sampleAccountIds) + .slice(0, NOTIFICATIONS_GROUP_MAX_AVATARS); + state.groups.splice(existingGroupIndex, 1); + } + } + } + trimNotifications(state); + }); + + // Then build the consolidated list and clear pending groups + state.groups = state.pendingGroups.concat(state.groups); + state.pendingGroups = []; + }) + .addCase(updateScrollPosition, (state, action) => { + state.scrolledToTop = action.payload.top; + updateLastReadId(state); + trimNotifications(state); + }) + .addCase(markNotificationsAsRead, (state) => { + const mostRecentGroup = state.groups.find(isNotificationGroup); + if ( + mostRecentGroup?.page_max_id && + compareId(state.lastReadId, mostRecentGroup.page_max_id) < 0 + ) + state.lastReadId = mostRecentGroup.page_max_id; + }) + .addCase(fetchMarkers.fulfilled, (state, action) => { + if ( + action.payload.markers.notifications && + compareId( + state.lastReadId, + action.payload.markers.notifications.last_read_id, + ) < 0 + ) + state.lastReadId = action.payload.markers.notifications.last_read_id; + }) + .addCase(mountNotifications, (state) => { + state.mounted += 1; + updateLastReadId(state); + }) + .addCase(unmountNotifications, (state) => { + state.mounted -= 1; + }) + .addCase(focusApp, (state) => { + state.isTabVisible = true; + updateLastReadId(state); + }) + .addCase(unfocusApp, (state) => { + state.isTabVisible = false; + }) + .addMatcher( + isAnyOf(authorizeFollowRequestSuccess, rejectFollowRequestSuccess), + (state, action) => { + removeNotificationsForAccounts( + state, + [action.payload.id], + 'follow_request', + ); + }, + ) + .addMatcher( + isAnyOf(fetchNotifications.pending, fetchNotificationsGap.pending), + (state) => { + state.isLoading = true; + }, + ) + .addMatcher( + isAnyOf(fetchNotifications.rejected, fetchNotificationsGap.rejected), + (state) => { + state.isLoading = false; + }, + ); + }, +); diff --git a/app/javascript/mastodon/reducers/notifications.js b/app/javascript/mastodon/reducers/notifications.js index 79aa5651ff1da6..622f5e8e884bbd 100644 --- a/app/javascript/mastodon/reducers/notifications.js +++ b/app/javascript/mastodon/reducers/notifications.js @@ -16,13 +16,13 @@ import { import { fetchMarkers, } from '../actions/markers'; +import { clearNotifications } from '../actions/notification_groups'; import { notificationsUpdate, NOTIFICATIONS_EXPAND_SUCCESS, NOTIFICATIONS_EXPAND_REQUEST, NOTIFICATIONS_EXPAND_FAIL, NOTIFICATIONS_FILTER_SET, - NOTIFICATIONS_CLEAR, NOTIFICATIONS_SCROLL_TOP, NOTIFICATIONS_LOAD_PENDING, NOTIFICATIONS_MOUNT, @@ -290,7 +290,7 @@ export default function notifications(state = initialState, action) { case authorizeFollowRequestSuccess.type: case rejectFollowRequestSuccess.type: return filterNotifications(state, [action.payload.id], 'follow_request'); - case NOTIFICATIONS_CLEAR: + case clearNotifications.pending.type: return state.set('items', ImmutableList()).set('pendingItems', ImmutableList()).set('hasMore', false); case timelineDelete.type: return deleteByStatus(state, action.payload.statusId); diff --git a/app/javascript/mastodon/selectors/notifications.ts b/app/javascript/mastodon/selectors/notifications.ts new file mode 100644 index 00000000000000..1b1ed2154cdaa7 --- /dev/null +++ b/app/javascript/mastodon/selectors/notifications.ts @@ -0,0 +1,34 @@ +import { createSelector } from '@reduxjs/toolkit'; + +import { compareId } from 'mastodon/compare_id'; +import type { RootState } from 'mastodon/store'; + +export const selectUnreadNotificationGroupsCount = createSelector( + [ + (s: RootState) => s.notificationGroups.lastReadId, + (s: RootState) => s.notificationGroups.pendingGroups, + (s: RootState) => s.notificationGroups.groups, + ], + (notificationMarker, pendingGroups, groups) => { + return ( + groups.filter( + (group) => + group.type !== 'gap' && + group.page_max_id && + compareId(group.page_max_id, notificationMarker) > 0, + ).length + + pendingGroups.filter( + (group) => + group.type !== 'gap' && + group.page_max_id && + compareId(group.page_max_id, notificationMarker) > 0, + ).length + ); + }, +); + +export const selectPendingNotificationGroupsCount = createSelector( + [(s: RootState) => s.notificationGroups.pendingGroups], + (pendingGroups) => + pendingGroups.filter((group) => group.type !== 'gap').length, +); diff --git a/app/javascript/mastodon/selectors/settings.ts b/app/javascript/mastodon/selectors/settings.ts new file mode 100644 index 00000000000000..64d9440bc8d3b0 --- /dev/null +++ b/app/javascript/mastodon/selectors/settings.ts @@ -0,0 +1,40 @@ +import type { RootState } from 'mastodon/store'; + +/* eslint-disable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */ +// state.settings is not yet typed, so we disable some ESLint checks for those selectors +export const selectSettingsNotificationsShows = (state: RootState) => + state.settings.getIn(['notifications', 'shows']).toJS() as Record< + string, + boolean + >; + +export const selectSettingsNotificationsExcludedTypes = (state: RootState) => + Object.entries(selectSettingsNotificationsShows(state)) + .filter(([_type, enabled]) => !enabled) + .map(([type, _enabled]) => type); + +export const selectSettingsNotificationsQuickFilterShow = (state: RootState) => + state.settings.getIn(['notifications', 'quickFilter', 'show']) as boolean; + +export const selectSettingsNotificationsQuickFilterActive = ( + state: RootState, +) => state.settings.getIn(['notifications', 'quickFilter', 'active']) as string; + +export const selectSettingsNotificationsQuickFilterAdvanced = ( + state: RootState, +) => + state.settings.getIn(['notifications', 'quickFilter', 'advanced']) as boolean; + +export const selectSettingsNotificationsShowUnread = (state: RootState) => + state.settings.getIn(['notifications', 'showUnread']) as boolean; + +export const selectNeedsNotificationPermission = (state: RootState) => + (state.settings.getIn(['notifications', 'alerts']).includes(true) && + state.notifications.get('browserSupport') && + state.notifications.get('browserPermission') === 'default' && + !state.settings.getIn([ + 'notifications', + 'dismissPermissionBanner', + ])) as boolean; + +/* eslint-enable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */ diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index e94ce2d8f49763..5a0a41c978e6b8 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1611,14 +1611,19 @@ body > [data-popper-placement] { } } -.status__wrapper-direct { +.status__wrapper-direct, +.notification-ungrouped--direct { background: rgba($ui-highlight-color, 0.05); &:focus { - background: rgba($ui-highlight-color, 0.05); + background: rgba($ui-highlight-color, 0.1); } +} - .status__prepend { +.status__wrapper-direct, +.notification-ungrouped--direct { + .status__prepend, + .notification-ungrouped__header { color: $highlight-text-color; } } @@ -2209,41 +2214,28 @@ a.account__display-name { } } -.notification__relationships-severance-event, -.notification__moderation-warning { - display: flex; - gap: 16px; +.notification-group--link { color: $secondary-text-color; text-decoration: none; - align-items: flex-start; - padding: 16px 32px; - border-bottom: 1px solid var(--background-border-color); - - &:hover { - color: $primary-text-color; - } - - .icon { - padding: 2px; - color: $highlight-text-color; - } - &__content { + .notification-group__main { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; flex-grow: 1; - font-size: 16px; - line-height: 24px; + font-size: 15px; + line-height: 22px; - strong { + strong, + bdi { font-weight: 700; } .link-button { font-size: inherit; line-height: inherit; + font-weight: inherit; } } } @@ -10193,8 +10185,8 @@ noscript { display: flex; align-items: center; border-bottom: 1px solid var(--background-border-color); - padding: 24px 32px; - gap: 16px; + padding: 16px 24px; + gap: 8px; color: $darker-text-color; text-decoration: none; @@ -10204,10 +10196,8 @@ noscript { color: $secondary-text-color; } - .icon { - width: 24px; - height: 24px; - padding: 2px; + .notification-group__icon { + color: inherit; } &__text { @@ -10345,6 +10335,251 @@ noscript { } } +.notification-group { + display: flex; + align-items: flex-start; + gap: 8px; + padding: 16px 24px; + border-bottom: 1px solid var(--background-border-color); + + &__icon { + width: 40px; + display: flex; + align-items: center; + justify-content: center; + flex: 0 0 auto; + color: $dark-text-color; + + .icon { + width: 28px; + height: 28px; + } + } + + &--follow &__icon, + &--follow-request &__icon { + color: $highlight-text-color; + } + + &--favourite &__icon { + color: $gold-star; + } + + &--reblog &__icon { + color: $valid-value-color; + } + + &--relationships-severance-event &__icon, + &--admin-report &__icon, + &--admin-sign-up &__icon { + color: $dark-text-color; + } + + &--moderation-warning &__icon { + color: $red-bookmark; + } + + &--follow-request &__actions { + align-items: center; + display: flex; + gap: 8px; + + .icon-button { + border: 1px solid var(--background-border-color); + border-radius: 50%; + padding: 1px; + } + } + + &__main { + display: flex; + flex-direction: column; + gap: 8px; + flex: 1 1 auto; + overflow: hidden; + + &__header { + display: flex; + flex-direction: column; + gap: 8px; + + &__wrapper { + display: flex; + justify-content: space-between; + } + + &__label { + display: flex; + gap: 8px; + font-size: 15px; + line-height: 22px; + color: $darker-text-color; + + a { + color: inherit; + text-decoration: none; + } + + bdi { + font-weight: 700; + color: $primary-text-color; + } + + time { + color: $dark-text-color; + } + } + } + + &__status { + border: 1px solid var(--background-border-color); + border-radius: 8px; + padding: 8px; + } + } + + &__avatar-group { + display: flex; + gap: 8px; + height: 28px; + overflow-y: hidden; + flex-wrap: wrap; + } + + .status { + padding: 0; + border: 0; + } + + &__embedded-status { + &__account { + display: flex; + align-items: center; + gap: 4px; + margin-bottom: 8px; + color: $dark-text-color; + + bdi { + color: inherit; + } + } + + .account__avatar { + opacity: 0.5; + } + + &__content { + display: -webkit-box; + font-size: 15px; + line-height: 22px; + color: $dark-text-color; + cursor: pointer; + -webkit-line-clamp: 4; + -webkit-box-orient: vertical; + max-height: 4 * 22px; + overflow: hidden; + + p, + a { + color: inherit; + } + } + } +} + +.notification-ungrouped { + padding: 16px 24px; + border-bottom: 1px solid var(--background-border-color); + + &__header { + display: flex; + align-items: center; + gap: 8px; + color: $dark-text-color; + font-size: 15px; + line-height: 22px; + font-weight: 500; + padding-inline-start: 24px; + margin-bottom: 16px; + + &__icon { + display: flex; + align-items: center; + justify-content: center; + flex: 0 0 auto; + + .icon { + width: 16px; + height: 16px; + } + } + + a { + color: inherit; + text-decoration: none; + } + } + + .status { + border: 0; + padding: 0; + + &__avatar { + width: 40px; + height: 40px; + } + } + + .status__wrapper-direct { + background: transparent; + } + + $icon-margin: 48px; // 40px avatar + 8px gap + + .status__content, + .status__action-bar, + .media-gallery, + .video-player, + .audio-player, + .attachment-list, + .picture-in-picture-placeholder, + .more-from-author, + .status-card, + .hashtag-bar { + margin-inline-start: $icon-margin; + width: calc(100% - $icon-margin); + } + + .more-from-author { + width: calc(100% - $icon-margin + 2px); + } + + .status__content__read-more-button { + margin-inline-start: $icon-margin; + } + + .notification__report { + border: 0; + padding: 0; + } +} + +.notification-group--unread, +.notification-ungrouped--unread { + position: relative; + + &::before { + content: ''; + position: absolute; + top: 0; + inset-inline-start: 0; + width: 100%; + height: 100%; + border-inline-start: 4px solid $highlight-text-color; + pointer-events: none; + } +} + .hover-card-controller[data-popper-reference-hidden='true'] { opacity: 0; pointer-events: none; diff --git a/app/models/notification.rb b/app/models/notification.rb index 01abe74f5e7eb3..6d404114788c00 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -30,6 +30,7 @@ class Notification < ApplicationRecord 'Poll' => :poll, }.freeze + # Please update app/javascript/api_types/notification.ts if you change this PROPERTIES = { mention: { filterable: true, diff --git a/app/models/notification_group.rb b/app/models/notification_group.rb index b1cbd7c19abf9f..223945f07bf681 100644 --- a/app/models/notification_group.rb +++ b/app/models/notification_group.rb @@ -3,13 +3,17 @@ class NotificationGroup < ActiveModelSerializers::Model attributes :group_key, :sample_accounts, :notifications_count, :notification, :most_recent_notification_id + # Try to keep this consistent with `app/javascript/mastodon/models/notification_group.ts` + SAMPLE_ACCOUNTS_SIZE = 8 + def self.from_notification(notification, max_id: nil) if notification.group_key.present? - # TODO: caching and preloading + # TODO: caching, and, if caching, preloading scope = notification.account.notifications.where(group_key: notification.group_key) scope = scope.where(id: ..max_id) if max_id.present? - most_recent_notifications = scope.order(id: :desc).take(3) + # Ideally, we would not load accounts for each notification group + most_recent_notifications = scope.order(id: :desc).includes(:from_account).take(SAMPLE_ACCOUNTS_SIZE) most_recent_id = most_recent_notifications.first.id sample_accounts = most_recent_notifications.map(&:from_account) notifications_count = scope.count diff --git a/app/serializers/rest/notification_group_serializer.rb b/app/serializers/rest/notification_group_serializer.rb index 9aa5663f4e040c..749f71775463e0 100644 --- a/app/serializers/rest/notification_group_serializer.rb +++ b/app/serializers/rest/notification_group_serializer.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class REST::NotificationGroupSerializer < ActiveModel::Serializer + # Please update app/javascript/api_types/notification.ts when making changes to the attributes attributes :group_key, :notifications_count, :type, :most_recent_notification_id attribute :page_min_id, if: :paginated? diff --git a/app/serializers/rest/notification_serializer.rb b/app/serializers/rest/notification_serializer.rb index ee17af80768439..320bc86961d922 100644 --- a/app/serializers/rest/notification_serializer.rb +++ b/app/serializers/rest/notification_serializer.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class REST::NotificationSerializer < ActiveModel::Serializer + # Please update app/javascript/api_types/notification.ts when making changes to the attributes attributes :id, :type, :created_at, :group_key attribute :filtered, if: :filtered? diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb index d69b5af141214f..acbb3fc784fe49 100644 --- a/app/services/notify_service.rb +++ b/app/services/notify_service.rb @@ -4,7 +4,6 @@ class NotifyService < BaseService include Redisable MAXIMUM_GROUP_SPAN_HOURS = 12 - MAXIMUM_GROUP_GAP_TIME = 4.hours.to_i NON_EMAIL_TYPES = %i( admin.report @@ -217,9 +216,8 @@ def notification_group_key previous_bucket = redis.get(redis_key).to_i hour_bucket = previous_bucket if hour_bucket < previous_bucket + MAXIMUM_GROUP_SPAN_HOURS - # Do not track groups past a given inactivity time # We do not concern ourselves with race conditions since we use hour buckets - redis.set(redis_key, hour_bucket, ex: MAXIMUM_GROUP_GAP_TIME) + redis.set(redis_key, hour_bucket, ex: MAXIMUM_GROUP_SPAN_HOURS) "#{type_prefix}-#{hour_bucket}" end diff --git a/config/routes.rb b/config/routes.rb index e4f091043d6c2a..93bdb959695aa1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -29,6 +29,7 @@ def redirect_with_vary(path) /lists/(*any) /links/(*any) /notifications/(*any) + /notifications_v2/(*any) /favourites /bookmarks /pinned diff --git a/package.json b/package.json index 404c4f486fcc05..4571ca03af1540 100644 --- a/package.json +++ b/package.json @@ -123,6 +123,7 @@ "tesseract.js": "^2.1.5", "tiny-queue": "^0.2.1", "twitter-text": "3.1.0", + "use-debounce": "^10.0.0", "webpack": "^4.47.0", "webpack-assets-manifest": "^4.0.6", "webpack-bundle-analyzer": "^4.8.0", diff --git a/yarn.lock b/yarn.lock index c5d04800619ed5..86640faa0596ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2910,6 +2910,7 @@ __metadata: tiny-queue: "npm:^0.2.1" twitter-text: "npm:3.1.0" typescript: "npm:^5.0.4" + use-debounce: "npm:^10.0.0" webpack: "npm:^4.47.0" webpack-assets-manifest: "npm:^4.0.6" webpack-bundle-analyzer: "npm:^4.8.0" @@ -17543,6 +17544,15 @@ __metadata: languageName: node linkType: hard +"use-debounce@npm:^10.0.0": + version: 10.0.0 + resolution: "use-debounce@npm:10.0.0" + peerDependencies: + react: ">=16.8.0" + checksum: 10c0/c1166cba52dedeab17e3e29275af89c57a3e8981b75f6e38ae2896ac36ecd4ed7d8fff5f882ba4b2f91eac7510d5ae0dd89fa4f7d081622ed436c3c89eda5cd1 + languageName: node + linkType: hard + "use-isomorphic-layout-effect@npm:^1.1.1, use-isomorphic-layout-effect@npm:^1.1.2": version: 1.1.2 resolution: "use-isomorphic-layout-effect@npm:1.1.2" From 6e47637dd4f93169ec783c01f4f9b4dfb7cb66ce Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 18 Jul 2024 17:23:40 +0200 Subject: [PATCH 096/101] Fix grouping across hourly buckets happening in a 12 seconds window instead of 12 hours window (#31062) --- app/services/notify_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb index acbb3fc784fe49..23f92c816b86c0 100644 --- a/app/services/notify_service.rb +++ b/app/services/notify_service.rb @@ -217,7 +217,7 @@ def notification_group_key hour_bucket = previous_bucket if hour_bucket < previous_bucket + MAXIMUM_GROUP_SPAN_HOURS # We do not concern ourselves with race conditions since we use hour buckets - redis.set(redis_key, hour_bucket, ex: MAXIMUM_GROUP_SPAN_HOURS) + redis.set(redis_key, hour_bucket, ex: MAXIMUM_GROUP_SPAN_HOURS.hours.to_i) "#{type_prefix}-#{hour_bucket}" end From 41b7281b56ea64b13fdf07d5ce7efadcbe65bd1c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 17:23:43 +0200 Subject: [PATCH 097/101] fix(deps): update dependency use-debounce to v10.0.1 (#31060) 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 86640faa0596ca..1a17c5862fa249 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17545,11 +17545,11 @@ __metadata: linkType: hard "use-debounce@npm:^10.0.0": - version: 10.0.0 - resolution: "use-debounce@npm:10.0.0" + version: 10.0.1 + resolution: "use-debounce@npm:10.0.1" peerDependencies: react: ">=16.8.0" - checksum: 10c0/c1166cba52dedeab17e3e29275af89c57a3e8981b75f6e38ae2896ac36ecd4ed7d8fff5f882ba4b2f91eac7510d5ae0dd89fa4f7d081622ed436c3c89eda5cd1 + checksum: 10c0/377a11814a708f5c392f465cbbe2d119a8a2635c8226cc5e30eba397c4436f8e8234385d069467b369d105ed0d3be733c6a08d8ae1004017c6d6f58f4d4c24d8 languageName: node linkType: hard From 848b59c8ae366895eb690a062698bd8f653e5959 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 18 Jul 2024 11:23:46 -0400 Subject: [PATCH 098/101] Reduce factory creation in `MediaAttachment` model spec (#31058) --- spec/models/media_attachment_spec.rb | 99 +++++++++++++++++----------- spec/rails_helper.rb | 1 + 2 files changed, 62 insertions(+), 38 deletions(-) diff --git a/spec/models/media_attachment_spec.rb b/spec/models/media_attachment_spec.rb index 24e8ca39c11f0e..3142b291fb2b00 100644 --- a/spec/models/media_attachment_spec.rb +++ b/spec/models/media_attachment_spec.rb @@ -90,7 +90,7 @@ media.destroy end - it 'saves media attachment with correct file metadata' do + it 'saves media attachment with correct file and size metadata' do expect(media) .to be_persisted .and be_processing_complete @@ -103,14 +103,12 @@ # Rack::Mime (used by PublicFileServerMiddleware) recognizes file extension expect(Rack::Mime.mime_type(extension, nil)).to eq content_type - end - it 'saves media attachment with correct size metadata' do - # strips original file name + # Strip original file name expect(media.file_file_name) .to_not start_with '600x400' - # sets meta for original and thumbnail + # Set meta for original and thumbnail expect(media.file.meta.deep_symbolize_keys) .to include( original: include( @@ -174,10 +172,18 @@ let(:media) { Fabricate(:media_attachment, file: attachment_fixture('avatar.gif')) } it 'sets correct file metadata' do - expect(media.type).to eq 'gifv' - expect(media.file_content_type).to eq 'video/mp4' - expect(media.file.meta['original']['width']).to eq 128 - expect(media.file.meta['original']['height']).to eq 128 + expect(media) + .to have_attributes( + type: eq('gifv'), + file_content_type: eq('video/mp4') + ) + expect(media_metadata) + .to include( + original: include( + width: eq(128), + height: eq(128) + ) + ) end end @@ -192,11 +198,19 @@ let(:media) { Fabricate(:media_attachment, file: attachment_fixture(fixture[:filename])) } it 'sets correct file metadata' do - expect(media.type).to eq 'image' - expect(media.file_content_type).to eq 'image/gif' - expect(media.file.meta['original']['width']).to eq fixture[:width] - expect(media.file.meta['original']['height']).to eq fixture[:height] - expect(media.file.meta['original']['aspect']).to eq fixture[:aspect] + expect(media) + .to have_attributes( + type: eq('image'), + file_content_type: eq('image/gif') + ) + expect(media_metadata) + .to include( + original: include( + width: eq(fixture[:width]), + height: eq(fixture[:height]), + aspect: eq(fixture[:aspect]) + ) + ) end end end @@ -204,39 +218,42 @@ describe 'ogg with cover art' do let(:media) { Fabricate(:media_attachment, file: attachment_fixture('boop.ogg')) } + let(:expected_media_duration) { 0.235102 } - it 'sets correct file metadata' do - expect(media.type).to eq 'audio' - expect(media.file.meta['original']['duration']).to be_within(0.05).of(0.235102) - expect(media.thumbnail.present?).to be true + # The libvips and ImageMagick implementations produce different results + let(:expected_background_color) { Rails.configuration.x.use_vips ? '#268cd9' : '#3088d4' } - expect(media.file.meta['colors']['background']).to eq(expected_background_color) - expect(media.file_file_name).to_not eq 'boop.ogg' - end + it 'sets correct file metadata' do + expect(media) + .to have_attributes( + type: eq('audio'), + thumbnail: be_present, + file_file_name: not_eq('boop.ogg') + ) - def expected_background_color - # The libvips and ImageMagick implementations produce different results - Rails.configuration.x.use_vips ? '#268cd9' : '#3088d4' + expect(media_metadata) + .to include( + original: include(duration: be_within(0.05).of(expected_media_duration)), + colors: include(background: eq(expected_background_color)) + ) end end describe 'mp3 with large cover art' do let(:media) { Fabricate(:media_attachment, file: attachment_fixture('boop.mp3')) } + let(:expected_media_duration) { 0.235102 } - it 'detects it as an audio file' do - expect(media.type).to eq 'audio' - end - - it 'sets meta for the duration' do - expect(media.file.meta['original']['duration']).to be_within(0.05).of(0.235102) - end - - it 'extracts thumbnail' do - expect(media.thumbnail.present?).to be true - end - - it 'gives the file a random name' do - expect(media.file_file_name).to_not eq 'boop.mp3' + it 'detects file type and sets correct metadata' do + expect(media) + .to have_attributes( + type: eq('audio'), + thumbnail: be_present, + file_file_name: not_eq('boop.mp3') + ) + expect(media_metadata) + .to include( + original: include(duration: be_within(0.05).of(expected_media_duration)) + ) end end @@ -274,4 +291,10 @@ def expected_background_color expect(media.valid?).to be true end end + + private + + def media_metadata + media.file.meta.deep_symbolize_keys + end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 79031f1a9430f7..d4b9bddf93f12a 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -161,6 +161,7 @@ def sign_in(resource, _deprecated = nil, scope: nil) end RSpec::Matchers.define_negated_matcher :not_change, :change +RSpec::Matchers.define_negated_matcher :not_eq, :eq RSpec::Matchers.define_negated_matcher :not_include, :include def request_fixture(name) From 82344342c1c5adb3f6a4b376559db737a9e982b7 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 18 Jul 2024 11:45:40 -0400 Subject: [PATCH 099/101] Add link to org-level contribution guidelines to contributing doc (#31043) --- CONTRIBUTING.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b68a9bde3eafff..8286fdd2f7d2cb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,6 +11,11 @@ You can contribute in the following ways: If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon). +Please review the org-level [contribution guidelines] for high-level acceptance +criteria guidance. + +[contribution guidelines]: https://github.com/mastodon/.github/blob/main/CONTRIBUTING.md + ## API Changes and Additions Please note that any changes or additions made to the API should have an accompanying pull request on [our documentation repository](https://github.com/mastodon/documentation). From 7224e2405413b816b2fbc171a9a9e7b86bae0261 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Thu, 18 Jul 2024 16:36:09 +0200 Subject: [PATCH 100/101] [Glitch] Grouped Notifications UI Port f587ff643f552a32a1c43e103a474a5065cd3657 to glitch-soc Co-authored-by: Eugen Rochko Co-authored-by: Claire Signed-off-by: Claire --- app/javascript/flavours/glitch/actions/app.ts | 3 + .../flavours/glitch/actions/markers.ts | 14 +- .../glitch/actions/notification_groups.ts | 144 +++++ .../flavours/glitch/actions/notifications.js | 13 +- .../actions/notifications_migration.tsx | 18 + .../glitch/actions/notifications_typed.ts | 9 +- .../flavours/glitch/actions/streaming.js | 11 +- .../flavours/glitch/api/notifications.ts | 18 + .../glitch/api_types/notifications.ts | 145 +++++ .../flavours/glitch/api_types/reports.ts | 16 + .../flavours/glitch/components/load_gap.tsx | 12 +- .../flavours/glitch/components/status.jsx | 16 +- .../glitch/components/status_header.jsx | 4 +- .../glitch/components/status_list.jsx | 2 +- .../glitch/components/status_prepend.jsx | 2 +- .../compose/components/edit_indicator.jsx | 10 +- .../compose/components/reply_indicator.jsx | 10 +- .../components/column_settings.jsx | 11 + .../filtered_notifications_banner.tsx | 4 +- .../components/moderation_warning.tsx | 51 +- .../relationships_severance_event.jsx | 15 +- .../containers/column_settings_container.js | 8 +- .../glitch/features/notifications/index.jsx | 2 +- .../components/avatar_group.tsx | 31 ++ .../components/embedded_status.tsx | 93 ++++ .../components/embedded_status_content.tsx | 165 ++++++ .../components/names_list.tsx | 51 ++ .../components/notification_admin_report.tsx | 132 +++++ .../components/notification_admin_sign_up.tsx | 31 ++ .../components/notification_favourite.tsx | 45 ++ .../components/notification_follow.tsx | 31 ++ .../notification_follow_request.tsx | 78 +++ .../components/notification_group.tsx | 134 +++++ .../notification_group_with_status.tsx | 91 ++++ .../components/notification_mention.tsx | 55 ++ .../notification_moderation_warning.tsx | 13 + .../components/notification_poll.tsx | 41 ++ .../components/notification_reblog.tsx | 45 ++ .../notification_severed_relationships.tsx | 15 + .../components/notification_status.tsx | 31 ++ .../components/notification_update.tsx | 31 ++ .../components/notification_with_status.tsx | 73 +++ .../features/notifications_v2/filter_bar.tsx | 145 +++++ .../features/notifications_v2/index.tsx | 354 ++++++++++++ .../glitch/features/notifications_wrapper.jsx | 13 + .../features/ui/components/columns_area.jsx | 4 +- .../ui/components/navigation_panel.jsx | 9 +- .../flavours/glitch/features/ui/index.jsx | 15 +- .../features/ui/util/async-components.js | 10 +- .../glitch/models/notification_group.ts | 207 +++++++ .../flavours/glitch/reducers/index.ts | 2 + .../flavours/glitch/reducers/markers.ts | 25 +- .../glitch/reducers/notification_groups.ts | 508 ++++++++++++++++++ .../flavours/glitch/reducers/notifications.js | 4 +- .../glitch/selectors/notifications.ts | 34 ++ .../flavours/glitch/selectors/settings.ts | 40 ++ .../flavours/glitch/styles/components.scss | 295 +++++++++- 57 files changed, 3271 insertions(+), 118 deletions(-) create mode 100644 app/javascript/flavours/glitch/actions/notification_groups.ts create mode 100644 app/javascript/flavours/glitch/actions/notifications_migration.tsx create mode 100644 app/javascript/flavours/glitch/api/notifications.ts create mode 100644 app/javascript/flavours/glitch/api_types/notifications.ts create mode 100644 app/javascript/flavours/glitch/api_types/reports.ts create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/avatar_group.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/embedded_status.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/embedded_status_content.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/names_list.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/notification_admin_report.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/notification_admin_sign_up.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/notification_favourite.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/notification_follow.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/notification_follow_request.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/notification_group.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/notification_group_with_status.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/notification_mention.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/notification_moderation_warning.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/notification_poll.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/notification_reblog.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/notification_severed_relationships.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/notification_status.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/notification_update.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/components/notification_with_status.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/filter_bar.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_v2/index.tsx create mode 100644 app/javascript/flavours/glitch/features/notifications_wrapper.jsx create mode 100644 app/javascript/flavours/glitch/models/notification_group.ts create mode 100644 app/javascript/flavours/glitch/reducers/notification_groups.ts create mode 100644 app/javascript/flavours/glitch/selectors/notifications.ts create mode 100644 app/javascript/flavours/glitch/selectors/settings.ts diff --git a/app/javascript/flavours/glitch/actions/app.ts b/app/javascript/flavours/glitch/actions/app.ts index 6fbfc07f68c931..be1a5cced20f4d 100644 --- a/app/javascript/flavours/glitch/actions/app.ts +++ b/app/javascript/flavours/glitch/actions/app.ts @@ -2,6 +2,9 @@ import { createAction } from '@reduxjs/toolkit'; import type { LayoutType } from '../is_mobile'; +export const focusApp = createAction('APP_FOCUS'); +export const unfocusApp = createAction('APP_UNFOCUS'); + interface ChangeLayoutPayload { layout: LayoutType; } diff --git a/app/javascript/flavours/glitch/actions/markers.ts b/app/javascript/flavours/glitch/actions/markers.ts index a85af1c4beec48..861eae41ec4f1e 100644 --- a/app/javascript/flavours/glitch/actions/markers.ts +++ b/app/javascript/flavours/glitch/actions/markers.ts @@ -75,9 +75,17 @@ interface MarkerParam { } function getLastNotificationId(state: RootState): string | undefined { - // @ts-expect-error state.notifications is not yet typed - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call - return state.getIn(['notifications', 'lastReadId']); + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access + const enableBeta = state.settings.getIn( + ['notifications', 'groupingBeta'], + false, + ) as boolean; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return enableBeta + ? state.notificationGroups.lastReadId + : // @ts-expect-error state.notifications is not yet typed + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + state.getIn(['notifications', 'lastReadId']); } const buildPostMarkersParams = (state: RootState) => { diff --git a/app/javascript/flavours/glitch/actions/notification_groups.ts b/app/javascript/flavours/glitch/actions/notification_groups.ts new file mode 100644 index 00000000000000..3f6d14a978b154 --- /dev/null +++ b/app/javascript/flavours/glitch/actions/notification_groups.ts @@ -0,0 +1,144 @@ +import { createAction } from '@reduxjs/toolkit'; + +import { + apiClearNotifications, + apiFetchNotifications, +} from 'flavours/glitch/api/notifications'; +import type { ApiAccountJSON } from 'flavours/glitch/api_types/accounts'; +import type { + ApiNotificationGroupJSON, + ApiNotificationJSON, +} from 'flavours/glitch/api_types/notifications'; +import { allNotificationTypes } from 'flavours/glitch/api_types/notifications'; +import type { ApiStatusJSON } from 'flavours/glitch/api_types/statuses'; +import type { NotificationGap } from 'flavours/glitch/reducers/notification_groups'; +import { + selectSettingsNotificationsExcludedTypes, + selectSettingsNotificationsQuickFilterActive, +} from 'flavours/glitch/selectors/settings'; +import type { AppDispatch } from 'flavours/glitch/store'; +import { + createAppAsyncThunk, + createDataLoadingThunk, +} from 'flavours/glitch/store/typed_functions'; + +import { importFetchedAccounts, importFetchedStatuses } from './importer'; +import { NOTIFICATIONS_FILTER_SET } from './notifications'; +import { saveSettings } from './settings'; + +function excludeAllTypesExcept(filter: string) { + return allNotificationTypes.filter((item) => item !== filter); +} + +function dispatchAssociatedRecords( + dispatch: AppDispatch, + notifications: ApiNotificationGroupJSON[] | ApiNotificationJSON[], +) { + const fetchedAccounts: ApiAccountJSON[] = []; + const fetchedStatuses: ApiStatusJSON[] = []; + + notifications.forEach((notification) => { + if ('sample_accounts' in notification) { + fetchedAccounts.push(...notification.sample_accounts); + } + + if (notification.type === 'admin.report') { + fetchedAccounts.push(notification.report.target_account); + } + + if (notification.type === 'moderation_warning') { + fetchedAccounts.push(notification.moderation_warning.target_account); + } + + if ('status' in notification) { + fetchedStatuses.push(notification.status); + } + }); + + if (fetchedAccounts.length > 0) + dispatch(importFetchedAccounts(fetchedAccounts)); + + if (fetchedStatuses.length > 0) + dispatch(importFetchedStatuses(fetchedStatuses)); +} + +export const fetchNotifications = createDataLoadingThunk( + 'notificationGroups/fetch', + async (_params, { getState }) => { + const activeFilter = + selectSettingsNotificationsQuickFilterActive(getState()); + + return apiFetchNotifications({ + exclude_types: + activeFilter === 'all' + ? selectSettingsNotificationsExcludedTypes(getState()) + : excludeAllTypesExcept(activeFilter), + }); + }, + ({ notifications }, { dispatch }) => { + dispatchAssociatedRecords(dispatch, notifications); + const payload: (ApiNotificationGroupJSON | NotificationGap)[] = + notifications; + + // TODO: might be worth not using gaps for that… + // if (nextLink) payload.push({ type: 'gap', loadUrl: nextLink.uri }); + if (notifications.length > 1) + payload.push({ type: 'gap', maxId: notifications.at(-1)?.page_min_id }); + + return payload; + // dispatch(submitMarkers()); + }, +); + +export const fetchNotificationsGap = createDataLoadingThunk( + 'notificationGroups/fetchGap', + async (params: { gap: NotificationGap }) => + apiFetchNotifications({ max_id: params.gap.maxId }), + + ({ notifications }, { dispatch }) => { + dispatchAssociatedRecords(dispatch, notifications); + + return { notifications }; + }, +); + +export const processNewNotificationForGroups = createAppAsyncThunk( + 'notificationGroups/processNew', + (notification: ApiNotificationJSON, { dispatch }) => { + dispatchAssociatedRecords(dispatch, [notification]); + + return notification; + }, +); + +export const loadPending = createAction('notificationGroups/loadPending'); + +export const updateScrollPosition = createAction<{ top: boolean }>( + 'notificationGroups/updateScrollPosition', +); + +export const setNotificationsFilter = createAppAsyncThunk( + 'notifications/filter/set', + ({ filterType }: { filterType: string }, { dispatch }) => { + dispatch({ + type: NOTIFICATIONS_FILTER_SET, + path: ['notifications', 'quickFilter', 'active'], + value: filterType, + }); + // dispatch(expandNotifications({ forceLoad: true })); + void dispatch(fetchNotifications()); + dispatch(saveSettings()); + }, +); + +export const clearNotifications = createDataLoadingThunk( + 'notifications/clear', + () => apiClearNotifications(), +); + +export const markNotificationsAsRead = createAction( + 'notificationGroups/markAsRead', +); + +export const mountNotifications = createAction('notificationGroups/mount'); +export const unmountNotifications = createAction('notificationGroups/unmount'); diff --git a/app/javascript/flavours/glitch/actions/notifications.js b/app/javascript/flavours/glitch/actions/notifications.js index 3a1af501c8d9e6..7a97e71bdd22e5 100644 --- a/app/javascript/flavours/glitch/actions/notifications.js +++ b/app/javascript/flavours/glitch/actions/notifications.js @@ -43,7 +43,6 @@ export const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL'; export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET'; -export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR'; export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP'; export const NOTIFICATIONS_LOAD_PENDING = 'NOTIFICATIONS_LOAD_PENDING'; @@ -186,7 +185,7 @@ const noOp = () => {}; let expandNotificationsController = new AbortController(); -export function expandNotifications({ maxId, forceLoad } = {}, done = noOp) { +export function expandNotifications({ maxId, forceLoad = false } = {}, done = noOp) { return (dispatch, getState) => { const activeFilter = getState().getIn(['settings', 'notifications', 'quickFilter', 'active']); const notifications = getState().get('notifications'); @@ -269,16 +268,6 @@ export function expandNotificationsFail(error, isLoadingMore) { }; } -export function clearNotifications() { - return (dispatch) => { - dispatch({ - type: NOTIFICATIONS_CLEAR, - }); - - api().post('/api/v1/notifications/clear'); - }; -} - export function scrollTopNotifications(top) { return { type: NOTIFICATIONS_SCROLL_TOP, diff --git a/app/javascript/flavours/glitch/actions/notifications_migration.tsx b/app/javascript/flavours/glitch/actions/notifications_migration.tsx new file mode 100644 index 00000000000000..32844d4b42e2d6 --- /dev/null +++ b/app/javascript/flavours/glitch/actions/notifications_migration.tsx @@ -0,0 +1,18 @@ +import { createAppAsyncThunk } from 'flavours/glitch/store'; + +import { fetchNotifications } from './notification_groups'; +import { expandNotifications } from './notifications'; + +export const initializeNotifications = createAppAsyncThunk( + 'notifications/initialize', + (_, { dispatch, getState }) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access + const enableBeta = getState().settings.getIn( + ['notifications', 'groupingBeta'], + false, + ) as boolean; + + if (enableBeta) void dispatch(fetchNotifications()); + else dispatch(expandNotifications()); + }, +); diff --git a/app/javascript/flavours/glitch/actions/notifications_typed.ts b/app/javascript/flavours/glitch/actions/notifications_typed.ts index 176362f4b1edd4..55896f4c378f49 100644 --- a/app/javascript/flavours/glitch/actions/notifications_typed.ts +++ b/app/javascript/flavours/glitch/actions/notifications_typed.ts @@ -1,11 +1,6 @@ import { createAction } from '@reduxjs/toolkit'; -import type { ApiAccountJSON } from '../api_types/accounts'; -// To be replaced once ApiNotificationJSON type exists -interface FakeApiNotificationJSON { - type: string; - account: ApiAccountJSON; -} +import type { ApiNotificationJSON } from 'flavours/glitch/api_types/notifications'; export const notificationsUpdate = createAction( 'notifications/update', @@ -13,7 +8,7 @@ export const notificationsUpdate = createAction( playSound, ...args }: { - notification: FakeApiNotificationJSON; + notification: ApiNotificationJSON; usePendingItems: boolean; playSound: boolean; }) => ({ diff --git a/app/javascript/flavours/glitch/actions/streaming.js b/app/javascript/flavours/glitch/actions/streaming.js index a55240646f3e69..7b006c1be7c019 100644 --- a/app/javascript/flavours/glitch/actions/streaming.js +++ b/app/javascript/flavours/glitch/actions/streaming.js @@ -10,6 +10,7 @@ import { deleteAnnouncement, } from './announcements'; import { updateConversations } from './conversations'; +import { processNewNotificationForGroups } from './notification_groups'; import { updateNotifications, expandNotifications } from './notifications'; import { updateStatus } from './statuses'; import { @@ -98,10 +99,16 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti case 'delete': dispatch(deleteFromTimelines(data.payload)); break; - case 'notification': + case 'notification': { // @ts-expect-error - dispatch(updateNotifications(JSON.parse(data.payload), messages, locale)); + const notificationJSON = JSON.parse(data.payload); + dispatch(updateNotifications(notificationJSON, messages, locale)); + // TODO: remove this once the groups feature replaces the previous one + if(getState().notificationGroups.groups.length > 0) { + dispatch(processNewNotificationForGroups(notificationJSON)); + } break; + } case 'conversation': // @ts-expect-error dispatch(updateConversations(JSON.parse(data.payload))); diff --git a/app/javascript/flavours/glitch/api/notifications.ts b/app/javascript/flavours/glitch/api/notifications.ts new file mode 100644 index 00000000000000..fe718788220d44 --- /dev/null +++ b/app/javascript/flavours/glitch/api/notifications.ts @@ -0,0 +1,18 @@ +import api, { apiRequest, getLinks } from 'flavours/glitch/api'; +import type { ApiNotificationGroupJSON } from 'flavours/glitch/api_types/notifications'; + +export const apiFetchNotifications = async (params?: { + exclude_types?: string[]; + max_id?: string; +}) => { + const response = await api().request({ + method: 'GET', + url: '/api/v2_alpha/notifications', + params, + }); + + return { notifications: response.data, links: getLinks(response) }; +}; + +export const apiClearNotifications = () => + apiRequest('POST', 'v1/notifications/clear'); diff --git a/app/javascript/flavours/glitch/api_types/notifications.ts b/app/javascript/flavours/glitch/api_types/notifications.ts new file mode 100644 index 00000000000000..ea37556d8d0af0 --- /dev/null +++ b/app/javascript/flavours/glitch/api_types/notifications.ts @@ -0,0 +1,145 @@ +// See app/serializers/rest/notification_group_serializer.rb + +import type { AccountWarningAction } from 'flavours/glitch/models/notification_group'; + +import type { ApiAccountJSON } from './accounts'; +import type { ApiReportJSON } from './reports'; +import type { ApiStatusJSON } from './statuses'; + +// See app/model/notification.rb +export const allNotificationTypes = [ + 'follow', + 'follow_request', + 'favourite', + 'reblog', + 'mention', + 'poll', + 'status', + 'update', + 'admin.sign_up', + 'admin.report', + 'moderation_warning', + 'severed_relationships', +]; + +export type NotificationWithStatusType = + | 'favourite' + | 'reblog' + | 'status' + | 'mention' + | 'poll' + | 'update'; + +export type NotificationType = + | NotificationWithStatusType + | 'follow' + | 'follow_request' + | 'moderation_warning' + | 'severed_relationships' + | 'admin.sign_up' + | 'admin.report'; + +export interface BaseNotificationJSON { + id: string; + type: NotificationType; + created_at: string; + group_key: string; + account: ApiAccountJSON; +} + +export interface BaseNotificationGroupJSON { + group_key: string; + notifications_count: number; + type: NotificationType; + sample_accounts: ApiAccountJSON[]; + latest_page_notification_at: string; // FIXME: This will only be present if the notification group is returned in a paginated list, not requested directly + most_recent_notification_id: string; + page_min_id?: string; + page_max_id?: string; +} + +interface NotificationGroupWithStatusJSON extends BaseNotificationGroupJSON { + type: NotificationWithStatusType; + status: ApiStatusJSON; +} + +interface NotificationWithStatusJSON extends BaseNotificationJSON { + type: NotificationWithStatusType; + status: ApiStatusJSON; +} + +interface ReportNotificationGroupJSON extends BaseNotificationGroupJSON { + type: 'admin.report'; + report: ApiReportJSON; +} + +interface ReportNotificationJSON extends BaseNotificationJSON { + type: 'admin.report'; + report: ApiReportJSON; +} + +type SimpleNotificationTypes = 'follow' | 'follow_request' | 'admin.sign_up'; +interface SimpleNotificationGroupJSON extends BaseNotificationGroupJSON { + type: SimpleNotificationTypes; +} + +interface SimpleNotificationJSON extends BaseNotificationJSON { + type: SimpleNotificationTypes; +} + +export interface ApiAccountWarningJSON { + id: string; + action: AccountWarningAction; + text: string; + status_ids: string[]; + created_at: string; + target_account: ApiAccountJSON; + appeal: unknown; +} + +interface ModerationWarningNotificationGroupJSON + extends BaseNotificationGroupJSON { + type: 'moderation_warning'; + moderation_warning: ApiAccountWarningJSON; +} + +interface ModerationWarningNotificationJSON extends BaseNotificationJSON { + type: 'moderation_warning'; + moderation_warning: ApiAccountWarningJSON; +} + +export interface ApiAccountRelationshipSeveranceEventJSON { + id: string; + type: 'account_suspension' | 'domain_block' | 'user_domain_block'; + purged: boolean; + target_name: string; + followers_count: number; + following_count: number; + created_at: string; +} + +interface AccountRelationshipSeveranceNotificationGroupJSON + extends BaseNotificationGroupJSON { + type: 'severed_relationships'; + event: ApiAccountRelationshipSeveranceEventJSON; +} + +interface AccountRelationshipSeveranceNotificationJSON + extends BaseNotificationJSON { + type: 'severed_relationships'; + event: ApiAccountRelationshipSeveranceEventJSON; +} + +export type ApiNotificationJSON = + | SimpleNotificationJSON + | ReportNotificationJSON + | AccountRelationshipSeveranceNotificationJSON + | NotificationWithStatusJSON + | ModerationWarningNotificationJSON; + +export type ApiNotificationGroupJSON = + | SimpleNotificationGroupJSON + | ReportNotificationGroupJSON + | AccountRelationshipSeveranceNotificationGroupJSON + | NotificationGroupWithStatusJSON + | ModerationWarningNotificationGroupJSON; diff --git a/app/javascript/flavours/glitch/api_types/reports.ts b/app/javascript/flavours/glitch/api_types/reports.ts new file mode 100644 index 00000000000000..b11cfdd2eb59c8 --- /dev/null +++ b/app/javascript/flavours/glitch/api_types/reports.ts @@ -0,0 +1,16 @@ +import type { ApiAccountJSON } from './accounts'; + +export type ReportCategory = 'other' | 'spam' | 'legal' | 'violation'; + +export interface ApiReportJSON { + id: string; + action_taken: unknown; + action_taken_at: unknown; + category: ReportCategory; + comment: string; + forwarded: boolean; + created_at: string; + status_ids: string[]; + rule_ids: string[]; + target_account: ApiAccountJSON; +} diff --git a/app/javascript/flavours/glitch/components/load_gap.tsx b/app/javascript/flavours/glitch/components/load_gap.tsx index f0d15d37761030..1870185c2914d9 100644 --- a/app/javascript/flavours/glitch/components/load_gap.tsx +++ b/app/javascript/flavours/glitch/components/load_gap.tsx @@ -9,18 +9,18 @@ const messages = defineMessages({ load_more: { id: 'status.load_more', defaultMessage: 'Load more' }, }); -interface Props { +interface Props { disabled: boolean; - maxId: string; - onClick: (maxId: string) => void; + param: T; + onClick: (params: T) => void; } -export const LoadGap: React.FC = ({ disabled, maxId, onClick }) => { +export const LoadGap = ({ disabled, param, onClick }: Props) => { const intl = useIntl(); const handleClick = useCallback(() => { - onClick(maxId); - }, [maxId, onClick]); + onClick(param); + }, [param, onClick]); return ( + ); +}; + +export const FilterBar: React.FC = () => { + const intl = useIntl(); + + const selectedFilter = useAppSelector( + selectSettingsNotificationsQuickFilterActive, + ); + const advancedMode = useAppSelector( + selectSettingsNotificationsQuickFilterAdvanced, + ); + + if (advancedMode) + return ( +
+ + + + + + + + + + + + + + + + + + + + + +
+ ); + else + return ( +
+ + + + + + +
+ ); +}; diff --git a/app/javascript/flavours/glitch/features/notifications_v2/index.tsx b/app/javascript/flavours/glitch/features/notifications_v2/index.tsx new file mode 100644 index 00000000000000..94051713751779 --- /dev/null +++ b/app/javascript/flavours/glitch/features/notifications_v2/index.tsx @@ -0,0 +1,354 @@ +import { useCallback, useEffect, useMemo, useRef } from 'react'; + +import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; + +import { Helmet } from 'react-helmet'; + +import { createSelector } from '@reduxjs/toolkit'; + +import { useDebouncedCallback } from 'use-debounce'; + +import DoneAllIcon from '@/material-icons/400-24px/done_all.svg?react'; +import NotificationsIcon from '@/material-icons/400-24px/notifications-fill.svg?react'; +import { + fetchNotificationsGap, + updateScrollPosition, + loadPending, + markNotificationsAsRead, + mountNotifications, + unmountNotifications, +} from 'flavours/glitch/actions/notification_groups'; +import { compareId } from 'flavours/glitch/compare_id'; +import { Icon } from 'flavours/glitch/components/icon'; +import { NotSignedInIndicator } from 'flavours/glitch/components/not_signed_in_indicator'; +import { useIdentity } from 'flavours/glitch/identity_context'; +import type { NotificationGap } from 'flavours/glitch/reducers/notification_groups'; +import { + selectUnreadNotificationGroupsCount, + selectPendingNotificationGroupsCount, +} from 'flavours/glitch/selectors/notifications'; +import { + selectNeedsNotificationPermission, + selectSettingsNotificationsExcludedTypes, + selectSettingsNotificationsQuickFilterActive, + selectSettingsNotificationsQuickFilterShow, + selectSettingsNotificationsShowUnread, +} from 'flavours/glitch/selectors/settings'; +import { useAppDispatch, useAppSelector } from 'flavours/glitch/store'; +import type { RootState } from 'flavours/glitch/store'; + +import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; +import { submitMarkers } from '../../actions/markers'; +import Column from '../../components/column'; +import { ColumnHeader } from '../../components/column_header'; +import { LoadGap } from '../../components/load_gap'; +import ScrollableList from '../../components/scrollable_list'; +import { FilteredNotificationsBanner } from '../notifications/components/filtered_notifications_banner'; +import NotificationsPermissionBanner from '../notifications/components/notifications_permission_banner'; +import ColumnSettingsContainer from '../notifications/containers/column_settings_container'; + +import { NotificationGroup } from './components/notification_group'; +import { FilterBar } from './filter_bar'; + +const messages = defineMessages({ + title: { id: 'column.notifications', defaultMessage: 'Notifications' }, + markAsRead: { + id: 'notifications.mark_as_read', + defaultMessage: 'Mark every notification as read', + }, +}); + +const getNotifications = createSelector( + [ + selectSettingsNotificationsQuickFilterShow, + selectSettingsNotificationsQuickFilterActive, + selectSettingsNotificationsExcludedTypes, + (state: RootState) => state.notificationGroups.groups, + ], + (showFilterBar, allowedType, excludedTypes, notifications) => { + if (!showFilterBar || allowedType === 'all') { + // used if user changed the notification settings after loading the notifications from the server + // otherwise a list of notifications will come pre-filtered from the backend + // we need to turn it off for FilterBar in order not to block ourselves from seeing a specific category + return notifications.filter( + (item) => item.type === 'gap' || !excludedTypes.includes(item.type), + ); + } + return notifications.filter( + (item) => item.type === 'gap' || allowedType === item.type, + ); + }, +); + +export const Notifications: React.FC<{ + columnId?: string; + multiColumn?: boolean; +}> = ({ columnId, multiColumn }) => { + const intl = useIntl(); + const notifications = useAppSelector(getNotifications); + const dispatch = useAppDispatch(); + const isLoading = useAppSelector((s) => s.notificationGroups.isLoading); + const hasMore = notifications.at(-1)?.type === 'gap'; + + const lastReadId = useAppSelector((s) => + selectSettingsNotificationsShowUnread(s) + ? s.notificationGroups.lastReadId + : '0', + ); + + const numPending = useAppSelector(selectPendingNotificationGroupsCount); + + const unreadNotificationsCount = useAppSelector( + selectUnreadNotificationGroupsCount, + ); + + const isUnread = unreadNotificationsCount > 0; + + const canMarkAsRead = + useAppSelector(selectSettingsNotificationsShowUnread) && + unreadNotificationsCount > 0; + + const needsNotificationPermission = useAppSelector( + selectNeedsNotificationPermission, + ); + + const columnRef = useRef(null); + + const selectChild = useCallback((index: number, alignTop: boolean) => { + const container = columnRef.current?.node as HTMLElement | undefined; + + if (!container) return; + + const element = container.querySelector( + `article:nth-of-type(${index + 1}) .focusable`, + ); + + if (element) { + if (alignTop && container.scrollTop > element.offsetTop) { + element.scrollIntoView(true); + } else if ( + !alignTop && + container.scrollTop + container.clientHeight < + element.offsetTop + element.offsetHeight + ) { + element.scrollIntoView(false); + } + element.focus(); + } + }, []); + + // Keep track of mounted components for unread notification handling + useEffect(() => { + dispatch(mountNotifications()); + + return () => { + dispatch(unmountNotifications()); + dispatch(updateScrollPosition({ top: false })); + }; + }, [dispatch]); + + const handleLoadGap = useCallback( + (gap: NotificationGap) => { + void dispatch(fetchNotificationsGap({ gap })); + }, + [dispatch], + ); + + const handleLoadOlder = useDebouncedCallback( + () => { + const gap = notifications.at(-1); + if (gap?.type === 'gap') void dispatch(fetchNotificationsGap({ gap })); + }, + 300, + { leading: true }, + ); + + const handleLoadPending = useCallback(() => { + dispatch(loadPending()); + }, [dispatch]); + + const handleScrollToTop = useDebouncedCallback(() => { + dispatch(updateScrollPosition({ top: true })); + }, 100); + + const handleScroll = useDebouncedCallback(() => { + dispatch(updateScrollPosition({ top: false })); + }, 100); + + useEffect(() => { + return () => { + handleLoadOlder.cancel(); + handleScrollToTop.cancel(); + handleScroll.cancel(); + }; + }, [handleLoadOlder, handleScrollToTop, handleScroll]); + + const handlePin = useCallback(() => { + if (columnId) { + dispatch(removeColumn(columnId)); + } else { + dispatch(addColumn('NOTIFICATIONS', {})); + } + }, [columnId, dispatch]); + + const handleMove = useCallback( + (dir: unknown) => { + dispatch(moveColumn(columnId, dir)); + }, + [dispatch, columnId], + ); + + const handleHeaderClick = useCallback(() => { + columnRef.current?.scrollTop(); + }, []); + + const handleMoveUp = useCallback( + (id: string) => { + const elementIndex = + notifications.findIndex( + (item) => item.type !== 'gap' && item.group_key === id, + ) - 1; + selectChild(elementIndex, true); + }, + [notifications, selectChild], + ); + + const handleMoveDown = useCallback( + (id: string) => { + const elementIndex = + notifications.findIndex( + (item) => item.type !== 'gap' && item.group_key === id, + ) + 1; + selectChild(elementIndex, false); + }, + [notifications, selectChild], + ); + + const handleMarkAsRead = useCallback(() => { + dispatch(markNotificationsAsRead()); + void dispatch(submitMarkers({ immediate: true })); + }, [dispatch]); + + const pinned = !!columnId; + const emptyMessage = ( + + ); + + const { signedIn } = useIdentity(); + + const filterBar = signedIn ? : null; + + const scrollableContent = useMemo(() => { + if (notifications.length === 0 && !hasMore) return null; + + return notifications.map((item) => + item.type === 'gap' ? ( + + ) : ( + 0 + } + /> + ), + ); + }, [ + notifications, + isLoading, + hasMore, + lastReadId, + handleLoadGap, + handleMoveUp, + handleMoveDown, + ]); + + const prepend = ( + <> + {needsNotificationPermission && } + + + ); + + const scrollContainer = signedIn ? ( + + {scrollableContent} + + ) : ( + + ); + + const extraButton = canMarkAsRead ? ( + + ) : null; + + return ( + + + + + + {filterBar} + + {scrollContainer} + + + {intl.formatMessage(messages.title)} + + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export default Notifications; diff --git a/app/javascript/flavours/glitch/features/notifications_wrapper.jsx b/app/javascript/flavours/glitch/features/notifications_wrapper.jsx new file mode 100644 index 00000000000000..15ab3367cc1bf8 --- /dev/null +++ b/app/javascript/flavours/glitch/features/notifications_wrapper.jsx @@ -0,0 +1,13 @@ +import Notifications from 'flavours/glitch/features/notifications'; +import Notifications_v2 from 'flavours/glitch/features/notifications_v2'; +import { useAppSelector } from 'flavours/glitch/store'; + +export const NotificationsWrapper = (props) => { + const optedInGroupedNotifications = useAppSelector((state) => state.getIn(['settings', 'notifications', 'groupingBeta'], false)); + + return ( + optedInGroupedNotifications ? : + ); +}; + +export default NotificationsWrapper; \ No newline at end of file diff --git a/app/javascript/flavours/glitch/features/ui/components/columns_area.jsx b/app/javascript/flavours/glitch/features/ui/components/columns_area.jsx index 9166499c5a08b9..e4c67ed65ef728 100644 --- a/app/javascript/flavours/glitch/features/ui/components/columns_area.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/columns_area.jsx @@ -10,7 +10,7 @@ import { scrollRight } from '../../../scroll'; import BundleContainer from '../containers/bundle_container'; import { Compose, - Notifications, + NotificationsWrapper, HomeTimeline, CommunityTimeline, PublicTimeline, @@ -32,7 +32,7 @@ import NavigationPanel from './navigation_panel'; const componentMap = { 'COMPOSE': Compose, 'HOME': HomeTimeline, - 'NOTIFICATIONS': Notifications, + 'NOTIFICATIONS': NotificationsWrapper, 'PUBLIC': PublicTimeline, 'REMOTE': PublicTimeline, 'COMMUNITY': CommunityTimeline, diff --git a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx index 98d82342cf3b6e..6153b3ceaa7c4e 100644 --- a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx @@ -33,6 +33,7 @@ import { NavigationPortal } from 'flavours/glitch/components/navigation_portal'; import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { timelinePreview, trendsEnabled } from 'flavours/glitch/initial_state'; import { transientSingleColumn } from 'flavours/glitch/is_mobile'; +import { selectUnreadNotificationGroupsCount } from 'flavours/glitch/selectors/notifications'; import { preferencesLink } from 'flavours/glitch/utils/backend_links'; import ColumnLink from './column_link'; @@ -60,15 +61,19 @@ const messages = defineMessages({ }); const NotificationsLink = () => { + const optedInGroupedNotifications = useSelector((state) => state.getIn(['settings', 'notifications', 'groupingBeta'], false)); const count = useSelector(state => state.getIn(['local_settings', 'notifications', 'tab_badge']) ? state.getIn(['notifications', 'unread']) : 0); const intl = useIntl(); + const newCount = useSelector(selectUnreadNotificationGroupsCount); + return ( } - activeIcon={} + icon={} + activeIcon={} text={intl.formatMessage(messages.notifications)} /> ); diff --git a/app/javascript/flavours/glitch/features/ui/index.jsx b/app/javascript/flavours/glitch/features/ui/index.jsx index 221f789a315995..7a2aa21f8812c7 100644 --- a/app/javascript/flavours/glitch/features/ui/index.jsx +++ b/app/javascript/flavours/glitch/features/ui/index.jsx @@ -12,8 +12,9 @@ import Favico from 'favico.js'; import { debounce } from 'lodash'; import { HotKeys } from 'react-hotkeys'; -import { changeLayout } from 'flavours/glitch/actions/app'; +import { focusApp, unfocusApp, changeLayout } from 'flavours/glitch/actions/app'; import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'flavours/glitch/actions/markers'; +import { initializeNotifications } from 'flavours/glitch/actions/notifications_migration'; import { INTRODUCTION_VERSION } from 'flavours/glitch/actions/onboarding'; import { HoverCardController } from 'flavours/glitch/components/hover_card_controller'; import { Permalink } from 'flavours/glitch/components/permalink'; @@ -24,7 +25,7 @@ import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import { uploadCompose, resetCompose, changeComposeSpoilerness } from '../../actions/compose'; import { clearHeight } from '../../actions/height_cache'; -import { expandNotifications, notificationsSetVisibility } from '../../actions/notifications'; +import { notificationsSetVisibility } from '../../actions/notifications'; import { fetchServer, fetchServerTranslationLanguages } from '../../actions/server'; import { expandHomeTimeline } from '../../actions/timelines'; import initialState, { me, owner, singleUserMode, trendsEnabled, trendsAsLanding, disableHoverCards } from '../../initial_state'; @@ -51,7 +52,7 @@ import { Favourites, DirectTimeline, HashtagTimeline, - Notifications, + NotificationsWrapper, NotificationRequests, NotificationRequest, FollowRequests, @@ -74,6 +75,7 @@ import { } from './util/async-components'; import { ColumnsContextProvider } from './util/columns_context'; import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers'; + // Dummy import, to make sure that ends up in the application bundle. // Without this it ends up in ~8 very commonly used bundles. import '../../components/status'; @@ -214,7 +216,7 @@ class SwitchingColumnsArea extends PureComponent { - + @@ -304,7 +306,10 @@ class UI extends PureComponent { const visibility = !document[this.visibilityHiddenProp]; this.props.dispatch(notificationsSetVisibility(visibility)); if (visibility) { + this.props.dispatch(focusApp()); this.props.dispatch(submitMarkers({ immediate: true })); + } else { + this.props.dispatch(unfocusApp()); } }; @@ -419,7 +424,7 @@ class UI extends PureComponent { if (signedIn) { this.props.dispatch(fetchMarkers()); this.props.dispatch(expandHomeTimeline()); - this.props.dispatch(expandNotifications()); + this.props.dispatch(initializeNotifications()); this.props.dispatch(fetchServerTranslationLanguages()); setTimeout(() => this.props.dispatch(fetchServer()), 3000); diff --git a/app/javascript/flavours/glitch/features/ui/util/async-components.js b/app/javascript/flavours/glitch/features/ui/util/async-components.js index a312cefff7dcfb..e334e1a3b6ea38 100644 --- a/app/javascript/flavours/glitch/features/ui/util/async-components.js +++ b/app/javascript/flavours/glitch/features/ui/util/async-components.js @@ -7,7 +7,15 @@ export function Compose () { } export function Notifications () { - return import(/* webpackChunkName: "flavours/glitch/async/notifications" */'../../notifications'); + return import(/* webpackChunkName: "flavours/glitch/async/notifications_v1" */'../../notifications'); +} + +export function Notifications_v2 () { + return import(/* webpackChunkName: "flavours/glitch/async/notifications_v2" */'../../notifications_v2'); +} + +export function NotificationsWrapper () { + return import(/* webpackChunkName: "flavours/glitch/async/notifications" */'../../notifications_wrapper'); } export function HomeTimeline () { diff --git a/app/javascript/flavours/glitch/models/notification_group.ts b/app/javascript/flavours/glitch/models/notification_group.ts new file mode 100644 index 00000000000000..6c8b8eb6e3d555 --- /dev/null +++ b/app/javascript/flavours/glitch/models/notification_group.ts @@ -0,0 +1,207 @@ +import type { + ApiAccountRelationshipSeveranceEventJSON, + ApiAccountWarningJSON, + BaseNotificationGroupJSON, + ApiNotificationGroupJSON, + ApiNotificationJSON, + NotificationType, + NotificationWithStatusType, +} from 'flavours/glitch/api_types/notifications'; +import type { ApiReportJSON } from 'flavours/glitch/api_types/reports'; + +// Maximum number of avatars displayed in a notification group +// This corresponds to the max lenght of `group.sampleAccountIds` +export const NOTIFICATIONS_GROUP_MAX_AVATARS = 8; + +interface BaseNotificationGroup + extends Omit { + sampleAccountIds: string[]; +} + +interface BaseNotificationWithStatus + extends BaseNotificationGroup { + type: Type; + statusId: string; +} + +interface BaseNotification + extends BaseNotificationGroup { + type: Type; +} + +export type NotificationGroupFavourite = + BaseNotificationWithStatus<'favourite'>; +export type NotificationGroupReblog = BaseNotificationWithStatus<'reblog'>; +export type NotificationGroupStatus = BaseNotificationWithStatus<'status'>; +export type NotificationGroupMention = BaseNotificationWithStatus<'mention'>; +export type NotificationGroupPoll = BaseNotificationWithStatus<'poll'>; +export type NotificationGroupUpdate = BaseNotificationWithStatus<'update'>; +export type NotificationGroupFollow = BaseNotification<'follow'>; +export type NotificationGroupFollowRequest = BaseNotification<'follow_request'>; +export type NotificationGroupAdminSignUp = BaseNotification<'admin.sign_up'>; + +export type AccountWarningAction = + | 'none' + | 'disable' + | 'mark_statuses_as_sensitive' + | 'delete_statuses' + | 'sensitive' + | 'silence' + | 'suspend'; +export interface AccountWarning + extends Omit { + targetAccountId: string; +} + +export interface NotificationGroupModerationWarning + extends BaseNotification<'moderation_warning'> { + moderationWarning: AccountWarning; +} + +type AccountRelationshipSeveranceEvent = + ApiAccountRelationshipSeveranceEventJSON; +export interface NotificationGroupSeveredRelationships + extends BaseNotification<'severed_relationships'> { + event: AccountRelationshipSeveranceEvent; +} + +interface Report extends Omit { + targetAccountId: string; +} + +export interface NotificationGroupAdminReport + extends BaseNotification<'admin.report'> { + report: Report; +} + +export type NotificationGroup = + | NotificationGroupFavourite + | NotificationGroupReblog + | NotificationGroupStatus + | NotificationGroupMention + | NotificationGroupPoll + | NotificationGroupUpdate + | NotificationGroupFollow + | NotificationGroupFollowRequest + | NotificationGroupModerationWarning + | NotificationGroupSeveredRelationships + | NotificationGroupAdminSignUp + | NotificationGroupAdminReport; + +function createReportFromJSON(reportJSON: ApiReportJSON): Report { + const { target_account, ...report } = reportJSON; + return { + targetAccountId: target_account.id, + ...report, + }; +} + +function createAccountWarningFromJSON( + warningJSON: ApiAccountWarningJSON, +): AccountWarning { + const { target_account, ...warning } = warningJSON; + return { + targetAccountId: target_account.id, + ...warning, + }; +} + +function createAccountRelationshipSeveranceEventFromJSON( + eventJson: ApiAccountRelationshipSeveranceEventJSON, +): AccountRelationshipSeveranceEvent { + return eventJson; +} + +export function createNotificationGroupFromJSON( + groupJson: ApiNotificationGroupJSON, +): NotificationGroup { + const { sample_accounts, ...group } = groupJson; + const sampleAccountIds = sample_accounts.map((account) => account.id); + + switch (group.type) { + case 'favourite': + case 'reblog': + case 'status': + case 'mention': + case 'poll': + case 'update': { + const { status, ...groupWithoutStatus } = group; + return { + statusId: status.id, + sampleAccountIds, + ...groupWithoutStatus, + }; + } + case 'admin.report': { + const { report, ...groupWithoutTargetAccount } = group; + return { + report: createReportFromJSON(report), + sampleAccountIds, + ...groupWithoutTargetAccount, + }; + } + case 'severed_relationships': + return { + ...group, + event: createAccountRelationshipSeveranceEventFromJSON(group.event), + sampleAccountIds, + }; + + case 'moderation_warning': { + const { moderation_warning, ...groupWithoutModerationWarning } = group; + return { + ...groupWithoutModerationWarning, + moderationWarning: createAccountWarningFromJSON(moderation_warning), + sampleAccountIds, + }; + } + default: + return { + sampleAccountIds, + ...group, + }; + } +} + +export function createNotificationGroupFromNotificationJSON( + notification: ApiNotificationJSON, +) { + const group = { + sampleAccountIds: [notification.account.id], + group_key: notification.group_key, + notifications_count: 1, + type: notification.type, + most_recent_notification_id: notification.id, + page_min_id: notification.id, + page_max_id: notification.id, + latest_page_notification_at: notification.created_at, + } as NotificationGroup; + + switch (notification.type) { + case 'favourite': + case 'reblog': + case 'status': + case 'mention': + case 'poll': + case 'update': + return { ...group, statusId: notification.status.id }; + case 'admin.report': + return { ...group, report: createReportFromJSON(notification.report) }; + case 'severed_relationships': + return { + ...group, + event: createAccountRelationshipSeveranceEventFromJSON( + notification.event, + ), + }; + case 'moderation_warning': + return { + ...group, + moderationWarning: createAccountWarningFromJSON( + notification.moderation_warning, + ), + }; + default: + return group; + } +} diff --git a/app/javascript/flavours/glitch/reducers/index.ts b/app/javascript/flavours/glitch/reducers/index.ts index 3fee5818f981d5..d8d50be7d21621 100644 --- a/app/javascript/flavours/glitch/reducers/index.ts +++ b/app/javascript/flavours/glitch/reducers/index.ts @@ -25,6 +25,7 @@ import { markersReducer } from './markers'; import media_attachments from './media_attachments'; import meta from './meta'; import { modalReducer } from './modal'; +import { notificationGroupsReducer } from './notification_groups'; import { notificationPolicyReducer } from './notification_policy'; import { notificationRequestsReducer } from './notification_requests'; import notifications from './notifications'; @@ -68,6 +69,7 @@ const reducers = { search, media_attachments, notifications, + notificationGroups: notificationGroupsReducer, height_cache, custom_emojis, lists, diff --git a/app/javascript/flavours/glitch/reducers/markers.ts b/app/javascript/flavours/glitch/reducers/markers.ts index 380e9d39952378..8b997e47be2eab 100644 --- a/app/javascript/flavours/glitch/reducers/markers.ts +++ b/app/javascript/flavours/glitch/reducers/markers.ts @@ -1,6 +1,10 @@ import { createReducer } from '@reduxjs/toolkit'; -import { submitMarkersAction } from 'flavours/glitch/actions/markers'; +import { + submitMarkersAction, + fetchMarkers, +} from 'flavours/glitch/actions/markers'; +import { compareId } from 'flavours/glitch/compare_id'; const initialState = { home: '0', @@ -15,4 +19,23 @@ export const markersReducer = createReducer(initialState, (builder) => { if (notifications) state.notifications = notifications; }, ); + builder.addCase( + fetchMarkers.fulfilled, + ( + state, + { + payload: { + markers: { home, notifications }, + }, + }, + ) => { + if (home && compareId(home.last_read_id, state.home) > 0) + state.home = home.last_read_id; + if ( + notifications && + compareId(notifications.last_read_id, state.notifications) > 0 + ) + state.notifications = notifications.last_read_id; + }, + ); }); diff --git a/app/javascript/flavours/glitch/reducers/notification_groups.ts b/app/javascript/flavours/glitch/reducers/notification_groups.ts new file mode 100644 index 00000000000000..b0b284b696e4bb --- /dev/null +++ b/app/javascript/flavours/glitch/reducers/notification_groups.ts @@ -0,0 +1,508 @@ +import { createReducer, isAnyOf } from '@reduxjs/toolkit'; + +import { + authorizeFollowRequestSuccess, + blockAccountSuccess, + muteAccountSuccess, + rejectFollowRequestSuccess, +} from 'flavours/glitch/actions/accounts_typed'; +import { focusApp, unfocusApp } from 'flavours/glitch/actions/app'; +import { blockDomainSuccess } from 'flavours/glitch/actions/domain_blocks_typed'; +import { fetchMarkers } from 'flavours/glitch/actions/markers'; +import { + clearNotifications, + fetchNotifications, + fetchNotificationsGap, + processNewNotificationForGroups, + loadPending, + updateScrollPosition, + markNotificationsAsRead, + mountNotifications, + unmountNotifications, +} from 'flavours/glitch/actions/notification_groups'; +import { + disconnectTimeline, + timelineDelete, +} from 'flavours/glitch/actions/timelines_typed'; +import type { ApiNotificationJSON } from 'flavours/glitch/api_types/notifications'; +import { compareId } from 'flavours/glitch/compare_id'; +import { usePendingItems } from 'flavours/glitch/initial_state'; +import { + NOTIFICATIONS_GROUP_MAX_AVATARS, + createNotificationGroupFromJSON, + createNotificationGroupFromNotificationJSON, +} from 'flavours/glitch/models/notification_group'; +import type { NotificationGroup } from 'flavours/glitch/models/notification_group'; + +const NOTIFICATIONS_TRIM_LIMIT = 50; + +export interface NotificationGap { + type: 'gap'; + maxId?: string; + sinceId?: string; +} + +interface NotificationGroupsState { + groups: (NotificationGroup | NotificationGap)[]; + pendingGroups: (NotificationGroup | NotificationGap)[]; + scrolledToTop: boolean; + isLoading: boolean; + lastReadId: string; + mounted: number; + isTabVisible: boolean; +} + +const initialState: NotificationGroupsState = { + groups: [], + pendingGroups: [], // holds pending groups in slow mode + scrolledToTop: false, + isLoading: false, + // The following properties are used to track unread notifications + lastReadId: '0', // used for unread notifications + mounted: 0, // number of mounted notification list components, usually 0 or 1 + isTabVisible: true, +}; + +function filterNotificationsForAccounts( + groups: NotificationGroupsState['groups'], + accountIds: string[], + onlyForType?: string, +) { + groups = groups + .map((group) => { + if ( + group.type !== 'gap' && + (!onlyForType || group.type === onlyForType) + ) { + const previousLength = group.sampleAccountIds.length; + + group.sampleAccountIds = group.sampleAccountIds.filter( + (id) => !accountIds.includes(id), + ); + + const newLength = group.sampleAccountIds.length; + const removed = previousLength - newLength; + + group.notifications_count -= removed; + } + + return group; + }) + .filter( + (group) => group.type === 'gap' || group.sampleAccountIds.length > 0, + ); + mergeGaps(groups); + return groups; +} + +function filterNotificationsForStatus( + groups: NotificationGroupsState['groups'], + statusId: string, +) { + groups = groups.filter( + (group) => + group.type === 'gap' || + !('statusId' in group) || + group.statusId !== statusId, + ); + mergeGaps(groups); + return groups; +} + +function removeNotificationsForAccounts( + state: NotificationGroupsState, + accountIds: string[], + onlyForType?: string, +) { + state.groups = filterNotificationsForAccounts( + state.groups, + accountIds, + onlyForType, + ); + state.pendingGroups = filterNotificationsForAccounts( + state.pendingGroups, + accountIds, + onlyForType, + ); +} + +function removeNotificationsForStatus( + state: NotificationGroupsState, + statusId: string, +) { + state.groups = filterNotificationsForStatus(state.groups, statusId); + state.pendingGroups = filterNotificationsForStatus( + state.pendingGroups, + statusId, + ); +} + +function isNotificationGroup( + groupOrGap: NotificationGroup | NotificationGap, +): groupOrGap is NotificationGroup { + return groupOrGap.type !== 'gap'; +} + +// Merge adjacent gaps in `groups` in-place +function mergeGaps(groups: NotificationGroupsState['groups']) { + for (let i = 0; i < groups.length; i++) { + const firstGroupOrGap = groups[i]; + + if (firstGroupOrGap?.type === 'gap') { + let lastGap = firstGroupOrGap; + let j = i + 1; + + for (; j < groups.length; j++) { + const groupOrGap = groups[j]; + if (groupOrGap?.type === 'gap') lastGap = groupOrGap; + else break; + } + + if (j - i > 1) { + groups.splice(i, j - i, { + type: 'gap', + maxId: firstGroupOrGap.maxId, + sinceId: lastGap.sinceId, + }); + } + } + } +} + +// Checks if `groups[index-1]` and `groups[index]` are gaps, and merge them in-place if they are +function mergeGapsAround( + groups: NotificationGroupsState['groups'], + index: number, +) { + if (index > 0) { + const potentialFirstGap = groups[index - 1]; + const potentialSecondGap = groups[index]; + + if ( + potentialFirstGap?.type === 'gap' && + potentialSecondGap?.type === 'gap' + ) { + groups.splice(index - 1, 2, { + type: 'gap', + maxId: potentialFirstGap.maxId, + sinceId: potentialSecondGap.sinceId, + }); + } + } +} + +function processNewNotification( + groups: NotificationGroupsState['groups'], + notification: ApiNotificationJSON, +) { + const existingGroupIndex = groups.findIndex( + (group) => + group.type !== 'gap' && group.group_key === notification.group_key, + ); + + // In any case, we are going to add a group at the top + // If there is currently a gap at the top, now is the time to update it + if (groups.length > 0 && groups[0]?.type === 'gap') { + groups[0].maxId = notification.id; + } + + if (existingGroupIndex > -1) { + const existingGroup = groups[existingGroupIndex]; + + if ( + existingGroup && + existingGroup.type !== 'gap' && + !existingGroup.sampleAccountIds.includes(notification.account.id) // This can happen for example if you like, then unlike, then like again the same post + ) { + // Update the existing group + if ( + existingGroup.sampleAccountIds.unshift(notification.account.id) > + NOTIFICATIONS_GROUP_MAX_AVATARS + ) + existingGroup.sampleAccountIds.pop(); + + existingGroup.most_recent_notification_id = notification.id; + existingGroup.page_max_id = notification.id; + existingGroup.latest_page_notification_at = notification.created_at; + existingGroup.notifications_count += 1; + + groups.splice(existingGroupIndex, 1); + mergeGapsAround(groups, existingGroupIndex); + + groups.unshift(existingGroup); + } + } else { + // Create a new group + groups.unshift(createNotificationGroupFromNotificationJSON(notification)); + } +} + +function trimNotifications(state: NotificationGroupsState) { + if (state.scrolledToTop) { + state.groups.splice(NOTIFICATIONS_TRIM_LIMIT); + } +} + +function shouldMarkNewNotificationsAsRead( + { + isTabVisible, + scrolledToTop, + mounted, + lastReadId, + groups, + }: NotificationGroupsState, + ignoreScroll = false, +) { + const isMounted = mounted > 0; + const oldestGroup = groups.findLast(isNotificationGroup); + const hasMore = groups.at(-1)?.type === 'gap'; + const oldestGroupReached = + !hasMore || + lastReadId === '0' || + (oldestGroup?.page_min_id && + compareId(oldestGroup.page_min_id, lastReadId) <= 0); + + return ( + isTabVisible && + (ignoreScroll || scrolledToTop) && + isMounted && + oldestGroupReached + ); +} + +function updateLastReadId( + state: NotificationGroupsState, + group: NotificationGroup | undefined = undefined, +) { + if (shouldMarkNewNotificationsAsRead(state)) { + group = group ?? state.groups.find(isNotificationGroup); + if ( + group?.page_max_id && + compareId(state.lastReadId, group.page_max_id) < 0 + ) + state.lastReadId = group.page_max_id; + } +} + +export const notificationGroupsReducer = createReducer( + initialState, + (builder) => { + builder + .addCase(fetchNotifications.fulfilled, (state, action) => { + state.groups = action.payload.map((json) => + json.type === 'gap' ? json : createNotificationGroupFromJSON(json), + ); + state.isLoading = false; + updateLastReadId(state); + }) + .addCase(fetchNotificationsGap.fulfilled, (state, action) => { + const { notifications } = action.payload; + + // find the gap in the existing notifications + const gapIndex = state.groups.findIndex( + (groupOrGap) => + groupOrGap.type === 'gap' && + groupOrGap.sinceId === action.meta.arg.gap.sinceId && + groupOrGap.maxId === action.meta.arg.gap.maxId, + ); + + if (gapIndex < 0) + // We do not know where to insert, let's return + return; + + // Filling a disconnection gap means we're getting historical data + // about groups we may know or may not know about. + + // The notifications timeline is split in two by the gap, with + // group information newer than the gap, and group information older + // than the gap. + + // Filling a gap should not touch anything before the gap, so any + // information on groups already appearing before the gap should be + // discarded, while any information on groups appearing after the gap + // can be updated and re-ordered. + + const oldestPageNotification = notifications.at(-1)?.page_min_id; + + // replace the gap with the notifications + a new gap + + const newerGroupKeys = state.groups + .slice(0, gapIndex) + .filter(isNotificationGroup) + .map((group) => group.group_key); + + const toInsert: NotificationGroupsState['groups'] = notifications + .map((json) => createNotificationGroupFromJSON(json)) + .filter( + (notification) => !newerGroupKeys.includes(notification.group_key), + ); + + const apiGroupKeys = (toInsert as NotificationGroup[]).map( + (group) => group.group_key, + ); + + const sinceId = action.meta.arg.gap.sinceId; + if ( + notifications.length > 0 && + !( + oldestPageNotification && + sinceId && + compareId(oldestPageNotification, sinceId) <= 0 + ) + ) { + // If we get an empty page, it means we reached the bottom, so we do not need to insert a new gap + // Similarly, if we've fetched more than the gap's, this means we have completely filled it + toInsert.push({ + type: 'gap', + maxId: notifications.at(-1)?.page_max_id, + sinceId, + } as NotificationGap); + } + + // Remove older groups covered by the API + state.groups = state.groups.filter( + (groupOrGap) => + groupOrGap.type !== 'gap' && + !apiGroupKeys.includes(groupOrGap.group_key), + ); + + // Replace the gap with API results (+ the new gap if needed) + state.groups.splice(gapIndex, 1, ...toInsert); + + // Finally, merge any adjacent gaps that could have been created by filtering + // groups earlier + mergeGaps(state.groups); + + state.isLoading = false; + + updateLastReadId(state); + }) + .addCase(processNewNotificationForGroups.fulfilled, (state, action) => { + const notification = action.payload; + processNewNotification( + usePendingItems ? state.pendingGroups : state.groups, + notification, + ); + updateLastReadId(state); + trimNotifications(state); + }) + .addCase(disconnectTimeline, (state, action) => { + if (action.payload.timeline === 'home') { + if (state.groups.length > 0 && state.groups[0]?.type !== 'gap') { + state.groups.unshift({ + type: 'gap', + sinceId: state.groups[0]?.page_min_id, + }); + } + } + }) + .addCase(timelineDelete, (state, action) => { + removeNotificationsForStatus(state, action.payload.statusId); + }) + .addCase(clearNotifications.pending, (state) => { + state.groups = []; + state.pendingGroups = []; + }) + .addCase(blockAccountSuccess, (state, action) => { + removeNotificationsForAccounts(state, [action.payload.relationship.id]); + }) + .addCase(muteAccountSuccess, (state, action) => { + if (action.payload.relationship.muting_notifications) + removeNotificationsForAccounts(state, [ + action.payload.relationship.id, + ]); + }) + .addCase(blockDomainSuccess, (state, action) => { + removeNotificationsForAccounts( + state, + action.payload.accounts.map((account) => account.id), + ); + }) + .addCase(loadPending, (state) => { + // First, remove any existing group and merge data + state.pendingGroups.forEach((group) => { + if (group.type !== 'gap') { + const existingGroupIndex = state.groups.findIndex( + (groupOrGap) => + isNotificationGroup(groupOrGap) && + groupOrGap.group_key === group.group_key, + ); + if (existingGroupIndex > -1) { + const existingGroup = state.groups[existingGroupIndex]; + if (existingGroup && existingGroup.type !== 'gap') { + group.notifications_count += existingGroup.notifications_count; + group.sampleAccountIds = group.sampleAccountIds + .concat(existingGroup.sampleAccountIds) + .slice(0, NOTIFICATIONS_GROUP_MAX_AVATARS); + state.groups.splice(existingGroupIndex, 1); + } + } + } + trimNotifications(state); + }); + + // Then build the consolidated list and clear pending groups + state.groups = state.pendingGroups.concat(state.groups); + state.pendingGroups = []; + }) + .addCase(updateScrollPosition, (state, action) => { + state.scrolledToTop = action.payload.top; + updateLastReadId(state); + trimNotifications(state); + }) + .addCase(markNotificationsAsRead, (state) => { + const mostRecentGroup = state.groups.find(isNotificationGroup); + if ( + mostRecentGroup?.page_max_id && + compareId(state.lastReadId, mostRecentGroup.page_max_id) < 0 + ) + state.lastReadId = mostRecentGroup.page_max_id; + }) + .addCase(fetchMarkers.fulfilled, (state, action) => { + if ( + action.payload.markers.notifications && + compareId( + state.lastReadId, + action.payload.markers.notifications.last_read_id, + ) < 0 + ) + state.lastReadId = action.payload.markers.notifications.last_read_id; + }) + .addCase(mountNotifications, (state) => { + state.mounted += 1; + updateLastReadId(state); + }) + .addCase(unmountNotifications, (state) => { + state.mounted -= 1; + }) + .addCase(focusApp, (state) => { + state.isTabVisible = true; + updateLastReadId(state); + }) + .addCase(unfocusApp, (state) => { + state.isTabVisible = false; + }) + .addMatcher( + isAnyOf(authorizeFollowRequestSuccess, rejectFollowRequestSuccess), + (state, action) => { + removeNotificationsForAccounts( + state, + [action.payload.id], + 'follow_request', + ); + }, + ) + .addMatcher( + isAnyOf(fetchNotifications.pending, fetchNotificationsGap.pending), + (state) => { + state.isLoading = true; + }, + ) + .addMatcher( + isAnyOf(fetchNotifications.rejected, fetchNotificationsGap.rejected), + (state) => { + state.isLoading = false; + }, + ); + }, +); diff --git a/app/javascript/flavours/glitch/reducers/notifications.js b/app/javascript/flavours/glitch/reducers/notifications.js index 226f2affe1030a..b3c138583c7f7c 100644 --- a/app/javascript/flavours/glitch/reducers/notifications.js +++ b/app/javascript/flavours/glitch/reducers/notifications.js @@ -12,6 +12,7 @@ import { import { fetchMarkers, } from '../actions/markers'; +import { clearNotifications } from '../actions/notification_groups'; import { NOTIFICATIONS_MOUNT, NOTIFICATIONS_UNMOUNT, @@ -21,7 +22,6 @@ import { NOTIFICATIONS_EXPAND_REQUEST, NOTIFICATIONS_EXPAND_FAIL, NOTIFICATIONS_FILTER_SET, - NOTIFICATIONS_CLEAR, NOTIFICATIONS_SCROLL_TOP, NOTIFICATIONS_LOAD_PENDING, NOTIFICATIONS_DELETE_MARKED_REQUEST, @@ -332,7 +332,7 @@ export default function notifications(state = initialState, action) { case authorizeFollowRequestSuccess.type: case rejectFollowRequestSuccess.type: return filterNotifications(state, [action.payload.id], 'follow_request'); - case NOTIFICATIONS_CLEAR: + case clearNotifications.pending.type: return state.set('items', ImmutableList()).set('pendingItems', ImmutableList()).set('hasMore', false); case timelineDelete.type: return deleteByStatus(state, action.payload.statusId); diff --git a/app/javascript/flavours/glitch/selectors/notifications.ts b/app/javascript/flavours/glitch/selectors/notifications.ts new file mode 100644 index 00000000000000..7cc64aa6c584da --- /dev/null +++ b/app/javascript/flavours/glitch/selectors/notifications.ts @@ -0,0 +1,34 @@ +import { createSelector } from '@reduxjs/toolkit'; + +import { compareId } from 'flavours/glitch/compare_id'; +import type { RootState } from 'flavours/glitch/store'; + +export const selectUnreadNotificationGroupsCount = createSelector( + [ + (s: RootState) => s.notificationGroups.lastReadId, + (s: RootState) => s.notificationGroups.pendingGroups, + (s: RootState) => s.notificationGroups.groups, + ], + (notificationMarker, pendingGroups, groups) => { + return ( + groups.filter( + (group) => + group.type !== 'gap' && + group.page_max_id && + compareId(group.page_max_id, notificationMarker) > 0, + ).length + + pendingGroups.filter( + (group) => + group.type !== 'gap' && + group.page_max_id && + compareId(group.page_max_id, notificationMarker) > 0, + ).length + ); + }, +); + +export const selectPendingNotificationGroupsCount = createSelector( + [(s: RootState) => s.notificationGroups.pendingGroups], + (pendingGroups) => + pendingGroups.filter((group) => group.type !== 'gap').length, +); diff --git a/app/javascript/flavours/glitch/selectors/settings.ts b/app/javascript/flavours/glitch/selectors/settings.ts new file mode 100644 index 00000000000000..ce2b8b15e59cb1 --- /dev/null +++ b/app/javascript/flavours/glitch/selectors/settings.ts @@ -0,0 +1,40 @@ +import type { RootState } from 'flavours/glitch/store'; + +/* eslint-disable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */ +// state.settings is not yet typed, so we disable some ESLint checks for those selectors +export const selectSettingsNotificationsShows = (state: RootState) => + state.settings.getIn(['notifications', 'shows']).toJS() as Record< + string, + boolean + >; + +export const selectSettingsNotificationsExcludedTypes = (state: RootState) => + Object.entries(selectSettingsNotificationsShows(state)) + .filter(([_type, enabled]) => !enabled) + .map(([type, _enabled]) => type); + +export const selectSettingsNotificationsQuickFilterShow = (state: RootState) => + state.settings.getIn(['notifications', 'quickFilter', 'show']) as boolean; + +export const selectSettingsNotificationsQuickFilterActive = ( + state: RootState, +) => state.settings.getIn(['notifications', 'quickFilter', 'active']) as string; + +export const selectSettingsNotificationsQuickFilterAdvanced = ( + state: RootState, +) => + state.settings.getIn(['notifications', 'quickFilter', 'advanced']) as boolean; + +export const selectSettingsNotificationsShowUnread = (state: RootState) => + state.settings.getIn(['notifications', 'showUnread']) as boolean; + +export const selectNeedsNotificationPermission = (state: RootState) => + (state.settings.getIn(['notifications', 'alerts']).includes(true) && + state.notifications.get('browserSupport') && + state.notifications.get('browserPermission') === 'default' && + !state.settings.getIn([ + 'notifications', + 'dismissPermissionBanner', + ])) as boolean; + +/* eslint-enable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */ diff --git a/app/javascript/flavours/glitch/styles/components.scss b/app/javascript/flavours/glitch/styles/components.scss index 06225b61e6c254..bcc3655155940e 100644 --- a/app/javascript/flavours/glitch/styles/components.scss +++ b/app/javascript/flavours/glitch/styles/components.scss @@ -1790,10 +1790,19 @@ body > [data-popper-placement] { } } -.status__wrapper-direct { +.status__wrapper-direct, +.notification-ungrouped--direct { background: rgba($ui-highlight-color, 0.05); - .status__prepend { + &:focus { + background: rgba($ui-highlight-color, 0.1); + } +} + +.status__wrapper-direct, +.notification-ungrouped--direct { + .status__prepend, + .notification-ungrouped__header { color: $highlight-text-color; } } @@ -2389,41 +2398,28 @@ a.account__display-name { } } -.notification__relationships-severance-event, -.notification__moderation-warning { - display: flex; - gap: 16px; +.notification-group--link { color: $secondary-text-color; text-decoration: none; - align-items: flex-start; - padding: 16px 32px; - border-bottom: 1px solid var(--background-border-color); - - &:hover { - color: $primary-text-color; - } - - .icon { - padding: 2px; - color: $highlight-text-color; - } - &__content { + .notification-group__main { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; flex-grow: 1; - font-size: 16px; - line-height: 24px; + font-size: 15px; + line-height: 22px; - strong { + strong, + bdi { font-weight: 700; } .link-button { font-size: inherit; line-height: inherit; + font-weight: inherit; } } } @@ -10747,8 +10743,8 @@ noscript { display: flex; align-items: center; border-bottom: 1px solid var(--background-border-color); - padding: 24px 32px; - gap: 16px; + padding: 16px 24px; + gap: 8px; color: $darker-text-color; text-decoration: none; @@ -10758,10 +10754,8 @@ noscript { color: $secondary-text-color; } - .icon { - width: 24px; - height: 24px; - padding: 2px; + .notification-group__icon { + color: inherit; } &__text { @@ -10899,6 +10893,251 @@ noscript { } } +.notification-group { + display: flex; + align-items: flex-start; + gap: 8px; + padding: 10px 14px; // glitch: reduced padding + border-bottom: 1px solid var(--background-border-color); + + &__icon { + width: 40px; + display: flex; + align-items: center; + justify-content: center; + flex: 0 0 auto; + color: $dark-text-color; + + .icon { + width: 28px; + height: 28px; + } + } + + &--follow &__icon, + &--follow-request &__icon { + color: $highlight-text-color; + } + + &--favourite &__icon { + color: $gold-star; + } + + &--reblog &__icon { + color: $valid-value-color; + } + + &--relationships-severance-event &__icon, + &--admin-report &__icon, + &--admin-sign-up &__icon { + color: $dark-text-color; + } + + &--moderation-warning &__icon { + color: $red-bookmark; + } + + &--follow-request &__actions { + align-items: center; + display: flex; + gap: 8px; + + .icon-button { + border: 1px solid var(--background-border-color); + border-radius: 50%; + padding: 1px; + } + } + + &__main { + display: flex; + flex-direction: column; + gap: 8px; + flex: 1 1 auto; + overflow: hidden; + + &__header { + display: flex; + flex-direction: column; + gap: 8px; + + &__wrapper { + display: flex; + justify-content: space-between; + } + + &__label { + display: flex; + gap: 8px; + font-size: 15px; + line-height: 22px; + color: $darker-text-color; + + a { + color: inherit; + text-decoration: none; + } + + bdi { + font-weight: 700; + color: $primary-text-color; + } + + time { + color: $dark-text-color; + } + } + } + + &__status { + border: 1px solid var(--background-border-color); + border-radius: 8px; + padding: 8px; + } + } + + &__avatar-group { + display: flex; + gap: 8px; + height: 28px; + overflow-y: hidden; + flex-wrap: wrap; + } + + .status { + padding: 0; + border: 0; + } + + &__embedded-status { + &__account { + display: flex; + align-items: center; + gap: 4px; + margin-bottom: 8px; + color: $dark-text-color; + + bdi { + color: inherit; + } + } + + .account__avatar { + opacity: 0.5; + } + + &__content { + display: -webkit-box; + font-size: 15px; + line-height: 22px; + color: $dark-text-color; + cursor: pointer; + -webkit-line-clamp: 4; + -webkit-box-orient: vertical; + max-height: 4 * 22px; + overflow: hidden; + + p, + a { + color: inherit; + } + } + } +} + +.notification-ungrouped { + padding: 16px 24px; + border-bottom: 1px solid var(--background-border-color); + + &__header { + display: flex; + align-items: center; + gap: 8px; + color: $dark-text-color; + font-size: 15px; + line-height: 22px; + font-weight: 500; + padding-inline-start: 24px; + margin-bottom: 16px; + + &__icon { + display: flex; + align-items: center; + justify-content: center; + flex: 0 0 auto; + + .icon { + width: 16px; + height: 16px; + } + } + + a { + color: inherit; + text-decoration: none; + } + } + + .status { + border: 0; + padding: 0; + + &__avatar { + width: 40px; + height: 40px; + } + } + + .status__wrapper-direct { + background: transparent; + } + + $icon-margin: 48px; // 40px avatar + 8px gap + + .status__content, + .status__action-bar, + .media-gallery, + .video-player, + .audio-player, + .attachment-list, + .picture-in-picture-placeholder, + .more-from-author, + .status-card, + .hashtag-bar { + margin-inline-start: $icon-margin; + width: calc(100% - $icon-margin); + } + + .more-from-author { + width: calc(100% - $icon-margin + 2px); + } + + .status__content__read-more-button { + margin-inline-start: $icon-margin; + } + + .notification__report { + border: 0; + padding: 0; + } +} + +.notification-group--unread, +.notification-ungrouped--unread { + position: relative; + + &::before { + content: ''; + position: absolute; + top: 0; + inset-inline-start: 0; + width: 100%; + height: 100%; + border-inline-start: 4px solid $highlight-text-color; + pointer-events: none; + } +} + .hover-card-controller[data-popper-reference-hidden='true'] { opacity: 0; pointer-events: none; From 8d8af90ef26d23049fc587695dc11f6f5d69e5ca Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Thu, 18 Jul 2024 23:42:30 -0700 Subject: [PATCH 101/101] mathjax: remove componentWillMount from latexdropdown --- .../compose/components/latex_dropdown.jsx | 22 +++++++++++-------- .../glitch/styles/neuromatchstodon/latex.scss | 1 - .../compose/components/latex_dropdown.jsx | 22 +++++++++++-------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/app/javascript/flavours/glitch/features/compose/components/latex_dropdown.jsx b/app/javascript/flavours/glitch/features/compose/components/latex_dropdown.jsx index a681e26c65bea8..aea07fc07a7281 100644 --- a/app/javascript/flavours/glitch/features/compose/components/latex_dropdown.jsx +++ b/app/javascript/flavours/glitch/features/compose/components/latex_dropdown.jsx @@ -174,6 +174,19 @@ class LaTeXDropdown extends React.PureComponent { placement: 'bottom', }; + constructor(props) { + super(); + + this.props = props; + + const { intl: { formatMessage } } = this.props; + + this.options = [ + { icon: 'inline-mode', value: 'inline', text: formatMessage(messages.inline_short), meta: formatMessage(messages.inline_long) }, + { icon: 'display-mode', value: 'display', text: formatMessage(messages.display_short), meta: formatMessage(messages.display_long) }, + ]; + } + handleToggle = ({ target }) => { if (this.props.isUserTouching && this.props.isUserTouching()) { if (this.state.open) { @@ -237,15 +250,6 @@ class LaTeXDropdown extends React.PureComponent { this.props.onChange(value); }; - componentWillMount () { - const { intl: { formatMessage } } = this.props; - - this.options = [ - { icon: 'inline-mode', value: 'inline', text: formatMessage(messages.inline_short), meta: formatMessage(messages.inline_long) }, - { icon: 'display-mode', value: 'display', text: formatMessage(messages.display_short), meta: formatMessage(messages.display_long) }, - ]; - } - setTargetRef = c => { this.target = c; }; diff --git a/app/javascript/flavours/glitch/styles/neuromatchstodon/latex.scss b/app/javascript/flavours/glitch/styles/neuromatchstodon/latex.scss index 10a59dc1ee17dd..7517509ed07200 100644 --- a/app/javascript/flavours/glitch/styles/neuromatchstodon/latex.scss +++ b/app/javascript/flavours/glitch/styles/neuromatchstodon/latex.scss @@ -1,6 +1,5 @@ .compose-form { .latex-dropdown { - position: absolute; top: 24px; right: 0; } diff --git a/app/javascript/mastodon/features/compose/components/latex_dropdown.jsx b/app/javascript/mastodon/features/compose/components/latex_dropdown.jsx index 61d92cf620ed33..1552349f7e84ed 100644 --- a/app/javascript/mastodon/features/compose/components/latex_dropdown.jsx +++ b/app/javascript/mastodon/features/compose/components/latex_dropdown.jsx @@ -174,6 +174,19 @@ class LaTeXDropdown extends React.PureComponent { placement: 'bottom', }; + constructor(props) { + super(); + + this.props = props; + + const { intl: { formatMessage } } = this.props; + + this.options = [ + { icon: 'inline-mode', value: 'inline', text: formatMessage(messages.inline_short), meta: formatMessage(messages.inline_long) }, + { icon: 'display-mode', value: 'display', text: formatMessage(messages.display_short), meta: formatMessage(messages.display_long) }, + ]; + } + handleToggle = ({ target }) => { if (this.props.isUserTouching && this.props.isUserTouching()) { if (this.state.open) { @@ -237,15 +250,6 @@ class LaTeXDropdown extends React.PureComponent { this.props.onChange(value); }; - componentWillMount () { - const { intl: { formatMessage } } = this.props; - - this.options = [ - { icon: 'inline-mode', value: 'inline', text: formatMessage(messages.inline_short), meta: formatMessage(messages.inline_long) }, - { icon: 'display-mode', value: 'display', text: formatMessage(messages.display_short), meta: formatMessage(messages.display_long) }, - ]; - } - setTargetRef = c => { this.target = c; };