From f3f06dafe34de5cb8562bc203f3c4557821217e5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 10:05:06 +0200 Subject: [PATCH 01/93] Update dependency babel-loader to v8.4.1 (#31931) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index a0c1bb7bae..49b6e241f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5169,17 +5169,17 @@ __metadata: linkType: hard "babel-loader@npm:^8.3.0": - version: 8.3.0 - resolution: "babel-loader@npm:8.3.0" + version: 8.4.1 + resolution: "babel-loader@npm:8.4.1" dependencies: find-cache-dir: "npm:^3.3.1" - loader-utils: "npm:^2.0.0" + loader-utils: "npm:^2.0.4" make-dir: "npm:^3.1.0" schema-utils: "npm:^2.6.5" peerDependencies: "@babel/core": ^7.0.0 webpack: ">=2" - checksum: 10c0/7b83bae35a12fbc5cdf250e2d36a288305fe5b6d20ab044ab7c09bbf456c8895b80af7a4f1e8b64b5c07a4fd48d4b5144dab40b4bc72a4fed532dc000362f38f + checksum: 10c0/efdca9c3ef502af58b923a32123d660c54fd0be125b7b64562c8a43bda0a3a55dac0db32331674104e7e5184061b75c3a0e395b2c5ccdc7cb2125dd9ec7108d2 languageName: node linkType: hard @@ -11421,7 +11421,7 @@ __metadata: languageName: node linkType: hard -"loader-utils@npm:^2.0.0": +"loader-utils@npm:^2.0.0, loader-utils@npm:^2.0.4": version: 2.0.4 resolution: "loader-utils@npm:2.0.4" dependencies: From a397141d78b073246059b36489eb28f27485f9f8 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 18 Sep 2024 04:05:25 -0400 Subject: [PATCH 02/93] Move non-action public method controller callback to private methods (#31933) --- app/controllers/auth/sessions_controller.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index a2fed644fe..ecac4c5ba8 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -20,11 +20,6 @@ class Auth::SessionsController < Devise::SessionsController p.form_action(false) end - def check_suspicious! - user = find_user - @login_is_suspicious = suspicious_sign_in?(user) unless user.nil? - end - def create super do |resource| # We only need to call this if this hasn't already been @@ -101,6 +96,11 @@ class Auth::SessionsController < Devise::SessionsController private + def check_suspicious! + user = find_user + @login_is_suspicious = suspicious_sign_in?(user) unless user.nil? + end + def home_paths(resource) paths = [about_path, '/explore'] From b7548dbf290b3eb9adebeac0ff1b0735ffec2872 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 08:05:59 +0000 Subject: [PATCH 03/93] Update dependency memory_profiler to v1.1.0 (#31947) 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 4a139155f5..12f1b5db2c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -429,7 +429,7 @@ GEM addressable (~> 2.5) azure-storage-blob (~> 2.0.1) hashie (~> 5.0) - memory_profiler (1.0.2) + memory_profiler (1.1.0) mime-types (3.5.2) mime-types-data (~> 3.2015) mime-types-data (3.2024.0820) From bd86c692cf08bb4c5b853f5b843adf0a0853d16d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 08:06:44 +0000 Subject: [PATCH 04/93] New Crowdin Translations (automated) (#31959) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/ar.json | 7 +- app/javascript/mastodon/locales/eo.json | 30 +++++ app/javascript/mastodon/locales/es-MX.json | 144 ++++++++++----------- app/javascript/mastodon/locales/fr-CA.json | 2 +- app/javascript/mastodon/locales/fr.json | 2 +- app/javascript/mastodon/locales/gd.json | 2 +- app/javascript/mastodon/locales/he.json | 1 + app/javascript/mastodon/locales/kab.json | 7 + config/locales/es-MX.yml | 12 +- config/locales/kab.yml | 2 + config/locales/lv.yml | 26 +++- config/locales/simple_form.es-MX.yml | 26 ++-- config/locales/simple_form.kab.yml | 6 +- config/locales/simple_form.lv.yml | 2 +- 14 files changed, 168 insertions(+), 101 deletions(-) diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index 43256506cd..d50ca8dbb5 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -36,6 +36,7 @@ "account.followers.empty": "لا أحدَ يُتابع هذا المُستخدم إلى حد الآن.", "account.followers_counter": "{count, plural, zero{لا مُتابع} one {مُتابعٌ واحِد} two {مُتابعانِ اِثنان} few {{counter} مُتابِعين} many {{counter} مُتابِعًا} other {{counter} مُتابع}}", "account.following": "الاشتراكات", + "account.following_counter": "{count, plural, zero{لا يُتابِع أحدًا} one {يُتابِعُ واحد} two{يُتابِعُ اِثنان} few{يُتابِعُ {counter}} many{يُتابِعُ {counter}} other {يُتابِعُ {counter}}}", "account.follows.empty": "لا يُتابع هذا المُستخدمُ أيَّ أحدٍ حتى الآن.", "account.go_to_profile": "اذهب إلى الملف الشخصي", "account.hide_reblogs": "إخفاء المعاد نشرها مِن @{name}", @@ -309,7 +310,7 @@ "follow_request.authorize": "ترخيص", "follow_request.reject": "رفض", "follow_requests.unlocked_explanation": "حتى وإن كان حسابك غير مقفل، يعتقد فريق {domain} أنك قد ترغب في مراجعة طلبات المتابعة من هذه الحسابات يدوياً.", - "follow_suggestions.curated_suggestion": "اختيار الموظفين", + "follow_suggestions.curated_suggestion": "انتقاه الفريق", "follow_suggestions.dismiss": "لا تُظهرها مجدّدًا", "follow_suggestions.featured_longer": "مختار يدوياً من قِبل فريق {domain}", "follow_suggestions.friends_of_friends_longer": "مشهور بين الأشخاص الذين تتابعهم", @@ -752,7 +753,7 @@ "status.edit": "تعديل", "status.edited": "آخر تعديل يوم {date}", "status.edited_x_times": "عُدّل {count, plural, zero {} one {مرةً واحدة} two {مرّتان} few {{count} مرات} many {{count} مرة} other {{count} مرة}}", - "status.embed": "الحصول على شفرة الإدماج", + "status.embed": "الحصول على شيفرة الدمج", "status.favourite": "فضّل", "status.favourites": "{count, plural, zero {}one {مفضلة واحدة} two {مفضلتان} few {# مفضلات} many {# مفضلات} other {# مفضلات}}", "status.filter": "تصفية هذا المنشور", @@ -773,7 +774,7 @@ "status.reblog": "إعادة النشر", "status.reblog_private": "إعادة النشر إلى الجمهور الأصلي", "status.reblogged_by": "شارَكَه {name}", - "status.reblogs": "{count, plural, one {تعزيز واحد} two {تعزيزتان} few {# تعزيزات} many {# تعزيزات} other {# تعزيزات}}", + "status.reblogs": "{count, plural, one {إعادة نشر واحدة} two {معاد نشرها مرتان} few {# إعادات نشر} many {# إعادات نشر} other {# إعادة نشر}}", "status.reblogs.empty": "لم يقم أي أحد بمشاركة هذا المنشور بعد. عندما يقوم أحدهم بذلك سوف يظهر هنا.", "status.redraft": "إزالة وإعادة الصياغة", "status.remove_bookmark": "احذفه مِن الفواصل المرجعية", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index ce7201dacf..e162e732d0 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -97,6 +97,8 @@ "block_modal.title": "Ĉu bloki uzanton?", "block_modal.you_wont_see_mentions": "Vi ne vidos afiŝojn, ke mencii ilin.", "boost_modal.combo": "Vi povas premi {combo} por preterpasi sekvafoje", + "boost_modal.reblog": "Ĉu diskonigi afiŝon?", + "boost_modal.undo_reblog": "Ĉu ĉesi diskonigi afiŝon?", "bundle_column_error.copy_stacktrace": "Kopii la eraran raporton", "bundle_column_error.error.body": "La petita paĝo ne povas redonitis. Eble estas eraro.", "bundle_column_error.error.title": "Ho, ve!", @@ -188,8 +190,12 @@ "confirmations.redraft.title": "Ĉu forigi kaj redakcii afiŝon?", "confirmations.reply.confirm": "Respondi", "confirmations.reply.message": "Respondi nun anstataŭigos la skribatan afiŝon. Ĉu vi certas, ke vi volas daŭrigi?", + "confirmations.reply.title": "Ĉu superskribi afiŝon?", "confirmations.unfollow.confirm": "Ne plu sekvi", "confirmations.unfollow.message": "Ĉu vi certas, ke vi volas ĉesi sekvi {name}?", + "confirmations.unfollow.title": "Ĉu ĉesi sekvi uzanton?", + "content_warning.hide": "Kaŝi afiŝon", + "content_warning.show": "Montri ĉiukaze", "conversation.delete": "Forigi konversacion", "conversation.mark_as_read": "Marki legita", "conversation.open": "Vidi konversacion", @@ -209,6 +215,8 @@ "dismissable_banner.explore_statuses": "Ĉi tioj estas afiŝoj de socia reto kiu populariĝas hodiau.", "dismissable_banner.explore_tags": "Ĉi tiuj kradvostoj populariĝas en ĉi tiu kaj aliaj serviloj en la malcentraliza reto nun.", "dismissable_banner.public_timeline": "Ĉi tioj estas plej lastaj publikaj afiŝoj de personoj ĉe socia reto kiu personoj ĉe {domain} sekvas.", + "domain_block_modal.they_cant_follow": "Neniu el ĉi tiu servilo povas sekvi vin.", + "domain_pill.username": "Uzantnomo", "embed.instructions": "Enkorpigu ĉi tiun afiŝon en vian retejon per kopio de la suba kodo.", "embed.preview": "Ĝi aperos tiel:", "emoji_button.activity": "Agadoj", @@ -281,6 +289,12 @@ "follow_request.authorize": "Rajtigi", "follow_request.reject": "Rifuzi", "follow_requests.unlocked_explanation": "Kvankam via konto ne estas ŝlosita, la dungitaro de {domain} opinias, ke vi eble volas revizii petojn pri sekvado de ĉi tiuj kontoj permane.", + "follow_suggestions.dismiss": "Ne montri denove", + "follow_suggestions.hints.friends_of_friends": "Ĉi tiu profilo estas populara inter la homoj, kiujn vi sekvas.", + "follow_suggestions.hints.most_followed": "Ĉi tiu profilo estas unu el la plej sekvataj en {domain}.", + "follow_suggestions.popular_suggestion_longer": "Populara en {domain}", + "follow_suggestions.view_all": "Vidi ĉiujn", + "follow_suggestions.who_to_follow": "Kiun sekvi", "followed_tags": "Sekvataj kradvortoj", "footer.about": "Pri", "footer.directory": "Profilujo", @@ -374,6 +388,7 @@ "limited_account_hint.action": "Montru profilon ĉiukaze", "limited_account_hint.title": "La profilo estas kaŝita de la moderigantoj de {domain}.", "link_preview.author": "De {name}", + "link_preview.shares": "{count, plural, one {{counter} afiŝo} other {{counter} afiŝoj}}", "lists.account.add": "Aldoni al la listo", "lists.account.remove": "Forigi de la listo", "lists.delete": "Forigi la liston", @@ -390,8 +405,12 @@ "lists.subheading": "Viaj listoj", "load_pending": "{count,plural, one {# nova elemento} other {# novaj elementoj}}", "loading_indicator.label": "Ŝargado…", + "media_gallery.hide": "Kaŝi", "moved_to_account_banner.text": "Via konto {disabledAccount} estas malvalidigita ĉar vi movis ĝin al {movedToAccount}.", + "mute_modal.show_options": "Montri agordojn", + "mute_modal.they_can_mention_and_follow": "Ili povas mencii kaj sekvi vin, sed vi ne vidos ilin.", "navigation_bar.about": "Pri", + "navigation_bar.administration": "Administrado", "navigation_bar.advanced_interface": "Malfermi altnivelan retpaĝan interfacon", "navigation_bar.blocks": "Blokitaj uzantoj", "navigation_bar.bookmarks": "Legosignoj", @@ -422,10 +441,18 @@ "notification.favourite": "{name} stelumis vian afiŝon", "notification.follow": "{name} eksekvis vin", "notification.follow_request": "{name} petis sekvi vin", + "notification.label.mention": "Mencii", + "notification.label.private_mention": "Privata mencio", + "notification.label.private_reply": "Privata respondo", + "notification.label.reply": "Respondi", + "notification.mention": "Mencii", + "notification.moderation-warning.learn_more": "Lerni pli", "notification.own_poll": "Via enketo finiĝis", "notification.reblog": "{name} diskonigis vian afiŝon", + "notification.relationships_severance_event.learn_more": "Lerni pli", "notification.status": "{name} ĵus afiŝis", "notification.update": "{name} redaktis afiŝon", + "notification_requests.accept": "Akcepti", "notifications.clear": "Forviŝi sciigojn", "notifications.clear_confirmation": "Ĉu vi certas, ke vi volas porĉiame forviŝi ĉiujn viajn sciigojn?", "notifications.column_settings.admin.report": "Novaj raportoj:", @@ -457,6 +484,8 @@ "notifications.permission_denied": "Labortablaj sciigoj ne disponeblas pro peto antaŭe rifuzita de retumiloj", "notifications.permission_denied_alert": "Labortablaj sciigoj ne povas esti ebligitaj, ĉar retumilpermeso antaŭe estis rifuzita", "notifications.permission_required": "Labortablaj sciigoj ne disponeblas ĉar la bezonata permeso ne estis donita.", + "notifications.policy.accept": "Akcepti", + "notifications.policy.filter_new_accounts_title": "Novaj kontoj", "notifications_permission_banner.enable": "Ŝalti retumilajn sciigojn", "notifications_permission_banner.how_to_control": "Por ricevi sciigojn kiam Mastodon ne estas malfermita, ebligu labortablajn sciigojn. Vi povas regi precize kiuj specoj de interagoj generas labortablajn sciigojn per la supra butono {icon} post kiam ili estas ebligitaj.", "notifications_permission_banner.title": "Neniam preterlasas iun ajn", @@ -581,6 +610,7 @@ "report_notification.attached_statuses": "{count, plural, one {{count} afiŝo almetita} other {{count} afiŝoj almetitaj}}", "report_notification.categories.legal": "Laŭleĝa", "report_notification.categories.other": "Alia", + "report_notification.categories.other_sentence": "alia", "report_notification.categories.spam": "Trudmesaĝo", "report_notification.categories.violation": "Malobservo de la regulo", "report_notification.open": "Malfermi la raporton", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index d1abc392bf..7b09ada9f8 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -39,11 +39,11 @@ "account.following_counter": "{count, plural, one {{counter} siguiendo} other {{counter} siguiendo}}", "account.follows.empty": "Este usuario todavía no sigue a nadie.", "account.go_to_profile": "Ir al perfil", - "account.hide_reblogs": "Ocultar retoots de @{name}", + "account.hide_reblogs": "Ocultar impulsos de @{name}", "account.in_memoriam": "En memoria.", "account.joined_short": "Se unió", "account.languages": "Cambiar idiomas suscritos", - "account.link_verified_on": "El proprietario de este link fue comprobado el {date}", + "account.link_verified_on": "El proprietario de este enlace fue comprobado el {date}", "account.locked_info": "El estado de privacidad de esta cuenta està configurado como bloqueado. El proprietario debe revisar manualmente quien puede seguirle.", "account.media": "Multimedia", "account.mention": "Mencionar a @{name}", @@ -61,7 +61,7 @@ "account.requested": "Esperando aprobación. Haga clic para cancelar la solicitud de seguimiento", "account.requested_follow": "{name} ha solicitado seguirte", "account.share": "Compartir el perfil de @{name}", - "account.show_reblogs": "Mostrar retoots de @{name}", + "account.show_reblogs": "Mostrar impulsos de @{name}", "account.statuses_counter": "{count, plural, one {{counter} publicación} other {{counter} publicaciones}}", "account.unblock": "Desbloquear a @{name}", "account.unblock_domain": "Mostrar a {domain}", @@ -70,8 +70,8 @@ "account.unfollow": "Dejar de seguir", "account.unmute": "Dejar de silenciar a @{name}", "account.unmute_notifications_short": "Dejar de silenciar notificaciones", - "account.unmute_short": "Desmutear", - "account_note.placeholder": "Clic para añadir nota", + "account.unmute_short": "Dejar de silenciar", + "account_note.placeholder": "Haz clic para agregar una nota", "admin.dashboard.daily_retention": "Tasa de retención de usuarios por día después de unirse", "admin.dashboard.monthly_retention": "Tasa de retención de usuarios por mes después de unirse", "admin.dashboard.retention.average": "Promedio", @@ -97,7 +97,7 @@ "block_modal.title": "¿Bloquear usuario?", "block_modal.you_wont_see_mentions": "No verás publicaciones que los mencionen.", "boost_modal.combo": "Puedes hacer clic en {combo} para saltar este aviso la próxima vez", - "boost_modal.reblog": "¿Impulsar la publicación?", + "boost_modal.reblog": "¿Deseas impulsar la publicación?", "boost_modal.undo_reblog": "¿Dejar de impulsar la publicación?", "bundle_column_error.copy_stacktrace": "Copiar informe de error", "bundle_column_error.error.body": "La página solicitada no pudo ser renderizada. Podría deberse a un error en nuestro código o a un problema de compatibilidad con el navegador.", @@ -130,7 +130,7 @@ "column.lists": "Listas", "column.mutes": "Usuarios silenciados", "column.notifications": "Notificaciones", - "column.pins": "Toots fijados", + "column.pins": "Publicaciones fijadas", "column.public": "Línea de tiempo federada", "column_back_button.label": "Atrás", "column_header.hide_settings": "Ocultar configuración", @@ -148,10 +148,10 @@ "compose.published.body": "Publicado.", "compose.published.open": "Abrir", "compose.saved.body": "Publicación guardada.", - "compose_form.direct_message_warning_learn_more": "Aprender mas", + "compose_form.direct_message_warning_learn_more": "Saber más", "compose_form.encryption_warning": "Las publicaciones en Mastodon no están cifradas de extremo a extremo. No comparta ninguna información sensible en Mastodon.", - "compose_form.hashtag_warning": "Este toot no será listado bajo ningún hashtag dado que no es público. Solo toots públicos pueden ser buscados por hashtag.", - "compose_form.lock_disclaimer": "Tu cuenta no está bloqueada. Todos pueden seguirte para ver tus toots solo para seguidores.", + "compose_form.hashtag_warning": "Esta publicación no será listada bajo ninguna etiqueta dado que no es pública. Solo publicaciones públicas pueden ser buscadas por etiqueta.", + "compose_form.lock_disclaimer": "Tu cuenta no está {locked}. Todos pueden seguirte para ver tus publicaciones solo para seguidores.", "compose_form.lock_disclaimer.lock": "bloqueado", "compose_form.placeholder": "¿En qué estás pensando?", "compose_form.poll.duration": "Duración de la encuesta", @@ -165,32 +165,32 @@ "compose_form.publish_form": "Publicar", "compose_form.reply": "Respuesta", "compose_form.save_changes": "Actualización", - "compose_form.spoiler.marked": "Texto oculto tras la advertencia", - "compose_form.spoiler.unmarked": "Texto no oculto", + "compose_form.spoiler.marked": "Quitar advertencia de contenido", + "compose_form.spoiler.unmarked": "Añadir advertencia de contenido", "compose_form.spoiler_placeholder": "Advertencia de contenido (opcional)", "confirmation_modal.cancel": "Cancelar", "confirmations.block.confirm": "Bloquear", "confirmations.delete.confirm": "Eliminar", - "confirmations.delete.message": "¿Estás seguro de que quieres borrar este toot?", + "confirmations.delete.message": "¿Estás seguro de que quieres borrar esta publicación?", "confirmations.delete.title": "¿Eliminar publicación?", "confirmations.delete_list.confirm": "Eliminar", "confirmations.delete_list.message": "¿Seguro que quieres borrar esta lista permanentemente?", - "confirmations.delete_list.title": "¿Eliminar lista?", + "confirmations.delete_list.title": "¿Deseas eliminar la lista?", "confirmations.discard_edit_media.confirm": "Descartar", "confirmations.discard_edit_media.message": "Tienes cambios sin guardar en la descripción o vista previa del archivo, ¿deseas descartarlos de cualquier manera?", "confirmations.edit.confirm": "Editar", "confirmations.edit.message": "Editar sobrescribirá el mensaje que estás escribiendo. ¿Estás seguro de que deseas continuar?", - "confirmations.edit.title": "¿Sobrescribir publicación?", + "confirmations.edit.title": "¿Sobreescribir publicación?", "confirmations.logout.confirm": "Cerrar sesión", - "confirmations.logout.message": "¿Estás seguro de querer cerrar la sesión?", - "confirmations.logout.title": "¿Cerrar sesión?", + "confirmations.logout.message": "¿Estás seguro de que quieres cerrar la sesión?", + "confirmations.logout.title": "¿Deseas cerrar sesión?", "confirmations.mute.confirm": "Silenciar", "confirmations.redraft.confirm": "Borrar y volver a borrador", "confirmations.redraft.message": "¿Estás seguro que quieres borrar esta publicación y editarla? Los favoritos e impulsos se perderán, y las respuestas a la publicación original quedarán separadas.", "confirmations.redraft.title": "¿Borrar y volver a redactar la publicación?", "confirmations.reply.confirm": "Responder", "confirmations.reply.message": "Responder sobrescribirá el mensaje que estás escribiendo. ¿Estás seguro de que deseas continuar?", - "confirmations.reply.title": "¿Sobrescribir publicación?", + "confirmations.reply.title": "¿Sobreescribir publicación?", "confirmations.unfollow.confirm": "Dejar de seguir", "confirmations.unfollow.message": "¿Estás seguro de que quieres dejar de seguir a {name}?", "confirmations.unfollow.title": "¿Dejar de seguir al usuario?", @@ -213,8 +213,8 @@ "dismissable_banner.dismiss": "Descartar", "dismissable_banner.explore_links": "Estas noticias están siendo discutidas por personas en este y otros servidores de la red descentralizada en este momento.", "dismissable_banner.explore_statuses": "Estas son las publicaciones que están en tendencia en la red ahora. Las publicaciones recientes con más impulsos y favoritos se muestran más arriba.", - "dismissable_banner.explore_tags": "Se trata de hashtags que están ganando adeptos en las redes sociales hoy en día. Los hashtags que son utilizados por más personas diferentes se clasifican mejor.", - "dismissable_banner.public_timeline": "Estos son los toots públicos más recientes de personas en la web social a las que sigue la gente en {domain}.", + "dismissable_banner.explore_tags": "Se trata de etiquetas que están ganando adeptos en las redes sociales hoy en día. Las etiquetas que son utilizadas por más personas diferentes se clasifican mejor.", + "dismissable_banner.public_timeline": "Estas son las publicaciones públicas más recientes de personas en la web social a las que sigue la gente en {domain}.", "domain_block_modal.block": "Bloquear servidor", "domain_block_modal.block_account_instead": "Bloquear @{name} en su lugar", "domain_block_modal.they_can_interact_with_old_posts": "Las personas de este servidor pueden interactuar con tus publicaciones antiguas.", @@ -236,7 +236,7 @@ "domain_pill.your_handle": "Tu alias:", "domain_pill.your_server": "Tu hogar digital, donde residen todas tus publicaciones. ¿No te gusta este sitio? Muévete a otro servidor en cualquier momento y llévate a tus seguidores.", "domain_pill.your_username": "Tu identificador único en este servidor. Es posible encontrar usuarios con el mismo nombre de usuario en diferentes servidores.", - "embed.instructions": "Añade este toot a tu sitio web con el siguiente código.", + "embed.instructions": "Añade esta publicación a tu sitio web con el siguiente código.", "embed.preview": "Así es como se verá:", "emoji_button.activity": "Actividad", "emoji_button.clear": "Borrar", @@ -249,16 +249,16 @@ "emoji_button.objects": "Objetos", "emoji_button.people": "Gente", "emoji_button.recent": "Usados frecuentemente", - "emoji_button.search": "Buscar…", + "emoji_button.search": "Buscar...", "emoji_button.search_results": "Resultados de búsqueda", "emoji_button.symbols": "Símbolos", "emoji_button.travel": "Viajes y lugares", "empty_column.account_hides_collections": "Este usuario ha elegido no hacer disponible esta información", "empty_column.account_suspended": "Cuenta suspendida", - "empty_column.account_timeline": "¡No hay toots aquí!", + "empty_column.account_timeline": "¡No hay publicaciones aquí!", "empty_column.account_unavailable": "Perfil no disponible", "empty_column.blocks": "Aún no has bloqueado a ningún usuario.", - "empty_column.bookmarked_statuses": "Aún no tienes ningún toot guardado como marcador. Cuando guardes uno, se mostrará aquí.", + "empty_column.bookmarked_statuses": "Aún no tienes ninguna publicación guardada como marcador. Cuando guardes una, se mostrará aquí.", "empty_column.community": "La línea de tiempo local está vacía. ¡Escribe algo para empezar la fiesta!", "empty_column.direct": "Aún no tienes menciones privadas. Cuando envíes o recibas una, aparecerán aquí.", "empty_column.domain_blocks": "Todavía no hay dominios ocultos.", @@ -266,8 +266,8 @@ "empty_column.favourited_statuses": "Todavía no tienes publicaciones favoritas. Cuando le des favorito a una publicación se mostrarán acá.", "empty_column.favourites": "Todavía nadie marcó como favorito esta publicación. Cuando alguien lo haga, se mostrará aquí.", "empty_column.follow_requests": "No tienes ninguna petición de seguidor. Cuando recibas una, se mostrará aquí.", - "empty_column.followed_tags": "No estás siguiendo ningún hashtag todavía. Cuando lo hagas, aparecerá aquí.", - "empty_column.hashtag": "No hay nada en este hashtag aún.", + "empty_column.followed_tags": "No estás siguiendo ninguna etiqueta todavía. Cuando lo hagas, aparecerá aquí.", + "empty_column.hashtag": "No hay nada en esta etiqueta aún.", "empty_column.home": "No estás siguiendo a nadie aún. Visita {public} o haz búsquedas para empezar y conocer gente nueva.", "empty_column.list": "No hay nada en esta lista aún. Cuando miembros de esta lista publiquen nuevos estatus, estos aparecerán qui.", "empty_column.lists": "No tienes ninguna lista. cuando crees una, se mostrará aquí.", @@ -304,7 +304,7 @@ "filter_modal.select_filter.title": "Filtrar esta publicación", "filter_modal.title.status": "Filtrar una publicación", "filter_warning.matches_filter": "Coincide con el filtro “{title}”", - "filtered_notifications_banner.pending_requests": "De {count, plural, =0 {nadie} one {una persona} other {# personas}} que puede que conozcas", + "filtered_notifications_banner.pending_requests": "De {count, plural, =0 {nadie} one {una persona} other {# people}} que puede que tú conozcas", "filtered_notifications_banner.title": "Notificaciones filtradas", "firehose.all": "Todas", "firehose.local": "Este servidor", @@ -315,7 +315,7 @@ "follow_suggestions.curated_suggestion": "Recomendaciones del equipo", "follow_suggestions.dismiss": "No mostrar de nuevo", "follow_suggestions.featured_longer": "Escogidos por el equipo de {domain}", - "follow_suggestions.friends_of_friends_longer": "Populares entre las personas a las que sigues", + "follow_suggestions.friends_of_friends_longer": "Popular entre las personas a las que sigues", "follow_suggestions.hints.featured": "Este perfil ha sido seleccionado a mano por el equipo de {domain}.", "follow_suggestions.hints.friends_of_friends": "Este perfil es popular entre las personas que sigues.", "follow_suggestions.hints.most_followed": "Este perfil es uno de los más seguidos en {domain}.", @@ -323,11 +323,11 @@ "follow_suggestions.hints.similar_to_recently_followed": "Este perfil es similar a los perfiles que has seguido recientemente.", "follow_suggestions.personalized_suggestion": "Sugerencia personalizada", "follow_suggestions.popular_suggestion": "Sugerencia popular", - "follow_suggestions.popular_suggestion_longer": "Populares en {domain}", + "follow_suggestions.popular_suggestion_longer": "Popular en {domain}", "follow_suggestions.similar_to_recently_followed_longer": "Similares a los perfiles que has seguido recientemente", "follow_suggestions.view_all": "Ver todo", "follow_suggestions.who_to_follow": "Recomendamos seguir", - "followed_tags": "Hashtags seguidos", + "followed_tags": "Etiquetas seguidas", "footer.about": "Acerca de", "footer.directory": "Directorio de perfiles", "footer.get_app": "Obtener la aplicación", @@ -344,8 +344,8 @@ "hashtag.column_settings.select.no_options_message": "No se encontraron sugerencias", "hashtag.column_settings.select.placeholder": "Introducir etiquetas…", "hashtag.column_settings.tag_mode.all": "Todos estos", - "hashtag.column_settings.tag_mode.any": "Cualquiera de estos", - "hashtag.column_settings.tag_mode.none": "Ninguno de estos", + "hashtag.column_settings.tag_mode.any": "Cualquiera de estas", + "hashtag.column_settings.tag_mode.none": "Ninguna de estas", "hashtag.column_settings.tag_toggle": "Incluye etiquetas adicionales para esta columna", "hashtag.counter_by_accounts": "{count, plural, one {{counter} participante} other {{counter} participantes}}", "hashtag.counter_by_uses": "{count, plural, one {{counter} publicación} other {{counter} publicaciones}}", @@ -361,7 +361,7 @@ "hints.profiles.see_more_posts": "Ver más publicaciones en {domain}", "hints.threads.replies_may_be_missing": "Puede que no se muestren algunas respuestas de otros servidores.", "hints.threads.see_more": "Ver más respuestas en {domain}", - "home.column_settings.show_reblogs": "Mostrar retoots", + "home.column_settings.show_reblogs": "Mostrar impulsos", "home.column_settings.show_replies": "Mostrar respuestas", "home.hide_announcements": "Ocultar anuncios", "home.pending_critical_update.body": "¡Por favor actualiza tu servidor Mastodon lo antes posible!", @@ -369,7 +369,7 @@ "home.pending_critical_update.title": "¡Actualización de seguridad crítica disponible!", "home.show_announcements": "Mostrar anuncios", "ignore_notifications_modal.disclaimer": "Mastodon no puede informar a los usuarios que has ignorado sus notificaciones. Ignorar notificaciones no impedirá que se sigan enviando los mensajes.", - "ignore_notifications_modal.filter_instead": "Filtrar en vez de ignorar", + "ignore_notifications_modal.filter_instead": "Filtrar en su lugar", "ignore_notifications_modal.filter_to_act_users": "Aún podrás aceptar, rechazar o reportar usuarios", "ignore_notifications_modal.filter_to_avoid_confusion": "Filtrar ayuda a evitar confusiones potenciales", "ignore_notifications_modal.filter_to_review_separately": "Puedes revisar las notificaciones filtradas por separado", @@ -399,13 +399,13 @@ "intervals.full.minutes": "{number, plural, one {# minuto} other {# minutos}}", "keyboard_shortcuts.back": "volver atrás", "keyboard_shortcuts.blocked": "abrir una lista de usuarios bloqueados", - "keyboard_shortcuts.boost": "retootear", + "keyboard_shortcuts.boost": "Impulsar publicación", "keyboard_shortcuts.column": "enfocar un estado en una de las columnas", "keyboard_shortcuts.compose": "enfocar el área de texto de redacción", "keyboard_shortcuts.description": "Descripción", "keyboard_shortcuts.direct": "para abrir la columna de menciones privadas", "keyboard_shortcuts.down": "mover hacia abajo en la lista", - "keyboard_shortcuts.enter": "abrir estado", + "keyboard_shortcuts.enter": "Abrir publicación", "keyboard_shortcuts.favourite": "Marcar como favorita la publicación", "keyboard_shortcuts.favourites": "Abrir lista de favoritos", "keyboard_shortcuts.federated": "abrir el timeline federado", @@ -419,16 +419,16 @@ "keyboard_shortcuts.my_profile": "abrir tu perfil", "keyboard_shortcuts.notifications": "abrir la columna de notificaciones", "keyboard_shortcuts.open_media": "para abrir archivos multimedia", - "keyboard_shortcuts.pinned": "abrir la lista de toots destacados", + "keyboard_shortcuts.pinned": "Abrir la lista de publicaciones fijadas", "keyboard_shortcuts.profile": "abrir el perfil del autor", - "keyboard_shortcuts.reply": "para responder", + "keyboard_shortcuts.reply": "Responder a la publicación", "keyboard_shortcuts.requests": "abrir la lista de peticiones de seguidores", "keyboard_shortcuts.search": "para poner el foco en la búsqueda", "keyboard_shortcuts.spoilers": "para mostrar/ocultar el campo CW", "keyboard_shortcuts.start": "abrir la columna \"comenzar\"", "keyboard_shortcuts.toggle_hidden": "mostrar/ocultar texto tras aviso de contenido (CW)", "keyboard_shortcuts.toggle_sensitivity": "mostrar/ocultar medios", - "keyboard_shortcuts.toot": "para comenzar un nuevo toot", + "keyboard_shortcuts.toot": "Comenzar una nueva publicación", "keyboard_shortcuts.unfocus": "para retirar el foco de la caja de redacción/búsqueda", "keyboard_shortcuts.up": "para ir hacia arriba en la lista", "lightbox.close": "Cerrar", @@ -474,7 +474,7 @@ "navigation_bar.blocks": "Usuarios bloqueados", "navigation_bar.bookmarks": "Marcadores", "navigation_bar.community_timeline": "Historia local", - "navigation_bar.compose": "Escribir un nuevo toot", + "navigation_bar.compose": "Redactar una nueva publicación", "navigation_bar.direct": "Menciones privadas", "navigation_bar.discover": "Descubrir", "navigation_bar.domain_blocks": "Dominios ocultos", @@ -482,7 +482,7 @@ "navigation_bar.favourites": "Favoritos", "navigation_bar.filters": "Palabras silenciadas", "navigation_bar.follow_requests": "Solicitudes para seguirte", - "navigation_bar.followed_tags": "Hashtags seguidos", + "navigation_bar.followed_tags": "Etiquetas seguidas", "navigation_bar.follows_and_followers": "Siguiendo y seguidores", "navigation_bar.lists": "Listas", "navigation_bar.logout": "Cerrar sesión", @@ -490,25 +490,25 @@ "navigation_bar.mutes": "Usuarios silenciados", "navigation_bar.opened_in_classic_interface": "Publicaciones, cuentas y otras páginas específicas se abren por defecto en la interfaz web clásica.", "navigation_bar.personal": "Personal", - "navigation_bar.pins": "Toots fijados", + "navigation_bar.pins": "Publicaciones fijadas", "navigation_bar.preferences": "Preferencias", "navigation_bar.public_timeline": "Historia federada", "navigation_bar.search": "Buscar", "navigation_bar.security": "Seguridad", "not_signed_in_indicator.not_signed_in": "Necesitas iniciar sesión para acceder a este recurso.", "notification.admin.report": "{name} denunció a {target}", - "notification.admin.report_account": "{name} informó de {count, plural, one {una publicación} other {# publicaciones}} de {target} por {category}", - "notification.admin.report_account_other": "{name} informó de {count, plural, one {una publicación} other {# publicaciones}} de {target}", - "notification.admin.report_statuses": "{name} informó de {target} por {category}", - "notification.admin.report_statuses_other": "{name} informó de {target}", + "notification.admin.report_account": "{name} reportó {count, plural, one {una publicación} other {# publicaciones}} de {target} por {category}", + "notification.admin.report_account_other": "{name} reportó {count, plural, one {una publicación} other {# publicaciones}} de {target}", + "notification.admin.report_statuses": "{name} reportó {target} por {category}", + "notification.admin.report_statuses_other": "{name} reportó {target}", "notification.admin.sign_up": "{name} se unio", - "notification.admin.sign_up.name_and_others": "{name} y {count, plural, one {# más} other {# más}} se registraron", + "notification.admin.sign_up.name_and_others": "{name} y {count, plural, one {# otro} other {# otros}} se registraron", "notification.favourite": "{name} marcó como favorita tu publicación", - "notification.favourite.name_and_others_with_link": "{name} y {count, plural, one {# más} other {# más}} marcaron tu publicación como favorita", + "notification.favourite.name_and_others_with_link": "{name} y {count, plural, one {# otro} other {# otros}} marcaron tu publicación como favorita", "notification.follow": "{name} te empezó a seguir", - "notification.follow.name_and_others": "{name} y {count, plural, one {# más} other {# más}} te siguieron", + "notification.follow.name_and_others": "{name} y {count, plural, one {# otro} other {# otros}} te siguieron", "notification.follow_request": "{name} ha solicitado seguirte", - "notification.follow_request.name_and_others": "{name} y {count, plural, one {# más} other {# más}} han solicitado seguirte", + "notification.follow_request.name_and_others": "{name} y {count, plural, one {# otro} other {# otros}} han solicitado seguirte", "notification.label.mention": "Mención", "notification.label.private_mention": "Mención privada", "notification.label.private_reply": "Respuesta privada", @@ -519,14 +519,14 @@ "notification.moderation_warning.action_delete_statuses": "Se han eliminado algunas de tus publicaciones.", "notification.moderation_warning.action_disable": "Tu cuenta ha sido desactivada.", "notification.moderation_warning.action_mark_statuses_as_sensitive": "Se han marcado como sensibles algunas de tus publicaciones.", - "notification.moderation_warning.action_none": "Tu cuenta ha recibido un aviso de moderación.", + "notification.moderation_warning.action_none": "Tu cuenta ha recibido una advertencia de moderación.", "notification.moderation_warning.action_sensitive": "De ahora en adelante, todas tus publicaciones se marcarán como sensibles.", "notification.moderation_warning.action_silence": "Tu cuenta ha sido limitada.", "notification.moderation_warning.action_suspend": "Tu cuenta ha sido suspendida.", "notification.own_poll": "Tu encuesta ha terminado", - "notification.poll": "Una encuesta ha terminado", - "notification.reblog": "{name} ha retooteado tu estado", - "notification.reblog.name_and_others_with_link": "{name} y {count, plural, one {# más} other {# más}} impulsaron tu publicación", + "notification.poll": "Una encuesta en la que has votado ha terminado", + "notification.reblog": "{name} ha impulsado tu publicación", + "notification.reblog.name_and_others_with_link": "{name} y {count, plural, one {# otro} other {# otros}} impulsaron tu publicación", "notification.relationships_severance_event": "Conexiones perdidas con {name}", "notification.relationships_severance_event.account_suspension": "Un administrador de {from} ha suspendido {target}, lo que significa que ya no puedes recibir actualizaciones de sus cuentas o interactuar con ellas.", "notification.relationships_severance_event.domain_block": "Un administrador de {from} ha bloqueado {target}, incluyendo {followersCount} de tus seguidores y {followingCount, plural, one {# cuenta} other {# cuentas}} que sigues.", @@ -536,7 +536,7 @@ "notification.update": "{name} editó una publicación", "notification_requests.accept": "Aceptar", "notification_requests.accept_multiple": "{count, plural, one {Aceptar # solicitud…} other {Aceptar # solicitudes…}}", - "notification_requests.confirm_accept_multiple.button": "{count, plural, one {Aceptar solicitud} other {Aceptar solicitudes}}", + "notification_requests.confirm_accept_multiple.button": "{count, plural, one {Solicitud aceptada} other {Solicitudes aceptadas}}", "notification_requests.confirm_accept_multiple.message": "Vas a aceptar {count, plural, one {una solicitud} other {# solicitudes}}. ¿Quieres continuar?", "notification_requests.confirm_accept_multiple.title": "¿Aceptar las solicitudes?", "notification_requests.confirm_dismiss_multiple.button": "{count, plural, one {Descartar solicitud} other {Descartar solicitudes}}", @@ -546,8 +546,8 @@ "notification_requests.dismiss_multiple": "{count, plural, one {Descartar # solicitud…} other {Descartar # solicitudes…}}", "notification_requests.edit_selection": "Editar", "notification_requests.exit_selection": "Hecho", - "notification_requests.explainer_for_limited_account": "Las notificaciones de esta cuenta han sido filtradas porque la cuenta ha sido limitada por un moderador.", - "notification_requests.explainer_for_limited_remote_account": "Las notificaciones de esta cuenta han sido filtradas porque la cuenta o su servidor ha sido limitada por un moderador.", + "notification_requests.explainer_for_limited_account": "Las notificaciones de esta cuenta han sido filtradas, ya que la cuenta ha sido limitada por un moderador.", + "notification_requests.explainer_for_limited_remote_account": "Las notificaciones de esta cuenta han sido filtradas, ya que la cuenta o su servidor ha sido limitada por un moderador.", "notification_requests.maximize": "Maximizar", "notification_requests.minimize_banner": "Minimizar banner de notificaciones filtradas", "notification_requests.notifications_from": "Notificaciones de {name}", @@ -555,7 +555,7 @@ "notification_requests.view": "Ver notificaciones", "notifications.clear": "Limpiar notificaciones", "notifications.clear_confirmation": "¿Seguro de querer borrar permanentemente todas tus notificaciones?", - "notifications.clear_title": "¿Borrar notificaciones?", + "notifications.clear_title": "¿Limpiar notificaciones?", "notifications.column_settings.admin.report": "Nuevas denuncias:", "notifications.column_settings.admin.sign_up": "Registros nuevos:", "notifications.column_settings.alert": "Notificaciones de escritorio", @@ -567,7 +567,7 @@ "notifications.column_settings.mention": "Menciones:", "notifications.column_settings.poll": "Resultados de la votación:", "notifications.column_settings.push": "Notificaciones push", - "notifications.column_settings.reblog": "Retoots:", + "notifications.column_settings.reblog": "Impulsos:", "notifications.column_settings.show": "Mostrar en columna", "notifications.column_settings.sound": "Reproducir sonido", "notifications.column_settings.status": "Nuevas publicaciones:", @@ -575,7 +575,7 @@ "notifications.column_settings.unread_notifications.highlight": "Destacar notificaciones no leídas", "notifications.column_settings.update": "Ediciones:", "notifications.filter.all": "Todos", - "notifications.filter.boosts": "Retoots", + "notifications.filter.boosts": "Impulsos", "notifications.filter.favourites": "Favoritos", "notifications.filter.follows": "Seguidores", "notifications.filter.mentions": "Menciones", @@ -621,7 +621,7 @@ "onboarding.profile.display_name_hint": "Tu nombre completo o tu apodo…", "onboarding.profile.lead": "Siempre puedes completar esto más tarde en los ajustes, donde hay aún más opciones de personalización disponibles.", "onboarding.profile.note": "Biografía", - "onboarding.profile.note_hint": "Puedes @mencionar a otras personas o #hashtags…", + "onboarding.profile.note_hint": "Puedes @mencionar a otras personas o #etiquetas…", "onboarding.profile.save_and_continue": "Guardar y continuar", "onboarding.profile.title": "Configuración del perfil", "onboarding.profile.upload_avatar": "Subir foto de perfil", @@ -639,7 +639,7 @@ "onboarding.steps.publish_status.title": "Escribe tu primera publicación", "onboarding.steps.setup_profile.body": "Si rellenas tu perfil tendrás más posibilidades de que otros interactúen contigo.", "onboarding.steps.setup_profile.title": "Personaliza tu perfil", - "onboarding.steps.share_profile.body": "¡Dile a tus amigos cómo encontrarte en Mastodon!", + "onboarding.steps.share_profile.body": "Dile a tus amigos cómo encontrarte en Mastodon", "onboarding.steps.share_profile.title": "Comparte tu perfil", "onboarding.tips.2fa": "¿Sabías que? Puedes proteger tu cuenta configurando la autenticación de dos factores en la configuración de su cuenta. Funciona con cualquier aplicación TOTP de su elección, ¡no necesitas número de teléfono!", "onboarding.tips.accounts_from_other_servers": "¿Sabías que? Como Mastodon es descentralizado, algunos perfiles que encuentras están alojados en servidores distintos del tuyo. Y sin embargo, ¡puedes interactuar con ellos! ¡Su servidor corresponde a la segunda mitad de su nombre de usuario!", @@ -665,7 +665,7 @@ "privacy.private.short": "Seguidores", "privacy.public.long": "Cualquiera dentro y fuera de Mastodon", "privacy.public.short": "Público", - "privacy.unlisted.additional": "Esto se comporta exactamente igual que el público, excepto que el post no aparecerá en las cronologías en directo o en los hashtags, la exploración o busquedas en Mastodon, incluso si está optado por activar la cuenta de usuario.", + "privacy.unlisted.additional": "Esto se comporta exactamente igual que el público, excepto que el post no aparecerá en las cronologías en directo o en las etiquetas, la exploración o busquedas en Mastodon, incluso si está optado por activar la cuenta de usuario.", "privacy.unlisted.long": "Menos fanfares algorítmicos", "privacy.unlisted.short": "Público silencioso", "privacy_policy.last_updated": "Actualizado por última vez {date}", @@ -699,7 +699,7 @@ "report.category.title_account": "perfil", "report.category.title_status": "publicación", "report.close": "Realizado", - "report.comment.title": "¿Hay algo más que usted cree que debamos saber?", + "report.comment.title": "¿Hay algo más que creas que deberíamos saber?", "report.forward": "Reenviar a {target}", "report.forward_hint": "Esta cuenta es de otro servidor. ¿Enviar una copia anonimizada del informe allí también?", "report.mute": "Silenciar", @@ -776,9 +776,9 @@ "status.admin_status": "Abrir este estado en la interfaz de moderación", "status.block": "Bloquear a @{name}", "status.bookmark": "Añadir marcador", - "status.cancel_reblog_private": "Eliminar retoot", - "status.cannot_reblog": "Este toot no puede retootearse", - "status.continued_thread": "Continuó el hilo", + "status.cancel_reblog_private": "Deshacer impulso", + "status.cannot_reblog": "Esta publicación no puede ser impulsada", + "status.continued_thread": "Hilo continuado", "status.copy": "Copiar enlace al estado", "status.delete": "Borrar", "status.detailed_status": "Vista de conversación detallada", @@ -803,16 +803,16 @@ "status.mute_conversation": "Silenciar conversación", "status.open": "Expandir estado", "status.pin": "Fijar", - "status.pinned": "Toot fijado", + "status.pinned": "Publicación fijada", "status.read_more": "Leer más", - "status.reblog": "Retootear", + "status.reblog": "Impulsar", "status.reblog_private": "Implusar a la audiencia original", - "status.reblogged_by": "Retooteado por {name}", + "status.reblogged_by": "Impulsado por {name}", "status.reblogs": "{count, plural, one {impulso} other {impulsos}}", - "status.reblogs.empty": "Nadie retooteó este toot todavía. Cuando alguien lo haga, aparecerá aquí.", + "status.reblogs.empty": "Nadie impulsó esta publicación todavía. Cuando alguien lo haga, aparecerá aquí.", "status.redraft": "Borrar y volver a borrador", "status.remove_bookmark": "Eliminar marcador", - "status.replied_in_thread": "Respondió en el hilo", + "status.replied_in_thread": "Respondido en el hilo", "status.replied_to": "Respondió a {name}", "status.reply": "Responder", "status.replyAll": "Responder al hilo", diff --git a/app/javascript/mastodon/locales/fr-CA.json b/app/javascript/mastodon/locales/fr-CA.json index e89256bc2f..8e33c68442 100644 --- a/app/javascript/mastodon/locales/fr-CA.json +++ b/app/javascript/mastodon/locales/fr-CA.json @@ -312,7 +312,7 @@ "follow_request.authorize": "Autoriser", "follow_request.reject": "Rejeter", "follow_requests.unlocked_explanation": "Même si votre compte n’est pas privé, l’équipe de {domain} a pensé que vous pourriez vouloir peut-être consulter manuellement les demandes d'abonnement de ces comptes.", - "follow_suggestions.curated_suggestion": "Choix du staff", + "follow_suggestions.curated_suggestion": "Sélectionné par l'équipe", "follow_suggestions.dismiss": "Ne plus afficher", "follow_suggestions.featured_longer": "Sélectionné par l'équipe de {domain}", "follow_suggestions.friends_of_friends_longer": "Populaire dans le cercle des personnes que vous suivez", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 03cb6471d6..e73ddf734f 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -312,7 +312,7 @@ "follow_request.authorize": "Accepter", "follow_request.reject": "Rejeter", "follow_requests.unlocked_explanation": "Même si votre compte n’est pas privé, l’équipe de {domain} a pensé que vous pourriez vouloir consulter manuellement les demandes de suivi de ces comptes.", - "follow_suggestions.curated_suggestion": "Choix du staff", + "follow_suggestions.curated_suggestion": "Sélectionné par l'équipe", "follow_suggestions.dismiss": "Ne plus afficher", "follow_suggestions.featured_longer": "Sélectionné par l'équipe de {domain}", "follow_suggestions.friends_of_friends_longer": "Populaire dans le cercle des personnes que vous suivez", diff --git a/app/javascript/mastodon/locales/gd.json b/app/javascript/mastodon/locales/gd.json index 97748e0ee9..2b93662694 100644 --- a/app/javascript/mastodon/locales/gd.json +++ b/app/javascript/mastodon/locales/gd.json @@ -36,7 +36,7 @@ "account.followers.empty": "Chan eil neach sam bith a’ leantainn air a’ chleachdaiche seo fhathast.", "account.followers_counter": "{count, plural, one {{counter} neach-leantainn} other {{counter} luchd-leantainn}}", "account.following": "A’ leantainn", - "account.following_counter": "{count, plural, one {Tha {counter} a’ leantainn} other {Tha {counter} a’ leantainn}}", + "account.following_counter": "{count, plural, one {A’ leantainn {counter}} other {A’ leantainn {counter}}}", "account.follows.empty": "Chan eil an cleachdaiche seo a’ leantainn neach sam bith fhathast.", "account.go_to_profile": "Tadhail air a’ phròifil", "account.hide_reblogs": "Falaich na brosnachaidhean o @{name}", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index de20d505c0..5e6ab56c23 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -787,6 +787,7 @@ "status.edit": "עריכה", "status.edited": "נערך לאחרונה {date}", "status.edited_x_times": "נערך {count, plural, one {פעם {count}} other {{count} פעמים}}", + "status.embed": "העתקת קוד להטמעה", "status.favourite": "חיבוב", "status.favourites": "{count, plural, one {חיבוב אחד} two {זוג חיבובים} other {# חיבובים}}", "status.filter": "סנן הודעה זו", diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json index 24286a119f..822fb59e6f 100644 --- a/app/javascript/mastodon/locales/kab.json +++ b/app/javascript/mastodon/locales/kab.json @@ -2,12 +2,14 @@ "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.no_reason_available": "Ulac taɣẓint", "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", + "account.account_note_header": "Tamawt tudmawant", "account.add_or_remove_from_list": "Rnu neɣ kkes seg tebdarin", "account.badges.bot": "Aṛubut", "account.badges.group": "Agraw", @@ -46,6 +48,7 @@ "account.mute_notifications_short": "Susem alɣuten", "account.mute_short": "Sgugem", "account.muted": "Yettwasgugem", + "account.mutual": "Temṭafarem", "account.no_bio": "Ulac aglam i d-yettunefken.", "account.open_original_page": "Ldi asebter anasli", "account.posts": "Tisuffaɣ", @@ -62,6 +65,7 @@ "account.unendorse": "Ur ttwellih ara fell-as deg umaɣnu-inek", "account.unfollow": "Ur ṭṭafaṛ ara", "account.unmute": "Kkes asgugem ɣef @{name}", + "account.unmute_notifications_short": "Serreḥ i yilɣa", "account.unmute_short": "Kkes asgugem", "account_note.placeholder": "Ulac iwenniten", "admin.dashboard.retention.cohort_size": "Iseqdacen imaynuten", @@ -152,6 +156,7 @@ "confirmations.edit.message": "Abeddel tura ad d-yaru izen-nni i d-tegreḍ akka tura. Tetḥeqqeḍ tebɣiḍ ad tkemmleḍ?", "confirmations.logout.confirm": "Ffeɣ", "confirmations.logout.message": "D tidet tebɣiḍ ad teffɣeḍ?", + "confirmations.logout.title": "Tebɣiḍ ad teffɣeḍ ssya?", "confirmations.mute.confirm": "Sgugem", "confirmations.redraft.confirm": "Kkes sakin ɛiwed tira", "confirmations.reply.confirm": "Err", @@ -351,6 +356,7 @@ "lists.subheading": "Tibdarin-ik·im", "load_pending": "{count, plural, one {# n uferdis amaynut} other {# n yiferdisen imaynuten}}", "loading_indicator.label": "Yessalay-d …", + "media_gallery.hide": "Ffer-it", "mute_modal.hide_from_notifications": "Ffer-it deg ulɣuten", "mute_modal.hide_options": "Ffer tinefrunin", "mute_modal.indefinite": "Alamma ssnesreɣ asgugem fell-as", @@ -405,6 +411,7 @@ "notification.status": "{name} akken i d-yessufeɣ", "notification_requests.accept": "Qbel", "notification_requests.dismiss": "Agi", + "notification_requests.edit_selection": "Ẓreg", "notification_requests.exit_selection": "Immed", "notification_requests.notifications_from": "Alɣuten sɣur {name}", "notifications.clear": "Sfeḍ alɣuten", diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml index ebe26c3f14..1ea346a00c 100644 --- a/config/locales/es-MX.yml +++ b/config/locales/es-MX.yml @@ -175,7 +175,7 @@ es-MX: approve_appeal: Aprobar apelación approve_user: Aprobar Usuario assigned_to_self_report: Asignar Reporte - change_email_user: Cambiar Correo Electrónico del Usuario + change_email_user: Cambiar correo electrónico por usuario change_role_user: Cambiar rol del usuario confirm_user: Confirmar Usuario create_account_warning: Crear Advertencia @@ -761,7 +761,7 @@ es-MX: desc_html: Esto se basa en scripts externos de hCaptcha, que pueden suponer una preocupación de seguridad y privacidad. Además, esto puede volver el proceso de registro significativamente menos accesible para algunas personas (especialmente con discapacidades). Por estas razones, por favor, considera medidas alternativas como el registro por aprobación manual o con invitación. title: Solicita a los nuevos usuarios que resuelvan un CAPTCHA para confirmar su cuenta content_retention: - danger_zone: Zona peligrosa + danger_zone: Zona de peligro preamble: Controlar cómo el contenido generado por el usuario se almacena en Mastodon. title: Retención de contenido default_noindex: @@ -944,9 +944,9 @@ es-MX: statuses: allow: Permitir publicación allow_account: Permitir autor - confirm_allow: "¿Estás seguro de que deseas permitir los estados seleccionados?" + confirm_allow: "¿Estás seguro de que deseas permitir las publicaciones seleccionadas?" confirm_allow_account: "¿Estás seguro de que deseas permitir las cuentas seleccionadas?" - confirm_disallow: "¿Estás seguro de que deseas restringir los estados seleccionados?" + confirm_disallow: "¿Estás seguro de que deseas restringir las publicaciones seleccionadas?" confirm_disallow_account: "¿Estás seguro de que deseas restringir las cuentas seleccionadas?" description_html: Estos son publicaciones que su servidor conoce que están siendo compartidas y marcadas como favoritas mucho en este momento. Pueden ayudar a tus usuarios nuevos y retornantes a encontrar más gente a la que seguir. No hay mensajes que se muestren públicamente hasta que apruebes el autor y el autor permita que su cuenta sea sugerida a otros. También puedes permitir o rechazar mensajes individuales. disallow: Rechazar publicación @@ -1152,7 +1152,7 @@ es-MX: title: Crear cuenta de Mastodon en %{domain}. status: account_status: Estado de la cuenta - confirming: Esperando confirmación de correo electrónico. + confirming: Esperando a que se complete la confirmación por correo electrónico. functional: Tu cuenta está completamente operativa. pending: Tu solicitud está pendiente de revisión por nuestro personal. Eso puede tardar un tiempo. Recibirás un correo electrónico cuando tu solicitud sea aprobada. redirecting_to: Tu cuenta se encuentra inactiva porque está siendo redirigida a %{acct}. @@ -1538,7 +1538,7 @@ es-MX: update: subject: "%{name} editó una publicación" notifications: - administration_emails: Notificaciones administrativas por correo + administration_emails: Notificaciones de administración por correo electrónico email_events: Eventos para notificaciones por correo electrónico email_events_hint: 'Selecciona los eventos para los que deseas recibir notificaciones:' number: diff --git a/config/locales/kab.yml b/config/locales/kab.yml index 7c3d526702..7044983ac9 100644 --- a/config/locales/kab.yml +++ b/config/locales/kab.yml @@ -703,6 +703,7 @@ kab: prev: Win iɛeddan preferences: other: Wiyaḍ + posting_defaults: Iɣewwaṛen n usuffeɣ imezwura privacy: privacy: Tabaḍnit search: Anadi @@ -779,6 +780,7 @@ kab: import: Kter import_and_export: Taktert d usifeḍ migrate: Tunigin n umiḍan + notifications: Alɣuten s imayl preferences: Imenyafen profile: Ameɣnu relationships: Imeḍfaṛen akked wid i teṭṭafaṛeḍ diff --git a/config/locales/lv.yml b/config/locales/lv.yml index 55a0751811..88faeae82b 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -1,7 +1,7 @@ --- lv: about: - about_mastodon_html: 'Nākotnes sociālais tīkls: bez reklāmām, bez korporatīvās uzraudzības, ētisks dizains un decentralizācija! Pārvaldi savus datus ar Mastodon!' + about_mastodon_html: 'Nākotnes sabiedriskais tīkls: bez reklāmām, bez korporatīvās novērošanas, ētiska projektēšana un decentralizēšana. Pārvaldi savus datus ar Mastodon!' contact_missing: Nav uzstādīts contact_unavailable: N/A hosted_on: Mastodon mitināts %{domain} @@ -613,6 +613,7 @@ lv: created_at: Ziņoti delete_and_resolve: Izdzēst rakstus forwarded: Pārsūtīti + forwarded_replies_explanation: Šis ziņojums ir no attāla lietotāja un par attālu saturu. Tas tika pārvirzīts šeit, jo saturs, par kuru tika ziņots, ir atbilde vienam no šī servera lietotājiem. forwarded_to: Pārsūtīti %{domain} mark_as_resolved: Atzīmēt kā atrisinātu mark_as_sensitive: Atzīmēt kā sensitīvu @@ -633,6 +634,7 @@ lv: report: 'Ziņojums #%{id}' reported_account: Ziņotais konts reported_by: Ziņoja + reported_with_application: Ziņots no lietotnes resolved: Atrisināts resolved_msg: Ziņojums veiksmīgi atrisināts! skip_to_actions: Pāriet uz darbībām @@ -876,7 +878,13 @@ lv: pending_review: Gaida pārskatīšanu review_requested: Pieprasīta pārskatīšana reviewed: Pārskatīts + title: Stāvoklis + name: Nosaukums + newest: Jaunākie + oldest: Vecākie + reset: Atiestatīt review: Pārskatīt stāvokli + search: Meklēt title: Tēmturi updated_msg: Tēmtura iestatījumi ir veiksmīgi atjaunināti title: Administrēšana @@ -1121,6 +1129,9 @@ lv: view_strikes: Skati iepriekšējos brīdinājumus par savu kontu too_fast: Veidlapa ir iesniegta pārāk ātri, mēģini vēlreiz. use_security_key: Lietot drošības atslēgu + author_attribution: + s_blog: "%{name} emuāri" + title: Autora attiecinājums challenge: confirm: Turpināt hint_html: "Padoms: Nākamās stundas laikā mēs tev vairs neprasīsim paroli." @@ -1158,6 +1169,9 @@ lv: before: 'Pirms turpināšanas lūgums uzmanīgi izlasīt šīs piezīmes:' caches: Citu serveru kešatmiņā saglabātais saturs var saglabāties data_removal: Tavas ziņas un citi dati tiks neatgriezeniski noņemti + email_change_html: Savu e-pasta adresi var mainīt bez sava konta izdzēšanas + email_contact_html: Ja tas joprojām nav saņemts, var nosūtīt e-pastu uz %{email}, lai saņemtu palīdzību + email_reconfirmation_html: Ja netiek saņemts apstiprinājuma e-pasta ziņojums, to var pieprasīt vēlreiz irreversible: Tu nevarēsi atjaunot vai atkārtoti aktivizēt savu kontu more_details_html: Plašāku informāciju skatīt privātuma politika. username_available: Tavs lietotājvārds atkal būs pieejams @@ -1377,6 +1391,7 @@ lv: '86400': 1 diena expires_in_prompt: Nekad generate: Ģenerēt uzaicinājuma saiti + invalid: Šis uzaicinājums nav derīgs invited_by: 'Tevi uzaicināja:' max_uses: one: 1 lietojums @@ -1395,6 +1410,7 @@ lv: authentication_methods: otp: divpakāpju autentifikācijas lietotne password: parole + sign_in_token: e-pasta drošības kods webauthn: drošības atslēgas description_html: Ja pamani darbības, kuras neatpazīsti, jāapsver iespēja nomainīt savu paroli un iespējot divpakāpju autentifikāciju. empty: Nav pieejama autentifikācijas vēsture @@ -1820,14 +1836,20 @@ lv: explanation: Šeit ir daži padomi, kā sākt darbu feature_action: Uzzināt vairāk feature_creativity: Mastodon nodrošina skaņas, video un attēlu ierakstus, pieejamības aprakstus, aptaujas, satura brīdinājumus, animētus profila attēlus, pielāgotas emocijzīmes, sīktēlu apgriešanas vadīklas un vēl, lai palīdzētu Tev sevi izpaust tiešsaistē. Vai Tu izplati savu mākslu, mūziku vai aplādes, Mastodon ir šeit ar Tevi. + feature_moderation_title: Moderēšana, kādai tai būtu jābūt follow_action: Sekot + follow_step: Sekošana aizraujošiem cilvēkiem ir viss, par ko ir Mastodon. follow_title: Pielāgo savu mājas barotni + follows_subtitle: Seko labi zināmiem kontiem follows_title: Kam sekot follows_view_more: Rādīt vairāk cilvēku, kuriem sekot hashtags_recent_count: one: "%{people} cilvēks pēdējās 2 dienās" other: "%{people} cilvēki pēdējās 2 dienās" zero: "%{people} cilvēku pēdējās divās dienās" + hashtags_subtitle: Izpēti, kas pēdējās divās dienāš ir piesasitījis cilvēku uzmanību + hashtags_title: Izplatīti tēmturi + hashtags_view_more: Skatīt vairāk izplatītu tēmturu post_action: Rakstīt post_step: Pasveicini pasauli ar tekstu, fotoattēliem, video vai aptaujām! post_title: Izveido savu pirmo ierakstu @@ -1843,6 +1865,7 @@ lv: invalid_otp_token: Nederīgs divfaktora kods otp_lost_help_html: Ja esi zaudējis piekļuvi abiem, tu vari sazināties ar %{email} rate_limited: Pārāk daudz autentifikācijas mēģinājumu, vēlāk jāmēģina vēlreiz. + seamless_external_login: Tu esi pieteicies caur ārēju pakalpojumu, tāpēc paroles un e-pasta iestatījumi nav pieejami. signed_in_as: 'Pieteicies kā:' verification: extra_instructions_html: Padoms: saite Tavā vietnē var būt neredzama. Svarīga daļa ir rel="me", kas novērš uzdošanos vietnēs ar lietotāju izveidotu saturu. Tu pat vari lapas galvenē izmantot tagu link, nevis a, taču HTML ir jābūt pieejamam bez JavaScript izpildīšanas. @@ -1851,6 +1874,7 @@ lv: instructions_html: Ievieto starpliktuvē un ielīmē tālāk norādīto kodu savas tīmekļvietnes HTML! Tad pievieno savas tīmekļvietnes adresi vienā no papildu laukiem savā profila cilnē "Labot profilu" un saglabā izmaiņas! verification: Pārbaude verified_links: Tavas verifikācijas saites + website_verification: Tīmekļvietnes apliecināšana webauthn_credentials: add: Pievienot jaunu drošības atslēgu create: diff --git a/config/locales/simple_form.es-MX.yml b/config/locales/simple_form.es-MX.yml index 2a2d06ce9b..3271b75668 100644 --- a/config/locales/simple_form.es-MX.yml +++ b/config/locales/simple_form.es-MX.yml @@ -16,12 +16,12 @@ es-MX: account_migration: acct: Especifique el nombre de usuario@dominio de la cuenta a la cual desea migrar account_warning_preset: - text: Puede usar sintaxis de toots, como URLs, hashtags y menciones + text: Puede usar sintaxis de publicaciones, como URLs, etiquetas y menciones title: Opcional. No visible para el destinatario admin_account_action: - include_statuses: El usuario verá qué toots han causado la acción de moderación o advertencia + include_statuses: El usuario verá qué publicaciones han causado la acción de moderación o advertencia send_email_notification: El usuario recibirá una explicación de lo que sucedió con respecto a su cuenta - text_html: Opcional. Puede usar sintaxis de toots. Puede añadir configuraciones predefinidas de advertencia para ahorrar tiempo + text_html: Opcional. Puede usar sintaxis de publicaciones. Puede añadir configuraciones predefinidas de advertencia para ahorrar tiempo type_html: Elige qué hacer con %{acct} types: disable: Evitar que el usuario utilice su cuenta, pero no eliminar ni ocultar sus contenidos. @@ -35,7 +35,7 @@ es-MX: ends_at: Opcional. El anuncio desaparecerá automáticamente en este momento scheduled_at: Dejar en blanco para publicar el anuncio inmediatamente starts_at: Opcional. En caso de que su anuncio esté vinculado a un intervalo de tiempo específico - text: Puedes usar la sintaxis toot. Por favor ten en cuenta el espacio que ocupará el anuncio en la pantalla del usuario + text: Puedes usar la sintaxis de publicaciones. Por favor ten en cuenta el espacio que ocupará el anuncio en la pantalla del usuario appeal: text: Sólo puede apelar una amonestación a la vez defaults: @@ -49,12 +49,12 @@ es-MX: email: Se le enviará un correo de confirmación header: WEBP, PNG, GIF o JPG. Máximo %{size}. Será escalado a %{dimensions}px inbox_url: Copia la URL de la página principal del relés que quieres utilizar - irreversible: Los toots filtrados desaparecerán irreversiblemente, incluso si este filtro es eliminado más adelante + irreversible: Las publicaciones filtradas desaparecerán irreversiblemente, incluso si este filtro es eliminado más adelante locale: El idioma de la interfaz de usuario, correos y notificaciones push password: Utilice al menos 8 caracteres - phrase: Se aplicará sin importar las mayúsculas o los avisos de contenido de un toot + phrase: Se aplicará sin importar las mayúsculas o los avisos de contenido de una publicación scopes: Qué APIs de la aplicación tendrán acceso. Si seleccionas el alcance de nivel mas alto, no necesitas seleccionar las individuales. - setting_aggregate_reblogs: No mostrar nuevos retoots para los toots que han sido recientemente retooteados (sólo afecta a los retoots recibidos recientemente) + setting_aggregate_reblogs: No mostrar nuevos impulsos para las publicaciones que han sido recientemente impulsadas (sólo afecta a las publicaciones recibidas recientemente) setting_always_send_emails: Normalmente las notificaciones por correo electrónico no se enviarán cuando estés usando Mastodon activamente setting_default_sensitive: El contenido multimedia sensible está oculto por defecto y puede ser mostrado con un click setting_display_media_default: Ocultar contenido multimedia marcado como sensible @@ -130,7 +130,7 @@ es-MX: tag: name: Sólo se puede cambiar el cajón de las letras, por ejemplo, para que sea más legible user: - chosen_languages: Cuando se marca, solo se mostrarán los toots en los idiomas seleccionados en los timelines públicos + chosen_languages: Cuando se marca, solo se mostrarán las publicaciones en los idiomas seleccionados en las líneas de tiempo públicas role: El rol controla qué permisos tiene el usuario. user_role: color: Color que se utilizará para el rol a lo largo de la interfaz de usuario, como RGB en formato hexadecimal @@ -160,7 +160,7 @@ es-MX: text: Texto predefinido title: Título admin_account_action: - include_statuses: Incluir en el correo electrónico a los toots denunciados + include_statuses: Incluir en el correo electrónico a las publicaciones denunciadas send_email_notification: Notificar al usuario por correo electrónico text: Aviso personalizado type: Acción @@ -205,21 +205,21 @@ es-MX: password: Contraseña phrase: Palabra clave o frase setting_advanced_layout: Habilitar interfaz web avanzada - setting_aggregate_reblogs: Agrupar retoots en las líneas de tiempo + setting_aggregate_reblogs: Agrupar impulsos en las líneas de tiempo setting_always_send_emails: Enviar siempre notificaciones por correo setting_auto_play_gif: Reproducir automáticamente los GIFs animados - setting_boost_modal: Mostrar ventana de confirmación antes de un Retoot + setting_boost_modal: Mostrar ventana de confirmación antes de impulsar setting_default_language: Idioma de publicación 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_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 setting_display_media_hide_all: Ocultar todo setting_display_media_show_all: Mostrar todo - setting_expand_spoilers: Siempre expandir los toots marcados con advertencias de contenido + setting_expand_spoilers: Siempre expandir las publicaciones marcadas con advertencias de contenido setting_hide_network: Ocultar tu red setting_reduce_motion: Reducir el movimiento de las animaciones setting_system_font_ui: Utilizar la tipografía por defecto del sistema diff --git a/config/locales/simple_form.kab.yml b/config/locales/simple_form.kab.yml index 68fc629d9e..203b023715 100644 --- a/config/locales/simple_form.kab.yml +++ b/config/locales/simple_form.kab.yml @@ -20,6 +20,7 @@ kab: irreversible: Tisuffaɣ i tessazedgeḍ ad ttwakksent i lebda, ula ma tekkseḍ imsizdeg-nni ar zdat locale: Tutlayt n ugrudem, imaylen d walɣuten yettudemren password: Seqdec ma drus 8 n yisekkilen + setting_always_send_emails: S umata, ilɣa s yimayl ur d-ttwaceyyεen ara mi ara tesseqdaceḍ Mastodon s wudem urmid setting_display_media_default: Ffer imidyaten yettwacreḍ d infariyen setting_display_media_hide_all: Ffer yal tikkelt akk taywalt setting_display_media_show_all: Ffer yal tikkelt teywalt yettwacreḍ d tanafrit @@ -84,8 +85,9 @@ kab: 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_always_send_emails: Dima ttazen-d ilɣa s yimayl + setting_default_language: Tutlayt n usuffeɣ + setting_default_privacy: Tabaḍnit n usuffeɣ setting_display_media: Askanay n imidyaten setting_display_media_default: Akk-a kan setting_display_media_hide_all: Ffer-iten akk diff --git a/config/locales/simple_form.lv.yml b/config/locales/simple_form.lv.yml index 4369c3c42b..ed27e08bc3 100644 --- a/config/locales/simple_form.lv.yml +++ b/config/locales/simple_form.lv.yml @@ -3,7 +3,7 @@ lv: simple_form: hints: account: - attribution_domains_as_text: Aizsargā no nepatiesas piedēvēšanas. + attribution_domains_as_text: Aizsargā no nepatiesa attiecinājuma. discoverable: Tavas publiskās ziņas un profils var tikt piedāvāti vai ieteikti dažādās Mastodon vietās, un tavs profils var tikt ieteikts citiem lietotājiem. display_name: Tavs pilnais vārds vai tavs joku vārds. fields: Tava mājas lapa, vietniekvārdi, vecums, viss, ko vēlies. From 6f3d7516dc535ab04b040d69b2682d8bc69d5d7e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 08:10:22 +0000 Subject: [PATCH 05/93] Update dependency dotenv to v3.1.4 (#31953) 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 12f1b5db2c..3f321c7b7e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -212,7 +212,7 @@ GEM domain_name (0.6.20240107) doorkeeper (5.7.1) railties (>= 5) - dotenv (3.1.2) + dotenv (3.1.4) drb (2.2.1) ed25519 (1.3.0) elasticsearch (7.17.11) From 943738671c02424b4956de0994c4bd298191fe54 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 18 Sep 2024 04:21:31 -0400 Subject: [PATCH 06/93] Remove unneeded `to_s` on `Link` header comparison in statuses controller spec (#31941) --- spec/controllers/statuses_controller_spec.rb | 30 ++++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/spec/controllers/statuses_controller_spec.rb b/spec/controllers/statuses_controller_spec.rb index 5042523df8..d9702251f4 100644 --- a/spec/controllers/statuses_controller_spec.rb +++ b/spec/controllers/statuses_controller_spec.rb @@ -63,7 +63,7 @@ RSpec.describe StatusesController do expect(response.headers).to include( 'Vary' => 'Accept, Accept-Language, Cookie', 'Cache-Control' => include('public'), - 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + 'Link' => include('activity+json') ) expect(response.body).to include status.text end @@ -79,7 +79,7 @@ RSpec.describe StatusesController do expect(response.headers).to include( 'Content-Type' => include('application/activity+json'), - 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + 'Link' => include('activity+json') ) expect(response.parsed_body) .to include(content: include(status.text)) @@ -168,7 +168,7 @@ RSpec.describe StatusesController do expect(response.headers).to include( 'Vary' => 'Accept, Accept-Language, Cookie', 'Cache-Control' => include('private'), - 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + 'Link' => include('activity+json') ) expect(response.body).to include status.text end @@ -184,7 +184,7 @@ RSpec.describe StatusesController do 'Vary' => 'Accept, Accept-Language, Cookie', 'Cache-Control' => include('private'), 'Content-Type' => include('application/activity+json'), - 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + 'Link' => include('activity+json') ) expect(response.parsed_body) .to include(content: include(status.text)) @@ -212,7 +212,7 @@ RSpec.describe StatusesController do expect(response.headers).to include( 'Vary' => 'Accept, Accept-Language, Cookie', 'Cache-Control' => include('private'), - 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + 'Link' => include('activity+json') ) expect(response.body).to include status.text end @@ -228,7 +228,7 @@ RSpec.describe StatusesController do 'Vary' => 'Accept, Accept-Language, Cookie', 'Cache-Control' => include('private'), 'Content-Type' => include('application/activity+json'), - 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + 'Link' => include('activity+json') ) expect(response.parsed_body) .to include(content: include(status.text)) @@ -278,7 +278,7 @@ RSpec.describe StatusesController do expect(response.headers).to include( 'Vary' => 'Accept, Accept-Language, Cookie', 'Cache-Control' => include('private'), - 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + 'Link' => include('activity+json') ) expect(response.body).to include status.text end @@ -294,7 +294,7 @@ RSpec.describe StatusesController do 'Vary' => 'Accept, Accept-Language, Cookie', 'Cache-Control' => include('private'), 'Content-Type' => include('application/activity+json'), - 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + 'Link' => include('activity+json') ) expect(response.parsed_body) .to include(content: include(status.text)) @@ -370,7 +370,7 @@ RSpec.describe StatusesController do expect(response.headers).to include( 'Vary' => 'Accept, Accept-Language, Cookie', 'Cache-Control' => include('private'), - 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + 'Link' => include('activity+json') ) expect(response.body).to include status.text end @@ -385,7 +385,7 @@ RSpec.describe StatusesController do .and have_cacheable_headers.with_vary('Accept, Accept-Language, Cookie') expect(response.headers).to include( 'Content-Type' => include('application/activity+json'), - 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + 'Link' => include('activity+json') ) expect(response.parsed_body) .to include(content: include(status.text)) @@ -412,7 +412,7 @@ RSpec.describe StatusesController do expect(response.headers).to include( 'Vary' => 'Accept, Accept-Language, Cookie', 'Cache-Control' => include('private'), - 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + 'Link' => include('activity+json') ) expect(response.body).to include status.text end @@ -428,7 +428,7 @@ RSpec.describe StatusesController do 'Vary' => 'Accept, Accept-Language, Cookie', 'Cache-Control' => include('private'), 'Content-Type' => include('application/activity+json'), - 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + 'Link' => include('activity+json') ) expect(response.parsed_body) @@ -479,7 +479,7 @@ RSpec.describe StatusesController do expect(response.headers).to include( 'Vary' => 'Accept, Accept-Language, Cookie', 'Cache-Control' => include('private'), - 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + 'Link' => include('activity+json') ) expect(response.body).to include status.text end @@ -495,7 +495,7 @@ RSpec.describe StatusesController do 'Vary' => 'Accept, Accept-Language, Cookie', 'Cache-Control' => include('private'), 'Content-Type' => include('application/activity+json'), - 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + 'Link' => include('activity+json') ) expect(response.parsed_body) .to include(content: include(status.text)) @@ -779,7 +779,7 @@ RSpec.describe StatusesController do expect(response.headers).to include( 'Vary' => 'Accept, Accept-Language, Cookie', 'Cache-Control' => include('public'), - 'Link' => satisfy { |header| header.to_s.include?('activity+json') } + 'Link' => include('activity+json') ) end end From eb16763bff5cc2d38dec19c82d47615cd2b54e27 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 18 Sep 2024 04:22:07 -0400 Subject: [PATCH 07/93] Use `have_http_link_header` matcher in `api/v1/trends/*` specs (#31940) --- spec/requests/api/v1/trends/links_spec.rb | 9 ++++++--- spec/requests/api/v1/trends/statuses_spec.rb | 9 ++++++--- spec/requests/api/v1/trends/tags_spec.rb | 10 ++++++---- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/spec/requests/api/v1/trends/links_spec.rb b/spec/requests/api/v1/trends/links_spec.rb index 012d035907..04d36da0a2 100644 --- a/spec/requests/api/v1/trends/links_spec.rb +++ b/spec/requests/api/v1/trends/links_spec.rb @@ -10,7 +10,9 @@ RSpec.describe 'API V1 Trends Links' do it 'returns http success' do get '/api/v1/trends/links' - expect(response).to have_http_status(200) + expect(response) + .to have_http_status(200) + .and not_have_http_link_header end end @@ -22,8 +24,9 @@ RSpec.describe 'API V1 Trends Links' do stub_const('Api::V1::Trends::LinksController::DEFAULT_LINKS_LIMIT', 2) get '/api/v1/trends/links' - expect(response).to have_http_status(200) - expect(response.headers).to include('Link') + expect(response) + .to have_http_status(200) + .and have_http_link_header(api_v1_trends_links_url(offset: 2)).for(rel: 'next') end def prepare_trends diff --git a/spec/requests/api/v1/trends/statuses_spec.rb b/spec/requests/api/v1/trends/statuses_spec.rb index 3b906e8f82..f04addfe0a 100644 --- a/spec/requests/api/v1/trends/statuses_spec.rb +++ b/spec/requests/api/v1/trends/statuses_spec.rb @@ -10,7 +10,9 @@ RSpec.describe 'API V1 Trends Statuses' do it 'returns http success' do get '/api/v1/trends/statuses' - expect(response).to have_http_status(200) + expect(response) + .to have_http_status(200) + .and not_have_http_link_header end end @@ -22,8 +24,9 @@ RSpec.describe 'API V1 Trends Statuses' do stub_const('Api::BaseController::DEFAULT_STATUSES_LIMIT', 2) get '/api/v1/trends/statuses' - expect(response).to have_http_status(200) - expect(response.headers).to include('Link') + expect(response) + .to have_http_status(200) + .and have_http_link_header(api_v1_trends_statuses_url(offset: 2)).for(rel: 'next') end def prepare_trends diff --git a/spec/requests/api/v1/trends/tags_spec.rb b/spec/requests/api/v1/trends/tags_spec.rb index 598f4e7752..2ff51eed63 100644 --- a/spec/requests/api/v1/trends/tags_spec.rb +++ b/spec/requests/api/v1/trends/tags_spec.rb @@ -10,8 +10,9 @@ RSpec.describe 'API V1 Trends Tags' do it 'returns http success' do get '/api/v1/trends/tags' - expect(response).to have_http_status(200) - expect(response.headers).to_not include('Link') + expect(response) + .to have_http_status(200) + .and not_have_http_link_header end end @@ -23,8 +24,9 @@ RSpec.describe 'API V1 Trends Tags' do stub_const('Api::V1::Trends::TagsController::DEFAULT_TAGS_LIMIT', 2) get '/api/v1/trends/tags' - expect(response).to have_http_status(200) - expect(response.headers).to include('Link') + expect(response) + .to have_http_status(200) + .and have_http_link_header(api_v1_trends_tags_url(offset: 2)).for(rel: 'next') end def prepare_trends From a791274824d1136278a0139f4b15f54433e56561 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 08:28:42 +0000 Subject: [PATCH 08/93] Update dependency sass to v1.79.1 (#31958) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 62 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/yarn.lock b/yarn.lock index 49b6e241f8..73ea69ee71 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5899,25 +5899,6 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:>=3.0.0 <4.0.0, chokidar@npm:^3.4.1": - version: 3.5.3 - resolution: "chokidar@npm:3.5.3" - dependencies: - anymatch: "npm:~3.1.2" - braces: "npm:~3.0.2" - fsevents: "npm:~2.3.2" - glob-parent: "npm:~5.1.2" - is-binary-path: "npm:~2.1.0" - is-glob: "npm:~4.0.1" - normalize-path: "npm:~3.0.0" - readdirp: "npm:~3.6.0" - dependenciesMeta: - fsevents: - optional: true - checksum: 10c0/1076953093e0707c882a92c66c0f56ba6187831aa51bb4de878c1fec59ae611a3bf02898f190efec8e77a086b8df61c2b2a3ea324642a0558bdf8ee6c5dc9ca1 - languageName: node - linkType: hard - "chokidar@npm:^2.1.8": version: 2.1.8 resolution: "chokidar@npm:2.1.8" @@ -5941,6 +5922,34 @@ __metadata: languageName: node linkType: hard +"chokidar@npm:^3.4.1": + version: 3.5.3 + resolution: "chokidar@npm:3.5.3" + dependencies: + anymatch: "npm:~3.1.2" + braces: "npm:~3.0.2" + fsevents: "npm:~2.3.2" + glob-parent: "npm:~5.1.2" + is-binary-path: "npm:~2.1.0" + is-glob: "npm:~4.0.1" + normalize-path: "npm:~3.0.0" + readdirp: "npm:~3.6.0" + dependenciesMeta: + fsevents: + optional: true + checksum: 10c0/1076953093e0707c882a92c66c0f56ba6187831aa51bb4de878c1fec59ae611a3bf02898f190efec8e77a086b8df61c2b2a3ea324642a0558bdf8ee6c5dc9ca1 + languageName: node + linkType: hard + +"chokidar@npm:^4.0.0": + version: 4.0.0 + resolution: "chokidar@npm:4.0.0" + dependencies: + readdirp: "npm:^4.0.1" + checksum: 10c0/42d03c53b0ad200689e4fae7763133561480561cab8ba5304e8f2298ff45ff84bf0f6065c3f02b9e557b74b156813734439a1a2ff19a1ea6b35692395cd92738 + languageName: node + linkType: hard + "chownr@npm:^2.0.0": version: 2.0.0 resolution: "chownr@npm:2.0.0" @@ -14884,6 +14893,13 @@ __metadata: languageName: node linkType: hard +"readdirp@npm:^4.0.1": + version: 4.0.1 + resolution: "readdirp@npm:4.0.1" + checksum: 10c0/e5a0b547015f68ecc918f115b62b75b2b840611480a9240cb3317090a0ddac01bb9b40315a8fa08acdf52a43eea17b808c89b645263cba3ab64dc557d7f801f1 + languageName: node + linkType: hard + "readdirp@npm:~3.6.0": version: 3.6.0 resolution: "readdirp@npm:3.6.0" @@ -15459,15 +15475,15 @@ __metadata: linkType: hard "sass@npm:^1.62.1": - version: 1.78.0 - resolution: "sass@npm:1.78.0" + version: 1.79.1 + resolution: "sass@npm:1.79.1" dependencies: - chokidar: "npm:>=3.0.0 <4.0.0" + chokidar: "npm:^4.0.0" immutable: "npm:^4.0.0" source-map-js: "npm:>=0.6.2 <2.0.0" bin: sass: sass.js - checksum: 10c0/6577a87c00b03a5a50f3a11b4b6592f28abce34e61812e381535a3b712151bd94db3ca06467d20395431e0f38a23f99e616d6859d771fb6d4617c359f590c48c + checksum: 10c0/187bc885bad2e81e5414a146c9e14a28f622b5aba5c1425f4dd6d8ef2045b66c69de93689503886a8d2cc55d6bcce97b05ee87b074628fefd03e762789f931d0 languageName: node linkType: hard From 7740f1a6bb6c34a1d4fdfeaa29c31ac24fd4b236 Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Wed, 18 Sep 2024 10:43:24 +0200 Subject: [PATCH 09/93] Mute XHR abort errors (#31952) --- app/javascript/mastodon/actions/alerts.js | 7 +++++++ app/javascript/mastodon/api.ts | 3 +++ 2 files changed, 10 insertions(+) diff --git a/app/javascript/mastodon/actions/alerts.js b/app/javascript/mastodon/actions/alerts.js index 42834146bf..48dee2587f 100644 --- a/app/javascript/mastodon/actions/alerts.js +++ b/app/javascript/mastodon/actions/alerts.js @@ -1,5 +1,7 @@ import { defineMessages } from 'react-intl'; +import { AxiosError } from 'axios'; + const messages = defineMessages({ unexpectedTitle: { id: 'alert.unexpected.title', defaultMessage: 'Oops!' }, unexpectedMessage: { id: 'alert.unexpected.message', defaultMessage: 'An unexpected error occurred.' }, @@ -50,6 +52,11 @@ export const showAlertForError = (error, skipNotFound = false) => { }); } + // An aborted request, e.g. due to reloading the browser window, it not really error + if (error.code === AxiosError.ECONNABORTED) { + return { type: ALERT_NOOP }; + } + console.error(error); return showAlert({ diff --git a/app/javascript/mastodon/api.ts b/app/javascript/mastodon/api.ts index 24672290c7..25bb25547c 100644 --- a/app/javascript/mastodon/api.ts +++ b/app/javascript/mastodon/api.ts @@ -42,6 +42,9 @@ const authorizationTokenFromInitialState = (): RawAxiosRequestHeaders => { // eslint-disable-next-line import/no-default-export export default function api(withAuthorization = true) { return axios.create({ + transitional: { + clarifyTimeoutError: true, + }, headers: { ...csrfHeader, ...(withAuthorization ? authorizationTokenFromInitialState() : {}), From 2d399f5d4a26e2f9c398ec7ab01f1aaee1d0e1ac Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 11:17:57 +0200 Subject: [PATCH 10/93] Update dependency pg-connection-string to v2.7.0 (#31950) 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 73ea69ee71..45ede6ca99 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13002,9 +13002,9 @@ __metadata: linkType: hard "pg-connection-string@npm:^2.6.0, pg-connection-string@npm:^2.6.4": - version: 2.6.4 - resolution: "pg-connection-string@npm:2.6.4" - checksum: 10c0/0d0b617df0fc6507bf6a94bdcd56c7a305788a1402d69bff9773350947c8f525d6d8136128065370749a3325e99658ae40fbdcce620fb8e60126181f0591a6a6 + version: 2.7.0 + resolution: "pg-connection-string@npm:2.7.0" + checksum: 10c0/50a1496a1c858f9495d78a2c7a66d93ef3602e718aff2953bb5738f3ea616d7f727f32fc20513c9bed127650cd14c1ddc7b458396f4000e689d4b64c65c5c51e languageName: node linkType: hard From 5405bdd344ff140e4e9cf9782770e7180794edec Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 18 Sep 2024 05:27:43 -0400 Subject: [PATCH 11/93] Remove unused E2EE messaging code (#31193) --- Gemfile | 1 - Gemfile.lock | 2 - .../activitypub/claims_controller.rb | 18 ----- .../activitypub/collections_controller.rb | 6 +- .../api/v1/crypto/deliveries_controller.rb | 30 ------- .../crypto/encrypted_messages_controller.rb | 47 ----------- .../api/v1/crypto/keys/claims_controller.rb | 25 ------ .../api/v1/crypto/keys/counts_controller.rb | 17 ---- .../api/v1/crypto/keys/queries_controller.rb | 26 ------ .../api/v1/crypto/keys/uploads_controller.rb | 29 ------- app/helpers/context_helper.rb | 17 ---- app/lib/activitypub/activity/create.rb | 35 +------- app/lib/inline_renderer.rb | 2 - app/lib/vacuum/system_keys_vacuum.rb | 13 --- app/models/account.rb | 8 +- app/models/concerns/account/associations.rb | 3 - app/models/device.rb | 36 --------- app/models/encrypted_message.rb | 49 ------------ app/models/message_franking.rb | 19 ----- app/models/one_time_key.rb | 22 ------ app/models/system_key.rb | 41 ---------- .../activitypub/activity_serializer.rb | 2 - .../activitypub/actor_serializer.rb | 7 +- .../activitypub/collection_serializer.rb | 2 - .../activitypub/device_serializer.rb | 52 ------------ .../encrypted_message_serializer.rb | 61 -------------- .../activitypub/one_time_key_serializer.rb | 35 -------- .../rest/encrypted_message_serializer.rb | 19 ----- .../rest/keys/claim_result_serializer.rb | 9 --- .../rest/keys/device_serializer.rb | 6 -- .../rest/keys/query_result_serializer.rb | 11 --- .../activitypub/process_account_service.rb | 1 - app/services/delete_account_service.rb | 2 - app/services/deliver_to_device_service.rb | 78 ------------------ app/services/keys/claim_service.rb | 79 ------------------- app/services/keys/query_service.rb | 79 ------------------- app/validators/ed25519_key_validator.rb | 19 ----- app/validators/ed25519_signature_validator.rb | 29 ------- app/workers/push_encrypted_message_worker.rb | 16 ---- config/initializers/doorkeeper.rb | 3 +- config/initializers/inflections.rb | 1 - config/locales/an.yml | 1 - config/locales/ar.yml | 1 - config/locales/ast.yml | 1 - config/locales/be.yml | 1 - config/locales/bg.yml | 1 - config/locales/ca.yml | 1 - config/locales/ckb.yml | 1 - config/locales/co.yml | 1 - config/locales/cs.yml | 1 - config/locales/cy.yml | 1 - config/locales/da.yml | 1 - config/locales/de.yml | 1 - config/locales/el.yml | 1 - config/locales/en-GB.yml | 1 - config/locales/en.yml | 1 - config/locales/eo.yml | 1 - config/locales/es-AR.yml | 1 - config/locales/es-MX.yml | 1 - config/locales/es.yml | 1 - config/locales/et.yml | 1 - config/locales/eu.yml | 1 - config/locales/fa.yml | 1 - config/locales/fi.yml | 1 - config/locales/fo.yml | 1 - config/locales/fr-CA.yml | 1 - config/locales/fr.yml | 1 - config/locales/fy.yml | 1 - config/locales/ga.yml | 1 - config/locales/gd.yml | 1 - config/locales/gl.yml | 1 - config/locales/he.yml | 1 - config/locales/hu.yml | 1 - config/locales/hy.yml | 1 - config/locales/ia.yml | 1 - config/locales/id.yml | 1 - config/locales/ie.yml | 1 - config/locales/io.yml | 1 - config/locales/is.yml | 1 - config/locales/it.yml | 1 - config/locales/ja.yml | 1 - config/locales/ko.yml | 1 - config/locales/ku.yml | 1 - config/locales/lad.yml | 1 - config/locales/lv.yml | 1 - config/locales/ms.yml | 1 - config/locales/my.yml | 1 - config/locales/nl.yml | 1 - config/locales/nn.yml | 1 - config/locales/no.yml | 1 - config/locales/pl.yml | 1 - config/locales/pt-BR.yml | 1 - config/locales/pt-PT.yml | 1 - config/locales/ru.yml | 1 - config/locales/sc.yml | 1 - config/locales/sco.yml | 1 - config/locales/si.yml | 1 - config/locales/sl.yml | 1 - config/locales/sq.yml | 1 - config/locales/sr-Latn.yml | 1 - config/locales/sr.yml | 1 - config/locales/sv.yml | 1 - config/locales/ta.yml | 1 - config/locales/th.yml | 1 - config/locales/tr.yml | 1 - config/locales/uk.yml | 1 - config/locales/vi.yml | 1 - config/locales/zh-CN.yml | 1 - config/locales/zh-HK.yml | 1 - config/locales/zh-TW.yml | 1 - config/routes.rb | 1 - config/routes/api.rb | 17 ---- ...20140205_drop_end_to_end_message_tables.rb | 15 ++++ db/schema.rb | 50 ------------ .../activitypub/claims_controller_spec.rb | 19 ----- spec/fabricators/device_fabricator.rb | 10 --- .../encrypted_message_fabricator.rb | 7 -- spec/fabricators/one_time_key_fabricator.rb | 13 --- spec/fabricators/system_key_fabricator.rb | 3 - spec/lib/activitypub/activity/create_spec.rb | 58 -------------- spec/lib/vacuum/system_keys_vacuum_spec.rb | 24 ------ spec/models/one_time_key_spec.rb | 23 ------ .../activitypub/device_serializer_spec.rb | 14 ---- .../one_time_key_serializer_spec.rb | 14 ---- .../rest/encrypted_message_serializer_spec.rb | 14 ---- .../rest/keys/claim_result_serializer_spec.rb | 14 ---- .../rest/keys/device_serializer_spec.rb | 14 ---- .../rest/keys/query_result_serializer_spec.rb | 14 ---- .../push_encrypted_message_worker_spec.rb | 13 --- streaming/index.js | 9 +-- 130 files changed, 25 insertions(+), 1347 deletions(-) delete mode 100644 app/controllers/activitypub/claims_controller.rb delete mode 100644 app/controllers/api/v1/crypto/deliveries_controller.rb delete mode 100644 app/controllers/api/v1/crypto/encrypted_messages_controller.rb delete mode 100644 app/controllers/api/v1/crypto/keys/claims_controller.rb delete mode 100644 app/controllers/api/v1/crypto/keys/counts_controller.rb delete mode 100644 app/controllers/api/v1/crypto/keys/queries_controller.rb delete mode 100644 app/controllers/api/v1/crypto/keys/uploads_controller.rb delete mode 100644 app/lib/vacuum/system_keys_vacuum.rb delete mode 100644 app/models/device.rb delete mode 100644 app/models/encrypted_message.rb delete mode 100644 app/models/message_franking.rb delete mode 100644 app/models/one_time_key.rb delete mode 100644 app/models/system_key.rb delete mode 100644 app/serializers/activitypub/device_serializer.rb delete mode 100644 app/serializers/activitypub/encrypted_message_serializer.rb delete mode 100644 app/serializers/activitypub/one_time_key_serializer.rb delete mode 100644 app/serializers/rest/encrypted_message_serializer.rb delete mode 100644 app/serializers/rest/keys/claim_result_serializer.rb delete mode 100644 app/serializers/rest/keys/device_serializer.rb delete mode 100644 app/serializers/rest/keys/query_result_serializer.rb delete mode 100644 app/services/deliver_to_device_service.rb delete mode 100644 app/services/keys/claim_service.rb delete mode 100644 app/services/keys/query_service.rb delete mode 100644 app/validators/ed25519_key_validator.rb delete mode 100644 app/validators/ed25519_signature_validator.rb delete mode 100644 app/workers/push_encrypted_message_worker.rb create mode 100644 db/migrate/20240720140205_drop_end_to_end_message_tables.rb delete mode 100644 spec/controllers/activitypub/claims_controller_spec.rb delete mode 100644 spec/fabricators/device_fabricator.rb delete mode 100644 spec/fabricators/encrypted_message_fabricator.rb delete mode 100644 spec/fabricators/one_time_key_fabricator.rb delete mode 100644 spec/fabricators/system_key_fabricator.rb delete mode 100644 spec/lib/vacuum/system_keys_vacuum_spec.rb delete mode 100644 spec/models/one_time_key_spec.rb delete mode 100644 spec/serializers/activitypub/device_serializer_spec.rb delete mode 100644 spec/serializers/activitypub/one_time_key_serializer_spec.rb delete mode 100644 spec/serializers/rest/encrypted_message_serializer_spec.rb delete mode 100644 spec/serializers/rest/keys/claim_result_serializer_spec.rb delete mode 100644 spec/serializers/rest/keys/device_serializer_spec.rb delete mode 100644 spec/serializers/rest/keys/query_result_serializer_spec.rb delete mode 100644 spec/workers/push_encrypted_message_worker_spec.rb diff --git a/Gemfile b/Gemfile index 4cce095ec5..bcb19421ab 100644 --- a/Gemfile +++ b/Gemfile @@ -47,7 +47,6 @@ gem 'color_diff', '~> 0.1' gem 'csv', '~> 3.2' gem 'discard', '~> 1.2' gem 'doorkeeper', '~> 5.6' -gem 'ed25519', '~> 1.3' gem 'fast_blank', '~> 1.0' gem 'fastimage' gem 'hiredis', '~> 0.6' diff --git a/Gemfile.lock b/Gemfile.lock index 3f321c7b7e..5ed8fe78eb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -214,7 +214,6 @@ GEM railties (>= 5) dotenv (3.1.4) drb (2.2.1) - ed25519 (1.3.0) elasticsearch (7.17.11) elasticsearch-api (= 7.17.11) elasticsearch-transport (= 7.17.11) @@ -937,7 +936,6 @@ DEPENDENCIES discard (~> 1.2) doorkeeper (~> 5.6) dotenv - ed25519 (~> 1.3) email_spec fabrication (~> 2.30) faker (~> 3.2) diff --git a/app/controllers/activitypub/claims_controller.rb b/app/controllers/activitypub/claims_controller.rb deleted file mode 100644 index 480baaf2bc..0000000000 --- a/app/controllers/activitypub/claims_controller.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -class ActivityPub::ClaimsController < ActivityPub::BaseController - skip_before_action :authenticate_user! - - before_action :require_account_signature! - before_action :set_claim_result - - def create - render json: @claim_result, serializer: ActivityPub::OneTimeKeySerializer - end - - private - - def set_claim_result - @claim_result = ::Keys::ClaimService.new.call(@account.id, params[:id]) - end -end diff --git a/app/controllers/activitypub/collections_controller.rb b/app/controllers/activitypub/collections_controller.rb index c25362c9bc..ab1b98e646 100644 --- a/app/controllers/activitypub/collections_controller.rb +++ b/app/controllers/activitypub/collections_controller.rb @@ -22,8 +22,6 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController @items = @items.map { |item| item.distributable? ? item : ActivityPub::TagManager.instance.uri_for(item) } when 'tags' @items = for_signed_account { @account.featured_tags } - when 'devices' - @items = @account.devices else not_found end @@ -31,7 +29,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController def set_size case params[:id] - when 'featured', 'devices', 'tags' + when 'featured', 'tags' @size = @items.size else not_found @@ -42,7 +40,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController case params[:id] when 'featured' @type = :ordered - when 'devices', 'tags' + when 'tags' @type = :unordered else not_found diff --git a/app/controllers/api/v1/crypto/deliveries_controller.rb b/app/controllers/api/v1/crypto/deliveries_controller.rb deleted file mode 100644 index aa9df6e03b..0000000000 --- a/app/controllers/api/v1/crypto/deliveries_controller.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -class Api::V1::Crypto::DeliveriesController < Api::BaseController - before_action -> { doorkeeper_authorize! :crypto } - before_action :require_user! - before_action :set_current_device - - def create - devices.each do |device_params| - DeliverToDeviceService.new.call(current_account, @current_device, device_params) - end - - render_empty - end - - private - - def set_current_device - @current_device = Device.find_by!(access_token: doorkeeper_token) - end - - def resource_params - params.require(:device) - params.permit(device: [:account_id, :device_id, :type, :body, :hmac]) - end - - def devices - Array(resource_params[:device]) - end -end diff --git a/app/controllers/api/v1/crypto/encrypted_messages_controller.rb b/app/controllers/api/v1/crypto/encrypted_messages_controller.rb deleted file mode 100644 index 93ae0e7771..0000000000 --- a/app/controllers/api/v1/crypto/encrypted_messages_controller.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: true - -class Api::V1::Crypto::EncryptedMessagesController < Api::BaseController - LIMIT = 80 - - before_action -> { doorkeeper_authorize! :crypto } - before_action :require_user! - before_action :set_current_device - - before_action :set_encrypted_messages, only: :index - after_action :insert_pagination_headers, only: :index - - def index - render json: @encrypted_messages, each_serializer: REST::EncryptedMessageSerializer - end - - def clear - @current_device.encrypted_messages.up_to(params[:up_to_id]).delete_all - render_empty - end - - private - - def set_current_device - @current_device = Device.find_by!(access_token: doorkeeper_token) - end - - def set_encrypted_messages - @encrypted_messages = @current_device.encrypted_messages.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) - end - - def next_path - api_v1_crypto_encrypted_messages_url pagination_params(max_id: pagination_max_id) if records_continue? - end - - def prev_path - api_v1_crypto_encrypted_messages_url pagination_params(min_id: pagination_since_id) unless @encrypted_messages.empty? - end - - def pagination_collection - @encrypted_messages - end - - def records_continue? - @encrypted_messages.size == limit_param(LIMIT) - end -end diff --git a/app/controllers/api/v1/crypto/keys/claims_controller.rb b/app/controllers/api/v1/crypto/keys/claims_controller.rb deleted file mode 100644 index f9d202d67b..0000000000 --- a/app/controllers/api/v1/crypto/keys/claims_controller.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -class Api::V1::Crypto::Keys::ClaimsController < Api::BaseController - before_action -> { doorkeeper_authorize! :crypto } - before_action :require_user! - before_action :set_claim_results - - def create - render json: @claim_results, each_serializer: REST::Keys::ClaimResultSerializer - end - - private - - def set_claim_results - @claim_results = devices.filter_map { |device_params| ::Keys::ClaimService.new.call(current_account, device_params[:account_id], device_params[:device_id]) } - end - - def resource_params - params.permit(device: [:account_id, :device_id]) - end - - def devices - Array(resource_params[:device]) - end -end diff --git a/app/controllers/api/v1/crypto/keys/counts_controller.rb b/app/controllers/api/v1/crypto/keys/counts_controller.rb deleted file mode 100644 index ffd7151b78..0000000000 --- a/app/controllers/api/v1/crypto/keys/counts_controller.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -class Api::V1::Crypto::Keys::CountsController < Api::BaseController - before_action -> { doorkeeper_authorize! :crypto } - before_action :require_user! - before_action :set_current_device - - def show - render json: { one_time_keys: @current_device.one_time_keys.count } - end - - private - - def set_current_device - @current_device = Device.find_by!(access_token: doorkeeper_token) - end -end diff --git a/app/controllers/api/v1/crypto/keys/queries_controller.rb b/app/controllers/api/v1/crypto/keys/queries_controller.rb deleted file mode 100644 index e6ce9f9192..0000000000 --- a/app/controllers/api/v1/crypto/keys/queries_controller.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -class Api::V1::Crypto::Keys::QueriesController < Api::BaseController - before_action -> { doorkeeper_authorize! :crypto } - before_action :require_user! - before_action :set_accounts - before_action :set_query_results - - def create - render json: @query_results, each_serializer: REST::Keys::QueryResultSerializer - end - - private - - def set_accounts - @accounts = Account.where(id: account_ids).includes(:devices) - end - - def set_query_results - @query_results = @accounts.filter_map { |account| ::Keys::QueryService.new.call(account) } - end - - def account_ids - Array(params[:id]).map(&:to_i) - end -end diff --git a/app/controllers/api/v1/crypto/keys/uploads_controller.rb b/app/controllers/api/v1/crypto/keys/uploads_controller.rb deleted file mode 100644 index fc4abf63b3..0000000000 --- a/app/controllers/api/v1/crypto/keys/uploads_controller.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -class Api::V1::Crypto::Keys::UploadsController < Api::BaseController - before_action -> { doorkeeper_authorize! :crypto } - before_action :require_user! - - def create - device = Device.find_or_initialize_by(access_token: doorkeeper_token) - - device.transaction do - device.account = current_account - device.update!(resource_params[:device]) - - if resource_params[:one_time_keys].present? && resource_params[:one_time_keys].is_a?(Enumerable) - resource_params[:one_time_keys].each do |one_time_key_params| - device.one_time_keys.create!(one_time_key_params) - end - end - end - - render json: device, serializer: REST::Keys::DeviceSerializer - end - - private - - def resource_params - params.permit(device: [:device_id, :name, :fingerprint_key, :identity_key], one_time_keys: [:key_id, :key, :signature]) - end -end diff --git a/app/helpers/context_helper.rb b/app/helpers/context_helper.rb index a0c1781d24..18bb088b48 100644 --- a/app/helpers/context_helper.rb +++ b/app/helpers/context_helper.rb @@ -23,23 +23,6 @@ module ContextHelper indexable: { 'toot' => 'http://joinmastodon.org/ns#', 'indexable' => 'toot:indexable' }, memorial: { 'toot' => 'http://joinmastodon.org/ns#', 'memorial' => 'toot:memorial' }, voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' }, - olm: { - 'toot' => 'http://joinmastodon.org/ns#', - 'Device' => 'toot:Device', - 'Ed25519Signature' => 'toot:Ed25519Signature', - 'Ed25519Key' => 'toot:Ed25519Key', - 'Curve25519Key' => 'toot:Curve25519Key', - 'EncryptedMessage' => 'toot:EncryptedMessage', - 'publicKeyBase64' => 'toot:publicKeyBase64', - 'deviceId' => 'toot:deviceId', - 'claim' => { '@type' => '@id', '@id' => 'toot:claim' }, - 'fingerprintKey' => { '@type' => '@id', '@id' => 'toot:fingerprintKey' }, - 'identityKey' => { '@type' => '@id', '@id' => 'toot:identityKey' }, - 'devices' => { '@type' => '@id', '@id' => 'toot:devices' }, - 'messageFranking' => 'toot:messageFranking', - 'messageType' => 'toot:messageType', - 'cipherText' => 'toot:cipherText', - }, suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' }, attribution_domains: { 'toot' => 'http://joinmastodon.org/ns#', 'attributionDomains' => { '@id' => 'toot:attributionDomains', '@type' => '@id' } }, }.freeze diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index d539ce41a0..aae73e01e0 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -8,44 +8,11 @@ class ActivityPub::Activity::Create < ActivityPub::Activity dereference_object! - case @object['type'] - when 'EncryptedMessage' - create_encrypted_message - else - create_status - end + create_status end private - def create_encrypted_message - return reject_payload! if non_matching_uri_hosts?(@account.uri, object_uri) || @options[:delivered_to_account_id].blank? - - target_account = Account.find(@options[:delivered_to_account_id]) - target_device = target_account.devices.find_by(device_id: @object.dig('to', 'deviceId')) - - return if target_device.nil? - - target_device.encrypted_messages.create!( - from_account: @account, - from_device_id: @object.dig('attributedTo', 'deviceId'), - type: @object['messageType'], - body: @object['cipherText'], - digest: @object.dig('digest', 'digestValue'), - message_franking: message_franking.to_token - ) - end - - def message_franking - MessageFranking.new( - hmac: @object.dig('digest', 'digestValue'), - original_franking: @object['messageFranking'], - source_account_id: @account.id, - target_account_id: @options[:delivered_to_account_id], - timestamp: Time.now.utc - ) - end - def create_status return reject_payload! if unsupported_object_type? || non_matching_uri_hosts?(@account.uri, object_uri) || tombstone_exists? || !related_to_local_activity? diff --git a/app/lib/inline_renderer.rb b/app/lib/inline_renderer.rb index 0aebb13fce..af967ac215 100644 --- a/app/lib/inline_renderer.rb +++ b/app/lib/inline_renderer.rb @@ -20,8 +20,6 @@ class InlineRenderer serializer = REST::AnnouncementSerializer when :reaction serializer = REST::ReactionSerializer - when :encrypted_message - serializer = REST::EncryptedMessageSerializer else return end diff --git a/app/lib/vacuum/system_keys_vacuum.rb b/app/lib/vacuum/system_keys_vacuum.rb deleted file mode 100644 index ceee2fd164..0000000000 --- a/app/lib/vacuum/system_keys_vacuum.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -class Vacuum::SystemKeysVacuum - def perform - vacuum_expired_system_keys! - end - - private - - def vacuum_expired_system_keys! - SystemKey.expired.delete_all - end -end diff --git a/app/models/account.rb b/app/models/account.rb index a4cab99f7b..078d7aaa05 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -44,7 +44,6 @@ # hide_collections :boolean # avatar_storage_schema_version :integer # header_storage_schema_version :integer -# devices_url :string # suspension_origin :integer # sensitized_at :datetime # trendable :boolean @@ -56,11 +55,12 @@ class Account < ApplicationRecord self.ignored_columns += %w( - subscription_expires_at - secret + devices_url + hub_url remote_url salmon_url - hub_url + secret + subscription_expires_at trust_level ) diff --git a/app/models/concerns/account/associations.rb b/app/models/concerns/account/associations.rb index 1c67b07e51..637e785953 100644 --- a/app/models/concerns/account/associations.rb +++ b/app/models/concerns/account/associations.rb @@ -7,9 +7,6 @@ module Account::Associations # Local users has_one :user, inverse_of: :account, dependent: :destroy - # E2EE - has_many :devices, dependent: :destroy, inverse_of: :account - # Timelines has_many :statuses, inverse_of: :account, dependent: :destroy has_many :favourites, inverse_of: :account, dependent: :destroy diff --git a/app/models/device.rb b/app/models/device.rb deleted file mode 100644 index 5dc6cf1e66..0000000000 --- a/app/models/device.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: devices -# -# id :bigint(8) not null, primary key -# access_token_id :bigint(8) -# account_id :bigint(8) -# device_id :string default(""), not null -# name :string default(""), not null -# fingerprint_key :text default(""), not null -# identity_key :text default(""), not null -# created_at :datetime not null -# updated_at :datetime not null -# - -class Device < ApplicationRecord - belongs_to :access_token, class_name: 'Doorkeeper::AccessToken' - belongs_to :account - - has_many :one_time_keys, dependent: :destroy, inverse_of: :device - has_many :encrypted_messages, dependent: :destroy, inverse_of: :device - - validates :name, :fingerprint_key, :identity_key, presence: true - validates :fingerprint_key, :identity_key, ed25519_key: true - - before_save :invalidate_associations, if: -> { device_id_changed? || fingerprint_key_changed? || identity_key_changed? } - - private - - def invalidate_associations - one_time_keys.destroy_all - encrypted_messages.destroy_all - end -end diff --git a/app/models/encrypted_message.rb b/app/models/encrypted_message.rb deleted file mode 100644 index 3e7e95594c..0000000000 --- a/app/models/encrypted_message.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: encrypted_messages -# -# id :bigint(8) not null, primary key -# device_id :bigint(8) -# from_account_id :bigint(8) -# from_device_id :string default(""), not null -# type :integer default(0), not null -# body :text default(""), not null -# digest :text default(""), not null -# message_franking :text default(""), not null -# created_at :datetime not null -# updated_at :datetime not null -# - -class EncryptedMessage < ApplicationRecord - self.inheritance_column = nil - - include Paginable - include Redisable - - scope :up_to, ->(id) { where(arel_table[:id].lteq(id)) } - - belongs_to :device - belongs_to :from_account, class_name: 'Account' - - around_create Mastodon::Snowflake::Callbacks - - after_commit :push_to_streaming_api - - private - - def push_to_streaming_api - return if destroyed? || !subscribed_to_timeline? - - PushEncryptedMessageWorker.perform_async(id) - end - - def subscribed_to_timeline? - redis.exists?("subscribed:#{streaming_channel}") - end - - def streaming_channel - "timeline:#{device.account_id}:#{device.device_id}" - end -end diff --git a/app/models/message_franking.rb b/app/models/message_franking.rb deleted file mode 100644 index c72bd1ccac..0000000000 --- a/app/models/message_franking.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -class MessageFranking - attr_reader :hmac, :source_account_id, :target_account_id, - :timestamp, :original_franking - - def initialize(attributes = {}) - @hmac = attributes[:hmac] - @source_account_id = attributes[:source_account_id] - @target_account_id = attributes[:target_account_id] - @timestamp = attributes[:timestamp] - @original_franking = attributes[:original_franking] - end - - def to_token - crypt = ActiveSupport::MessageEncryptor.new(SystemKey.current_key, serializer: Oj) - crypt.encrypt_and_sign(self) - end -end diff --git a/app/models/one_time_key.rb b/app/models/one_time_key.rb deleted file mode 100644 index 23604e2f7d..0000000000 --- a/app/models/one_time_key.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: one_time_keys -# -# id :bigint(8) not null, primary key -# device_id :bigint(8) -# key_id :string default(""), not null -# key :text default(""), not null -# signature :text default(""), not null -# created_at :datetime not null -# updated_at :datetime not null -# - -class OneTimeKey < ApplicationRecord - belongs_to :device - - validates :key_id, :key, :signature, presence: true - validates :key, ed25519_key: true - validates :signature, ed25519_signature: { message: :key, verify_key: ->(one_time_key) { one_time_key.device.fingerprint_key } } -end diff --git a/app/models/system_key.rb b/app/models/system_key.rb deleted file mode 100644 index 1be399dd68..0000000000 --- a/app/models/system_key.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: system_keys -# -# id :bigint(8) not null, primary key -# key :binary -# created_at :datetime not null -# updated_at :datetime not null -# -class SystemKey < ApplicationRecord - ROTATION_PERIOD = 1.week.freeze - - before_validation :set_key - - scope :expired, ->(now = Time.now.utc) { where(arel_table[:created_at].lt(now - (ROTATION_PERIOD * 3))) } - - class << self - def current_key - previous_key = order(id: :asc).last - - if previous_key && previous_key.created_at >= ROTATION_PERIOD.ago - previous_key.key - else - create.key - end - end - end - - private - - def set_key - return if key.present? - - cipher = OpenSSL::Cipher.new('AES-256-GCM') - cipher.encrypt - - self.key = cipher.random_key - end -end diff --git a/app/serializers/activitypub/activity_serializer.rb b/app/serializers/activitypub/activity_serializer.rb index 5bdf53f032..23a5b42bc7 100644 --- a/app/serializers/activitypub/activity_serializer.rb +++ b/app/serializers/activitypub/activity_serializer.rb @@ -5,8 +5,6 @@ class ActivityPub::ActivitySerializer < ActivityPub::Serializer case model.class.name when 'Status' ActivityPub::NoteSerializer - when 'DeliverToDeviceService::EncryptedMessage' - ActivityPub::EncryptedMessageSerializer else super end diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb index a6281e23b9..f698e758e8 100644 --- a/app/serializers/activitypub/actor_serializer.rb +++ b/app/serializers/activitypub/actor_serializer.rb @@ -7,7 +7,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer context :security context_extensions :manually_approves_followers, :featured, :also_known_as, - :moved_to, :property_value, :discoverable, :olm, :suspended, + :moved_to, :property_value, :discoverable, :suspended, :memorial, :indexable, :attribution_domains attributes :id, :type, :following, :followers, @@ -21,7 +21,6 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer has_many :virtual_tags, key: :tag has_many :virtual_attachments, key: :attachment - attribute :devices, unless: :instance_actor? attribute :moved_to, if: :moved? attribute :also_known_as, if: :also_known_as? attribute :suspended, if: :suspended? @@ -72,10 +71,6 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer object.instance_actor? ? instance_actor_inbox_url : account_inbox_url(object) end - def devices - account_collection_url(object, :devices) - end - def outbox object.instance_actor? ? instance_actor_outbox_url : account_outbox_url(object) end diff --git a/app/serializers/activitypub/collection_serializer.rb b/app/serializers/activitypub/collection_serializer.rb index 34026a6b5b..1b410cecae 100644 --- a/app/serializers/activitypub/collection_serializer.rb +++ b/app/serializers/activitypub/collection_serializer.rb @@ -14,8 +14,6 @@ class ActivityPub::CollectionSerializer < ActivityPub::Serializer case model.class.name when 'Status' ActivityPub::NoteSerializer - when 'Device' - ActivityPub::DeviceSerializer when 'FeaturedTag' ActivityPub::HashtagSerializer when 'ActivityPub::CollectionPresenter' diff --git a/app/serializers/activitypub/device_serializer.rb b/app/serializers/activitypub/device_serializer.rb deleted file mode 100644 index 5f0fdc8af9..0000000000 --- a/app/serializers/activitypub/device_serializer.rb +++ /dev/null @@ -1,52 +0,0 @@ -# frozen_string_literal: true - -class ActivityPub::DeviceSerializer < ActivityPub::Serializer - context_extensions :olm - - include RoutingHelper - - class FingerprintKeySerializer < ActivityPub::Serializer - attributes :type, :public_key_base64 - - def type - 'Ed25519Key' - end - - def public_key_base64 - object.fingerprint_key - end - end - - class IdentityKeySerializer < ActivityPub::Serializer - attributes :type, :public_key_base64 - - def type - 'Curve25519Key' - end - - def public_key_base64 - object.identity_key - end - end - - attributes :device_id, :type, :name, :claim - - has_one :fingerprint_key, serializer: FingerprintKeySerializer - has_one :identity_key, serializer: IdentityKeySerializer - - def type - 'Device' - end - - def claim - account_claim_url(object.account, id: object.device_id) - end - - def fingerprint_key - object - end - - def identity_key - object - end -end diff --git a/app/serializers/activitypub/encrypted_message_serializer.rb b/app/serializers/activitypub/encrypted_message_serializer.rb deleted file mode 100644 index 3c525d23e5..0000000000 --- a/app/serializers/activitypub/encrypted_message_serializer.rb +++ /dev/null @@ -1,61 +0,0 @@ -# frozen_string_literal: true - -class ActivityPub::EncryptedMessageSerializer < ActivityPub::Serializer - context :security - - context_extensions :olm - - class DeviceSerializer < ActivityPub::Serializer - attributes :type, :device_id - - def type - 'Device' - end - - def device_id - object - end - end - - class DigestSerializer < ActivityPub::Serializer - attributes :type, :digest_algorithm, :digest_value - - def type - 'Digest' - end - - def digest_algorithm - 'http://www.w3.org/2000/09/xmldsig#hmac-sha256' - end - - def digest_value - object - end - end - - attributes :type, :message_type, :cipher_text, :message_franking - - has_one :attributed_to, serializer: DeviceSerializer - has_one :to, serializer: DeviceSerializer - has_one :digest, serializer: DigestSerializer - - def type - 'EncryptedMessage' - end - - def attributed_to - object.source_device.device_id - end - - def to - object.target_device_id - end - - def message_type - object.type - end - - def cipher_text - object.body - end -end diff --git a/app/serializers/activitypub/one_time_key_serializer.rb b/app/serializers/activitypub/one_time_key_serializer.rb deleted file mode 100644 index 5932eb5b55..0000000000 --- a/app/serializers/activitypub/one_time_key_serializer.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -class ActivityPub::OneTimeKeySerializer < ActivityPub::Serializer - context :security - - context_extensions :olm - - class SignatureSerializer < ActivityPub::Serializer - attributes :type, :signature_value - - def type - 'Ed25519Signature' - end - - def signature_value - object.signature - end - end - - attributes :key_id, :type, :public_key_base64 - - has_one :signature, serializer: SignatureSerializer - - def type - 'Curve25519Key' - end - - def public_key_base64 - object.key - end - - def signature - object - end -end diff --git a/app/serializers/rest/encrypted_message_serializer.rb b/app/serializers/rest/encrypted_message_serializer.rb deleted file mode 100644 index 80c26d060e..0000000000 --- a/app/serializers/rest/encrypted_message_serializer.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -class REST::EncryptedMessageSerializer < ActiveModel::Serializer - attributes :id, :account_id, :device_id, - :type, :body, :digest, :message_franking, - :created_at - - def id - object.id.to_s - end - - def account_id - object.from_account_id.to_s - end - - def device_id - object.from_device_id - end -end diff --git a/app/serializers/rest/keys/claim_result_serializer.rb b/app/serializers/rest/keys/claim_result_serializer.rb deleted file mode 100644 index 145044f557..0000000000 --- a/app/serializers/rest/keys/claim_result_serializer.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -class REST::Keys::ClaimResultSerializer < ActiveModel::Serializer - attributes :account_id, :device_id, :key_id, :key, :signature - - def account_id - object.account.id.to_s - end -end diff --git a/app/serializers/rest/keys/device_serializer.rb b/app/serializers/rest/keys/device_serializer.rb deleted file mode 100644 index f9b821b79c..0000000000 --- a/app/serializers/rest/keys/device_serializer.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -class REST::Keys::DeviceSerializer < ActiveModel::Serializer - attributes :device_id, :name, :identity_key, - :fingerprint_key -end diff --git a/app/serializers/rest/keys/query_result_serializer.rb b/app/serializers/rest/keys/query_result_serializer.rb deleted file mode 100644 index 8f8bdde289..0000000000 --- a/app/serializers/rest/keys/query_result_serializer.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -class REST::Keys::QueryResultSerializer < ActiveModel::Serializer - attributes :account_id - - has_many :devices, serializer: REST::Keys::DeviceSerializer - - def account_id - object.account.id.to_s - end -end diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index 1e2d614d72..a7422b5d02 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -108,7 +108,6 @@ class ActivityPub::ProcessAccountService < BaseService def set_immediate_attributes! @account.featured_collection_url = @json['featured'] || '' - @account.devices_url = @json['devices'] || '' @account.display_name = @json['name'] || '' @account.note = @json['summary'] || '' @account.locked = @json['manuallyApprovesFollowers'] || false diff --git a/app/services/delete_account_service.rb b/app/services/delete_account_service.rb index 328d8ae8f8..0c03267d43 100644 --- a/app/services/delete_account_service.rb +++ b/app/services/delete_account_service.rb @@ -13,7 +13,6 @@ class DeleteAccountService < BaseService conversation_mutes conversations custom_filters - devices domain_blocks featured_tags follow_requests @@ -40,7 +39,6 @@ class DeleteAccountService < BaseService conversation_mutes conversations custom_filters - devices domain_blocks featured_tags follow_requests diff --git a/app/services/deliver_to_device_service.rb b/app/services/deliver_to_device_service.rb deleted file mode 100644 index 71711945c0..0000000000 --- a/app/services/deliver_to_device_service.rb +++ /dev/null @@ -1,78 +0,0 @@ -# frozen_string_literal: true - -class DeliverToDeviceService < BaseService - include Payloadable - - class EncryptedMessage < ActiveModelSerializers::Model - attributes :source_account, :target_account, :source_device, - :target_device_id, :type, :body, :digest, - :message_franking - end - - def call(source_account, source_device, options = {}) - @source_account = source_account - @source_device = source_device - @target_account = Account.find(options[:account_id]) - @target_device_id = options[:device_id] - @body = options[:body] - @type = options[:type] - @hmac = options[:hmac] - - set_message_franking! - - if @target_account.local? - deliver_to_local! - else - deliver_to_remote! - end - end - - private - - def set_message_franking! - @message_franking = message_franking.to_token - end - - def deliver_to_local! - target_device = @target_account.devices.find_by!(device_id: @target_device_id) - - target_device.encrypted_messages.create!( - from_account: @source_account, - from_device_id: @source_device.device_id, - type: @type, - body: @body, - digest: @hmac, - message_franking: @message_franking - ) - end - - def deliver_to_remote! - ActivityPub::DeliveryWorker.perform_async( - Oj.dump(serialize_payload(ActivityPub::ActivityPresenter.from_encrypted_message(encrypted_message), ActivityPub::ActivitySerializer)), - @source_account.id, - @target_account.inbox_url - ) - end - - def message_franking - MessageFranking.new( - source_account_id: @source_account.id, - target_account_id: @target_account.id, - hmac: @hmac, - timestamp: Time.now.utc - ) - end - - def encrypted_message - EncryptedMessage.new( - source_account: @source_account, - target_account: @target_account, - source_device: @source_device, - target_device_id: @target_device_id, - type: @type, - body: @body, - digest: @hmac, - message_franking: @message_franking - ) - end -end diff --git a/app/services/keys/claim_service.rb b/app/services/keys/claim_service.rb deleted file mode 100644 index ebce9cce7d..0000000000 --- a/app/services/keys/claim_service.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -class Keys::ClaimService < BaseService - HEADERS = { 'Content-Type' => 'application/activity+json' }.freeze - - class Result < ActiveModelSerializers::Model - attributes :account, :device_id, :key_id, - :key, :signature - - def initialize(account, device_id, key_attributes = {}) - super( - account: account, - device_id: device_id, - key_id: key_attributes[:key_id], - key: key_attributes[:key], - signature: key_attributes[:signature], - ) - end - end - - def call(source_account, target_account_id, device_id) - @source_account = source_account - @target_account = Account.find(target_account_id) - @device_id = device_id - - if @target_account.local? - claim_local_key! - else - claim_remote_key! - end - rescue ActiveRecord::RecordNotFound - nil - end - - private - - def claim_local_key! - device = @target_account.devices.find_by(device_id: @device_id) - key = nil - - ApplicationRecord.transaction do - key = device.one_time_keys.order(Arel.sql('random()')).first! - key.destroy! - end - - @result = Result.new(@target_account, @device_id, key) - end - - def claim_remote_key! - query_result = QueryService.new.call(@target_account) - device = query_result.find(@device_id) - - return unless device.present? && device.valid_claim_url? - - json = fetch_resource_with_post(device.claim_url) - - return unless json.present? && json['publicKeyBase64'].present? - - @result = Result.new(@target_account, @device_id, key_id: json['id'], key: json['publicKeyBase64'], signature: json.dig('signature', 'signatureValue')) - rescue HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error => e - Rails.logger.debug { "Claiming one-time key for #{@target_account.acct}:#{@device_id} failed: #{e}" } - nil - end - - def fetch_resource_with_post(uri) - build_post_request(uri).perform do |response| - raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response) - - body_to_json(response.body_with_limit) if response.code == 200 - end - end - - def build_post_request(uri) - Request.new(:post, uri).tap do |request| - request.on_behalf_of(@source_account) - request.add_headers(HEADERS) - end - end -end diff --git a/app/services/keys/query_service.rb b/app/services/keys/query_service.rb deleted file mode 100644 index 33e13293f3..0000000000 --- a/app/services/keys/query_service.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -class Keys::QueryService < BaseService - include JsonLdHelper - - class Result < ActiveModelSerializers::Model - attributes :account, :devices - - def initialize(account, devices) - super( - account: account, - devices: devices || [], - ) - end - - def find(device_id) - @devices.find { |device| device.device_id == device_id } - end - end - - class Device < ActiveModelSerializers::Model - attributes :device_id, :name, :identity_key, :fingerprint_key - - def initialize(attributes = {}) - super( - device_id: attributes[:device_id], - name: attributes[:name], - identity_key: attributes[:identity_key], - fingerprint_key: attributes[:fingerprint_key], - ) - @claim_url = attributes[:claim_url] - end - - def valid_claim_url? - return false if @claim_url.blank? - - begin - parsed_url = Addressable::URI.parse(@claim_url).normalize - rescue Addressable::URI::InvalidURIError - return false - end - - %w(http https).include?(parsed_url.scheme) && parsed_url.host.present? - end - end - - def call(account) - @account = account - - if @account.local? - query_local_devices! - else - query_remote_devices! - end - - Result.new(@account, @devices) - end - - private - - def query_local_devices! - @devices = @account.devices.map { |device| Device.new(device) } - end - - def query_remote_devices! - return if @account.devices_url.blank? - - json = fetch_resource(@account.devices_url) - - return if json['items'].blank? - - @devices = as_array(json['items']).map do |device| - Device.new(device_id: device['id'], name: device['name'], identity_key: device.dig('identityKey', 'publicKeyBase64'), fingerprint_key: device.dig('fingerprintKey', 'publicKeyBase64'), claim_url: device['claim']) - end - rescue HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error => e - Rails.logger.debug { "Querying devices for #{@account.acct} failed: #{e}" } - nil - end -end diff --git a/app/validators/ed25519_key_validator.rb b/app/validators/ed25519_key_validator.rb deleted file mode 100644 index adf49296b2..0000000000 --- a/app/validators/ed25519_key_validator.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -class Ed25519KeyValidator < ActiveModel::EachValidator - def validate_each(record, attribute, value) - return if value.blank? - - key = Base64.decode64(value) - - record.errors.add(attribute, I18n.t('crypto.errors.invalid_key')) unless verified?(key) - end - - private - - def verified?(key) - Ed25519.validate_key_bytes(key) - rescue ArgumentError - false - end -end diff --git a/app/validators/ed25519_signature_validator.rb b/app/validators/ed25519_signature_validator.rb deleted file mode 100644 index 0e74c231ec..0000000000 --- a/app/validators/ed25519_signature_validator.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -class Ed25519SignatureValidator < ActiveModel::EachValidator - def validate_each(record, attribute, value) - return if value.blank? - - verify_key = Ed25519::VerifyKey.new(Base64.decode64(option_to_value(record, :verify_key))) - signature = Base64.decode64(value) - message = option_to_value(record, :message) - - record.errors.add(attribute, I18n.t('crypto.errors.invalid_signature')) unless verified?(verify_key, signature, message) - end - - private - - def verified?(verify_key, signature, message) - verify_key.verify(signature, message) - rescue Ed25519::VerifyError, ArgumentError - false - end - - def option_to_value(record, key) - if options[key].is_a?(Proc) - options[key].call(record) - else - record.public_send(options[key]) - end - end -end diff --git a/app/workers/push_encrypted_message_worker.rb b/app/workers/push_encrypted_message_worker.rb deleted file mode 100644 index 6bee12675b..0000000000 --- a/app/workers/push_encrypted_message_worker.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -class PushEncryptedMessageWorker - include Sidekiq::Worker - include Redisable - - def perform(encrypted_message_id) - encrypted_message = EncryptedMessage.find(encrypted_message_id) - message = InlineRenderer.render(encrypted_message, nil, :encrypted_message) - timeline_id = "timeline:#{encrypted_message.device.account_id}:#{encrypted_message.device.device_id}" - - redis.publish(timeline_id, Oj.dump(event: :encrypted_message, payload: message)) - rescue ActiveRecord::RecordNotFound - true - end -end diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index 86fde3cacf..b47e76c08b 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -118,8 +118,7 @@ Doorkeeper.configure do :'admin:write:domain_blocks', :'admin:write:ip_blocks', :'admin:write:email_domain_blocks', - :'admin:write:canonical_email_blocks', - :crypto + :'admin:write:canonical_email_blocks' # Change the way client credentials are retrieved from the request object. # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index ba459e19f2..2b0563f4d3 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -19,7 +19,6 @@ ActiveSupport::Inflector.inflections(:en) do |inflect| inflect.acronym 'CLI' inflect.acronym 'DeepL' inflect.acronym 'DSL' - inflect.acronym 'Ed25519' inflect.acronym 'JsonLd' inflect.acronym 'OEmbed' inflect.acronym 'OStatus' diff --git a/config/locales/an.yml b/config/locales/an.yml index 589bb39836..2f181e0757 100644 --- a/config/locales/an.yml +++ b/config/locales/an.yml @@ -953,7 +953,6 @@ an: crypto: errors: invalid_key: no ye una clau Ed25519 u Curve25519 valida - invalid_signature: no ye una sinyatura Ed25519 valida date: formats: default: "%d %b %Y" diff --git a/config/locales/ar.yml b/config/locales/ar.yml index 7512e03fd5..81705acba0 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -1178,7 +1178,6 @@ ar: crypto: errors: invalid_key: ليس بمفتاح Ed25519 أو Curve25519 صالح - invalid_signature: ليس بتوقيع Ed25519 صالح date: formats: default: "%d %b %Y" diff --git a/config/locales/ast.yml b/config/locales/ast.yml index be3441507e..a7d4a9917e 100644 --- a/config/locales/ast.yml +++ b/config/locales/ast.yml @@ -486,7 +486,6 @@ ast: crypto: errors: invalid_key: nun ye una clave ed25519 o curve25519 válida - invalid_signature: nun ye una clave ed25519 válida datetime: distance_in_words: about_x_hours: "%{count} h" diff --git a/config/locales/be.yml b/config/locales/be.yml index 48ca5751cc..d45f443b75 100644 --- a/config/locales/be.yml +++ b/config/locales/be.yml @@ -1189,7 +1189,6 @@ be: crypto: errors: invalid_key: гэта не сапраўдны Ed25519 або Curve25519 ключ - invalid_signature: гэта не сапраўдная Ed25519 сігнатура date: formats: default: "%d.%m.%Y" diff --git a/config/locales/bg.yml b/config/locales/bg.yml index 604eeca480..e2c246827a 100644 --- a/config/locales/bg.yml +++ b/config/locales/bg.yml @@ -1115,7 +1115,6 @@ bg: crypto: errors: invalid_key: не е валиден ключ Ed25519 или Curve25519 - invalid_signature: не е валиден подпис Ed25519 date: formats: default: "%b %d, %Y" diff --git a/config/locales/ca.yml b/config/locales/ca.yml index d985a2ac42..33a1d4e88f 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -1171,7 +1171,6 @@ ca: crypto: errors: invalid_key: no és una clau Ed25519 o Curve25519 vàlida - invalid_signature: no és una signatura Ed25519 vàlida date: formats: default: "%b %d, %Y" diff --git a/config/locales/ckb.yml b/config/locales/ckb.yml index 8af3d86388..bc668d2ce4 100644 --- a/config/locales/ckb.yml +++ b/config/locales/ckb.yml @@ -608,7 +608,6 @@ ckb: crypto: errors: invalid_key: کلیلی باوڕپێکراو Ed25519 یان Curve25519 دروست نییە - invalid_signature: واژووی Ed25519 بڕوادار نییە date: formats: default: "%b %d, %Y" diff --git a/config/locales/co.yml b/config/locales/co.yml index b072e5e4ef..58ddd7d01b 100644 --- a/config/locales/co.yml +++ b/config/locales/co.yml @@ -569,7 +569,6 @@ co: crypto: errors: invalid_key: ùn hè micca una chjave Ed25519 o Curve25519 valida - invalid_signature: ùn hè micca una firma Ed25519 valida date: formats: default: "%d %b %Y" diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 1000442870..b7fc8ab1b0 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -1149,7 +1149,6 @@ cs: crypto: errors: invalid_key: není platný klíč Ed25519 nebo Curve25519 - invalid_signature: není platný podpis typu Ed25519 date: formats: default: "%d. %b %Y" diff --git a/config/locales/cy.yml b/config/locales/cy.yml index 9d3c0c82f3..c2c193d943 100644 --- a/config/locales/cy.yml +++ b/config/locales/cy.yml @@ -1246,7 +1246,6 @@ cy: crypto: errors: invalid_key: ddim yn allwedd Ed25519 na Curve25519 dilys - invalid_signature: ddim yn llofnod Ed25519 dilys date: formats: default: "%b %d %Y" diff --git a/config/locales/da.yml b/config/locales/da.yml index 6f781742a8..a177b97de7 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -1174,7 +1174,6 @@ da: crypto: errors: invalid_key: er ikke en gyldig Ed25519- eller Curve25519-nøgle - invalid_signature: er ikke en gylidig Ed25519-signatur date: formats: default: "%d. %b %Y" diff --git a/config/locales/de.yml b/config/locales/de.yml index 040ddaaf69..85e24c230e 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -1174,7 +1174,6 @@ de: crypto: errors: invalid_key: ist kein gültiger Ed25519- oder Curve25519-Schlüssel - invalid_signature: ist keine gültige Ed25519-Signatur date: formats: default: "%d. %b %Y" diff --git a/config/locales/el.yml b/config/locales/el.yml index 1f408e26ea..0da957cdb2 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -1128,7 +1128,6 @@ el: crypto: errors: invalid_key: δεν είναι έγκυρο κλειδί Ed25519 ή Curve25519 - invalid_signature: δεν είναι έγκυρη υπογραφή Ed25519 date: formats: default: "%b %d, %Y" diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 56255f5d7a..577978ce88 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -1174,7 +1174,6 @@ en-GB: crypto: errors: invalid_key: is not a valid Ed25519 or Curve25519 key - invalid_signature: is not a valid Ed25519 signature date: formats: default: "%b %d, %Y" diff --git a/config/locales/en.yml b/config/locales/en.yml index b1c100da0c..625f53f2e5 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1174,7 +1174,6 @@ en: crypto: errors: invalid_key: is not a valid Ed25519 or Curve25519 key - invalid_signature: is not a valid Ed25519 signature date: formats: default: "%b %d, %Y" diff --git a/config/locales/eo.yml b/config/locales/eo.yml index 46c6cbcf86..7ebf074819 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -1041,7 +1041,6 @@ eo: crypto: errors: invalid_key: 올바른 Ed25519 혹은 Curve25519 키가 아닙니다 - invalid_signature: 올바른 Ed25519 시그니처가 아닙니다 date: formats: default: "%Y-%b-%d" diff --git a/config/locales/es-AR.yml b/config/locales/es-AR.yml index 4d60d080a2..9f1dc46c9d 100644 --- a/config/locales/es-AR.yml +++ b/config/locales/es-AR.yml @@ -1174,7 +1174,6 @@ es-AR: crypto: errors: invalid_key: no es una clave Ed25519 o Curve25519 válida - invalid_signature: no es una firma Ed25519 válida date: formats: default: "%d de %b de %Y" diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml index 1ea346a00c..b39b23c617 100644 --- a/config/locales/es-MX.yml +++ b/config/locales/es-MX.yml @@ -1174,7 +1174,6 @@ es-MX: crypto: errors: invalid_key: no es una clave Ed25519 o Curve25519 válida - invalid_signature: no es una firma Ed25519 válida date: formats: default: "%d %b %Y" diff --git a/config/locales/es.yml b/config/locales/es.yml index c652876f3a..2815ada779 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1174,7 +1174,6 @@ es: crypto: errors: invalid_key: no es una clave Ed25519 o Curve25519 válida - invalid_signature: no es una firma Ed25519 válida date: formats: default: "%d %b %Y" diff --git a/config/locales/et.yml b/config/locales/et.yml index aca4f93c4d..60f98a471e 100644 --- a/config/locales/et.yml +++ b/config/locales/et.yml @@ -1174,7 +1174,6 @@ et: crypto: errors: invalid_key: ei ole õige Ed25519 ega Curve25519 võti - invalid_signature: ei ole õige Ed25519 allkiri date: formats: default: "%d. %b %Y" diff --git a/config/locales/eu.yml b/config/locales/eu.yml index 6260033990..7c7f995a71 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -1091,7 +1091,6 @@ eu: crypto: errors: invalid_key: ez da baliozko Ed25519 edo Curve25519 gakoa - invalid_signature: ez da baliozko Ed25519 sinadura date: formats: default: "%Y(e)ko %b %d" diff --git a/config/locales/fa.yml b/config/locales/fa.yml index 996c8d6cd2..c2c22af4c4 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -971,7 +971,6 @@ fa: crypto: errors: invalid_key: یک کلید معتبر Ed25519 یا Curve25519 نیست - invalid_signature: یک امضای معتبر Ed25519 نیست date: formats: default: "%d %b %Y" diff --git a/config/locales/fi.yml b/config/locales/fi.yml index b48b499bbe..5df82bff9f 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -1174,7 +1174,6 @@ fi: crypto: errors: invalid_key: ei ole kelvollinen Ed25519- tai Curve25519-avain - invalid_signature: ei ole kelvollinen Ed25519-allekirjoitus date: formats: default: "%b %d, %Y" diff --git a/config/locales/fo.yml b/config/locales/fo.yml index 266b73bb10..ce21aa3be7 100644 --- a/config/locales/fo.yml +++ b/config/locales/fo.yml @@ -1174,7 +1174,6 @@ fo: crypto: errors: invalid_key: er ikki ein gildur Ed25519 ella Curve25519 lykil - invalid_signature: er ikki ein gildug Ed25519 undirskrift date: formats: default: "%b %d, %Y" diff --git a/config/locales/fr-CA.yml b/config/locales/fr-CA.yml index b70a515fd1..3f9252ffab 100644 --- a/config/locales/fr-CA.yml +++ b/config/locales/fr-CA.yml @@ -1175,7 +1175,6 @@ fr-CA: crypto: errors: invalid_key: n’est pas une clé Ed25519 ou Curve25519 valide - invalid_signature: n’est pas une signature Ed25519 valide date: formats: default: "%d %b %Y" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index bc616d2896..cb76ae2243 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -1175,7 +1175,6 @@ fr: crypto: errors: invalid_key: n’est pas une clé Ed25519 ou Curve25519 valide - invalid_signature: n’est pas une signature Ed25519 valide date: formats: default: "%d %b %Y" diff --git a/config/locales/fy.yml b/config/locales/fy.yml index 6afdecd556..8b854494d4 100644 --- a/config/locales/fy.yml +++ b/config/locales/fy.yml @@ -1164,7 +1164,6 @@ fy: crypto: errors: invalid_key: is gjin jildige Ed25519- of Curve25519-kaai - invalid_signature: is gjin jildige Ed25519-hantekening date: formats: default: "%d %b %Y" diff --git a/config/locales/ga.yml b/config/locales/ga.yml index 1071871c95..a6369354cd 100644 --- a/config/locales/ga.yml +++ b/config/locales/ga.yml @@ -1228,7 +1228,6 @@ ga: crypto: errors: invalid_key: nach eochair bhailí Ed25519 nó Curve25519 í - invalid_signature: nach síniú bailí Ed25519 é date: formats: default: "%b %d, %Y" diff --git a/config/locales/gd.yml b/config/locales/gd.yml index 5e63b5bd23..b5cbc4a73e 100644 --- a/config/locales/gd.yml +++ b/config/locales/gd.yml @@ -1210,7 +1210,6 @@ gd: crypto: errors: invalid_key: "– chan e iuchair Ed25519 no Curve25519 dhligheach a th’ ann" - invalid_signature: "– chan e soidhneadh Ed25519 dligheach a th’ ann" date: formats: default: "%d %b %Y" diff --git a/config/locales/gl.yml b/config/locales/gl.yml index 58fd2d9bab..9813514a7b 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -1174,7 +1174,6 @@ gl: crypto: errors: invalid_key: non é unha chave Ed25519 ou Curve25519 válida - invalid_signature: non é unha sinatura Ed25519 válida date: formats: default: "%d %b, %Y" diff --git a/config/locales/he.yml b/config/locales/he.yml index 7a2d0a1d98..13a1f6f05d 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -1210,7 +1210,6 @@ he: crypto: errors: invalid_key: זהו לא מפתח Ed25519 או Curve25519 קביל - invalid_signature: היא לא חתימת Ed25519 קבילה date: formats: default: "%b %d, %Y" diff --git a/config/locales/hu.yml b/config/locales/hu.yml index 10c7506b01..9767c48834 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -1174,7 +1174,6 @@ hu: crypto: errors: invalid_key: érvénytelen Ed25519 vagy Curve25519 kulcs - invalid_signature: érvénytelen Ed25519 aláírás date: formats: default: "%Y. %b %d." diff --git a/config/locales/hy.yml b/config/locales/hy.yml index 80dbc77991..1fda020c07 100644 --- a/config/locales/hy.yml +++ b/config/locales/hy.yml @@ -495,7 +495,6 @@ hy: crypto: errors: invalid_key: անվաւեր Ed25519 կամ Curve25519 բանալի - invalid_signature: անվաւեր Ed25519 բանալի date: formats: default: "%b %d, %Y" diff --git a/config/locales/ia.yml b/config/locales/ia.yml index 957bae3991..6632af061e 100644 --- a/config/locales/ia.yml +++ b/config/locales/ia.yml @@ -1158,7 +1158,6 @@ ia: crypto: errors: invalid_key: non es un clave Ed25519 o Curve25519 valide - invalid_signature: non es un signatura Ed25519 valide date: formats: default: "%d %b %Y" diff --git a/config/locales/id.yml b/config/locales/id.yml index 222d2b5680..96a5022a7c 100644 --- a/config/locales/id.yml +++ b/config/locales/id.yml @@ -936,7 +936,6 @@ id: crypto: errors: invalid_key: bukan kunci Ed25519 atau Curve25519 yang valid - invalid_signature: bukan tanda tangan Ed25519 yang valid date: formats: default: "%d %b %Y" diff --git a/config/locales/ie.yml b/config/locales/ie.yml index 6a79686f48..513a8eda7e 100644 --- a/config/locales/ie.yml +++ b/config/locales/ie.yml @@ -1089,7 +1089,6 @@ ie: crypto: errors: invalid_key: ne es un valid clave Ed25519 o Curve25519 - invalid_signature: ne es un valid signatura Ed25519 date: formats: default: "%d.%m.%Y" diff --git a/config/locales/io.yml b/config/locales/io.yml index dbbe228e72..97cc417df0 100644 --- a/config/locales/io.yml +++ b/config/locales/io.yml @@ -1064,7 +1064,6 @@ io: crypto: errors: invalid_key: ne esas valida klefo Ed25519 o Curve25519 - invalid_signature: ne esas valida parafo Ed25519 date: formats: default: "%d %b, %Y" diff --git a/config/locales/is.yml b/config/locales/is.yml index 78dfd6048f..590805ad30 100644 --- a/config/locales/is.yml +++ b/config/locales/is.yml @@ -1178,7 +1178,6 @@ is: crypto: errors: invalid_key: er ekki gildur Ed25519 eða Curve25519-lykill - invalid_signature: er ekki gild Ed25519 undirritun date: formats: default: "%d. %b, %Y" diff --git a/config/locales/it.yml b/config/locales/it.yml index 792add14e3..fe6fec17d7 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -1176,7 +1176,6 @@ it: crypto: errors: invalid_key: non è una chiave Ed25519 o Curve25519 valida - invalid_signature: non è una firma Ed25519 valida date: formats: default: "%d %b %Y" diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 13f59e981b..ed6293e1ca 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -1146,7 +1146,6 @@ ja: crypto: errors: invalid_key: 有効なEd25519またはCurve25519キーではありません - invalid_signature: 有効なEd25519署名ではありません date: formats: default: "%Y年%m月%d日" diff --git a/config/locales/ko.yml b/config/locales/ko.yml index cbcc09a4da..3217a8e49a 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -1151,7 +1151,6 @@ ko: crypto: errors: invalid_key: 유효하지 않은 Ed25519 또는 Curve25519 키 - invalid_signature: 유효하지 않은 Ed25519 서명 date: formats: default: "%Y-%m-%d" diff --git a/config/locales/ku.yml b/config/locales/ku.yml index 6b80e32ba8..08843f645e 100644 --- a/config/locales/ku.yml +++ b/config/locales/ku.yml @@ -950,7 +950,6 @@ ku: crypto: errors: invalid_key: ed25519 ne derbasdare ne jî Curve25519 kilîta - invalid_signature: Ed25519 ne îmzeyek derbasdar e date: formats: default: "%b%d%Y" diff --git a/config/locales/lad.yml b/config/locales/lad.yml index 2f5eb1553e..3d1f1a61e2 100644 --- a/config/locales/lad.yml +++ b/config/locales/lad.yml @@ -1122,7 +1122,6 @@ lad: crypto: errors: invalid_key: no es una yave Ed25519 o Curve25519 valida - invalid_signature: no es una firma Ed25519 valida date: formats: default: "%d %b %Y" diff --git a/config/locales/lv.yml b/config/locales/lv.yml index 88faeae82b..be6555a386 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -1140,7 +1140,6 @@ lv: crypto: errors: invalid_key: nav derīga Ed25519 vai Curve25519 atslēga - invalid_signature: nav derīgs Ed25519 paraksts date: formats: default: "%b %d, %Y" diff --git a/config/locales/ms.yml b/config/locales/ms.yml index 39c695a539..0c0ffb69bf 100644 --- a/config/locales/ms.yml +++ b/config/locales/ms.yml @@ -1051,7 +1051,6 @@ ms: crypto: errors: invalid_key: bukan kunci Ed25519 atau Curve25519 yang sah - invalid_signature: bukan tandatangan Ed25519 yang sah date: formats: default: "%b %d, %Y" diff --git a/config/locales/my.yml b/config/locales/my.yml index 92464523a0..6acaa34cdc 100644 --- a/config/locales/my.yml +++ b/config/locales/my.yml @@ -1044,7 +1044,6 @@ my: crypto: errors: invalid_key: မှန်ကန်သော Ed25519 သို့မဟုတ် Curve25519 ကီး မဟုတ်ပါ။ - invalid_signature: မှန်ကန်သော Ed25519 လက်မှတ်မဟုတ်ပါ date: formats: default: "%b %d, %Y" diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 63656991a8..afc4652bf6 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -1174,7 +1174,6 @@ nl: crypto: errors: invalid_key: is geen geldige Ed25519- of Curve25519-sleutel - invalid_signature: is geen geldige Ed25519-handtekening date: formats: default: "%d %b %Y" diff --git a/config/locales/nn.yml b/config/locales/nn.yml index b7beeb4263..dcf571a792 100644 --- a/config/locales/nn.yml +++ b/config/locales/nn.yml @@ -1174,7 +1174,6 @@ nn: crypto: errors: invalid_key: er ikkje ein gild Ed25519 eller Curve25519 nykel - invalid_signature: er ikkje ein gild Ed25519-signatur date: formats: default: "%d. %b, %Y" diff --git a/config/locales/no.yml b/config/locales/no.yml index 635ceedde4..1f0b6bacce 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -1083,7 +1083,6 @@ crypto: errors: invalid_key: er ikke en gyldig Ed25519- eller Curve25519-nøkkel - invalid_signature: er ikke en gyldig Ed25519-signatur date: formats: default: "%d. %b, %Y" diff --git a/config/locales/pl.yml b/config/locales/pl.yml index f0d09cb2d3..314adf885f 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -1210,7 +1210,6 @@ pl: crypto: errors: invalid_key: nie jest prawidłowym kluczem Ed25519 lub Curve25519 - invalid_signature: nie jest prawidłowym podpisem Ed25519 date: formats: default: "%d. %b %Y" diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index d1140f3645..7742a80569 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -1174,7 +1174,6 @@ pt-BR: crypto: errors: invalid_key: não é uma chave Ed25519 ou Curve25519 válida - invalid_signature: não é uma assinatura Ed25519 válida date: formats: default: "%d %b, %Y" diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml index f155490eb5..d6c5c4a6ff 100644 --- a/config/locales/pt-PT.yml +++ b/config/locales/pt-PT.yml @@ -1174,7 +1174,6 @@ pt-PT: crypto: errors: invalid_key: não é uma chave Ed25519 ou Curve25519 válida - invalid_signature: não é uma assinatura Ed25519 válida date: formats: default: "%d %b %Y" diff --git a/config/locales/ru.yml b/config/locales/ru.yml index d66dded89b..f1198c3f16 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -1129,7 +1129,6 @@ ru: crypto: errors: invalid_key: не является допустимым Ed25519 или Curve25519 ключом - invalid_signature: не является допустимой Ed25519 подписью date: formats: default: "%d %b %Y" diff --git a/config/locales/sc.yml b/config/locales/sc.yml index 435749f470..780270ddf1 100644 --- a/config/locales/sc.yml +++ b/config/locales/sc.yml @@ -693,7 +693,6 @@ sc: crypto: errors: invalid_key: no est una crae Ed25519 o Curve25519 vàlida - invalid_signature: no est una firma Ed25519 vàlida date: formats: default: "%d %b, %Y" diff --git a/config/locales/sco.yml b/config/locales/sco.yml index 70143a968e..97eaa21ed6 100644 --- a/config/locales/sco.yml +++ b/config/locales/sco.yml @@ -940,7 +940,6 @@ sco: crypto: errors: invalid_key: isnae a valid Ed25519 or Curve25519 key - invalid_signature: isnae a valid Ed25519 signature date: formats: default: "%b %d, %Y" diff --git a/config/locales/si.yml b/config/locales/si.yml index 135a99ceb2..8460de01da 100644 --- a/config/locales/si.yml +++ b/config/locales/si.yml @@ -829,7 +829,6 @@ si: crypto: errors: invalid_key: වලංගු Ed25519 හෝ Curve25519 යතුරක් නොවේ - invalid_signature: වලංගු Ed25519 අත්සනක් නොවේ date: formats: default: "%Y %b %d" diff --git a/config/locales/sl.yml b/config/locales/sl.yml index ef6d00b8d3..c8e806bf35 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -1196,7 +1196,6 @@ sl: crypto: errors: invalid_key: ni veljaven ključ Ed25519 ali Curve25519 - invalid_signature: ni veljaven podpis Ed25519 date: formats: default: "%d %b %Y" diff --git a/config/locales/sq.yml b/config/locales/sq.yml index 70d20592a5..294c8a888f 100644 --- a/config/locales/sq.yml +++ b/config/locales/sq.yml @@ -1166,7 +1166,6 @@ sq: crypto: errors: invalid_key: s’është kyç Ed25519 ose Curve25519 i vlefshëm - invalid_signature: s’është nënshkrim Ed25519 i vlefshëm date: formats: default: "%d %b, %Y" diff --git a/config/locales/sr-Latn.yml b/config/locales/sr-Latn.yml index 91f0933398..bfb52c275b 100644 --- a/config/locales/sr-Latn.yml +++ b/config/locales/sr-Latn.yml @@ -1110,7 +1110,6 @@ sr-Latn: crypto: errors: invalid_key: nije validan Ed25519 ili Curve25519 ključ - invalid_signature: nije validan Ed25519 potpis date: formats: default: "%d. %b. %Y." diff --git a/config/locales/sr.yml b/config/locales/sr.yml index 67aee931be..af7e7ab8d6 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -1140,7 +1140,6 @@ sr: crypto: errors: invalid_key: није валидан Ed25519 или Curve25519 кључ - invalid_signature: није валидан Ed25519 потпис date: formats: default: "%d. %b. %Y." diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 99b7ec9b3a..6146cfc8d7 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -1128,7 +1128,6 @@ sv: crypto: errors: invalid_key: är inte en giltig Ed25519 eller Curve25519 nyckel - invalid_signature: är inte en giltig Ed25519 signatur date: formats: default: "%b %d, %Y" diff --git a/config/locales/ta.yml b/config/locales/ta.yml index 3a98b6a25d..56b2895c74 100644 --- a/config/locales/ta.yml +++ b/config/locales/ta.yml @@ -188,7 +188,6 @@ ta: crypto: errors: invalid_key: ஒரு முறையான Ed25519 அல்லது Curve25519 key அல்ல - invalid_signature: ஒரு முறையான Ed25519 அடையாளம் அல்ல filters: index: empty: தடுப்புகள் ஏதும் இல்லை. diff --git a/config/locales/th.yml b/config/locales/th.yml index 65424f4eb6..d56385f261 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -1156,7 +1156,6 @@ th: crypto: errors: invalid_key: ไม่ใช่กุญแจ Ed25519 หรือ Curve25519 ที่ถูกต้อง - invalid_signature: ไม่ใช่ลายเซ็น Ed25519 ที่ถูกต้อง date: formats: default: "%d %b %Y" diff --git a/config/locales/tr.yml b/config/locales/tr.yml index d6ca6b4276..16dd4c899d 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -1174,7 +1174,6 @@ tr: crypto: errors: invalid_key: geçerli bir Ed25519 veya Curve25519 anahtarı değil - invalid_signature: geçerli bir Ed25519 imzası değil date: formats: default: "%d %b %Y" diff --git a/config/locales/uk.yml b/config/locales/uk.yml index e8c4e68998..0ef08a1555 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -1210,7 +1210,6 @@ uk: crypto: errors: invalid_key: не є припустимим ключем Ed25519 або Curve25519 - invalid_signature: не є дійсним підписом Ed25519 date: formats: default: "%b %d, %Y" diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 8f047a2cb7..98696aef7c 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -1156,7 +1156,6 @@ vi: crypto: errors: invalid_key: không phải là mã khóa Ed25519 hoặc Curve25519 đúng - invalid_signature: không phải là chữ ký số Ed25519 đúng date: formats: default: "%-d %B, %Y" diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index 277785f683..8b34da076a 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -1156,7 +1156,6 @@ zh-CN: crypto: errors: invalid_key: 不是有效的 Ed25519 或者 Curve25519 密钥 - invalid_signature: 不是有效的 Ed25519 签名 date: formats: default: "%Y年%m月%d日" diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml index 7682712759..598c65d049 100644 --- a/config/locales/zh-HK.yml +++ b/config/locales/zh-HK.yml @@ -1071,7 +1071,6 @@ zh-HK: crypto: errors: invalid_key: 不是一個有效的 Ed25519 或 Curve25519 密鑰 - invalid_signature: 不是一個有效的 Ed25519 簽名 date: formats: default: "%Y年%b月%d日" diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 35f000b601..41c4c8a534 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -1158,7 +1158,6 @@ zh-TW: crypto: errors: invalid_key: 這不是一把有效的 Ed25519 或 Curve25519 金鑰 - invalid_signature: 這不是有效的 Ed25519 簽章 date: formats: default: "%Y年%b月%d日" diff --git a/config/routes.rb b/config/routes.rb index 3ac619519d..ad607e537b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -134,7 +134,6 @@ Rails.application.routes.draw do scope module: :activitypub do resource :outbox, only: [:show] resource :inbox, only: [:create] - resource :claim, only: [:create] resources :collections, only: [:show] resource :followers_synchronization, only: [:show] end diff --git a/config/routes/api.rb b/config/routes/api.rb index b237791736..39c699b9a3 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -68,23 +68,6 @@ namespace :api, format: false do end end - # namespace :crypto do - # resources :deliveries, only: :create - - # namespace :keys do - # resource :upload, only: [:create] - # resource :query, only: [:create] - # resource :claim, only: [:create] - # resource :count, only: [:show] - # end - - # resources :encrypted_messages, only: [:index] do - # collection do - # post :clear - # end - # end - # end - resources :conversations, only: [:index, :destroy] do member do post :read diff --git a/db/migrate/20240720140205_drop_end_to_end_message_tables.rb b/db/migrate/20240720140205_drop_end_to_end_message_tables.rb new file mode 100644 index 0000000000..dd5662885c --- /dev/null +++ b/db/migrate/20240720140205_drop_end_to_end_message_tables.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class DropEndToEndMessageTables < ActiveRecord::Migration[7.1] + def up + drop_table :system_keys + drop_table :one_time_keys + drop_table :encrypted_messages + drop_table :devices + safety_assured { remove_column :accounts, :devices_url } + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/schema.rb b/db/schema.rb index 540a971333..e9aa78c205 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -193,7 +193,6 @@ ActiveRecord::Schema[7.1].define(version: 2024_09_09_014637) do t.boolean "hide_collections" t.integer "avatar_storage_schema_version" t.integer "header_storage_schema_version" - t.string "devices_url" t.datetime "sensitized_at", precision: nil t.integer "suspension_origin" t.boolean "trendable" @@ -412,19 +411,6 @@ ActiveRecord::Schema[7.1].define(version: 2024_09_09_014637) do t.index ["account_id"], name: "index_custom_filters_on_account_id" end - create_table "devices", force: :cascade do |t| - t.bigint "access_token_id" - t.bigint "account_id" - t.string "device_id", default: "", null: false - t.string "name", default: "", null: false - t.text "fingerprint_key", default: "", null: false - t.text "identity_key", default: "", null: false - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.index ["access_token_id"], name: "index_devices_on_access_token_id" - t.index ["account_id"], name: "index_devices_on_account_id" - end - create_table "domain_allows", force: :cascade do |t| t.string "domain", default: "", null: false t.datetime "created_at", precision: nil, null: false @@ -454,20 +440,6 @@ ActiveRecord::Schema[7.1].define(version: 2024_09_09_014637) do t.index ["domain"], name: "index_email_domain_blocks_on_domain", unique: true end - create_table "encrypted_messages", id: :bigint, default: -> { "timestamp_id('encrypted_messages'::text)" }, force: :cascade do |t| - t.bigint "device_id" - t.bigint "from_account_id" - t.string "from_device_id", default: "", null: false - t.integer "type", default: 0, null: false - t.text "body", default: "", null: false - t.text "digest", default: "", null: false - t.text "message_franking", default: "", null: false - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.index ["device_id"], name: "index_encrypted_messages_on_device_id" - t.index ["from_account_id"], name: "index_encrypted_messages_on_from_account_id" - end - create_table "favourites", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false @@ -781,17 +753,6 @@ ActiveRecord::Schema[7.1].define(version: 2024_09_09_014637) do t.index ["uid"], name: "index_oauth_applications_on_uid", unique: true end - create_table "one_time_keys", force: :cascade do |t| - t.bigint "device_id" - t.string "key_id", default: "", null: false - t.text "key", default: "", null: false - t.text "signature", default: "", null: false - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.index ["device_id"], name: "index_one_time_keys_on_device_id" - t.index ["key_id"], name: "index_one_time_keys_on_key_id" - end - create_table "pghero_space_stats", force: :cascade do |t| t.text "database" t.text "schema" @@ -1104,12 +1065,6 @@ ActiveRecord::Schema[7.1].define(version: 2024_09_09_014637) do t.index ["status_id"], name: "index_statuses_tags_on_status_id" end - create_table "system_keys", force: :cascade do |t| - t.binary "key" - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - end - create_table "tag_follows", force: :cascade do |t| t.bigint "tag_id", null: false t.bigint "account_id", null: false @@ -1306,11 +1261,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_09_09_014637) do add_foreign_key "custom_filter_statuses", "custom_filters", on_delete: :cascade add_foreign_key "custom_filter_statuses", "statuses", on_delete: :cascade add_foreign_key "custom_filters", "accounts", on_delete: :cascade - add_foreign_key "devices", "accounts", on_delete: :cascade - add_foreign_key "devices", "oauth_access_tokens", column: "access_token_id", on_delete: :cascade add_foreign_key "email_domain_blocks", "email_domain_blocks", column: "parent_id", on_delete: :cascade - add_foreign_key "encrypted_messages", "accounts", column: "from_account_id", on_delete: :cascade - add_foreign_key "encrypted_messages", "devices", on_delete: :cascade add_foreign_key "favourites", "accounts", name: "fk_5eb6c2b873", on_delete: :cascade add_foreign_key "favourites", "statuses", name: "fk_b0e856845e", on_delete: :cascade add_foreign_key "featured_tags", "accounts", on_delete: :cascade @@ -1353,7 +1304,6 @@ ActiveRecord::Schema[7.1].define(version: 2024_09_09_014637) do add_foreign_key "oauth_access_tokens", "oauth_applications", column: "application_id", name: "fk_f5fc4c1ee3", on_delete: :cascade add_foreign_key "oauth_access_tokens", "users", column: "resource_owner_id", name: "fk_e84df68546", on_delete: :cascade add_foreign_key "oauth_applications", "users", column: "owner_id", name: "fk_b0988c7c0a", on_delete: :cascade - add_foreign_key "one_time_keys", "devices", on_delete: :cascade add_foreign_key "poll_votes", "accounts", on_delete: :cascade add_foreign_key "poll_votes", "polls", on_delete: :cascade add_foreign_key "polls", "accounts", on_delete: :cascade diff --git a/spec/controllers/activitypub/claims_controller_spec.rb b/spec/controllers/activitypub/claims_controller_spec.rb deleted file mode 100644 index e887be2cbe..0000000000 --- a/spec/controllers/activitypub/claims_controller_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe ActivityPub::ClaimsController do - let(:account) { Fabricate(:account) } - - describe 'POST #create' do - context 'without signature' do - before do - post :create, params: { account_username: account.username }, body: '{}' - end - - it 'returns http not authorized' do - expect(response).to have_http_status(401) - end - end - end -end diff --git a/spec/fabricators/device_fabricator.rb b/spec/fabricators/device_fabricator.rb deleted file mode 100644 index 37a2e8977d..0000000000 --- a/spec/fabricators/device_fabricator.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -Fabricator(:device) do - access_token { Fabricate.build(:access_token) } - account { Fabricate.build(:account) } - device_id { Faker::Number.number(digits: 5) } - name { Faker::App.name } - fingerprint_key { Base64.strict_encode64(Ed25519::SigningKey.generate.verify_key.to_bytes) } - identity_key { Base64.strict_encode64(Ed25519::SigningKey.generate.verify_key.to_bytes) } -end diff --git a/spec/fabricators/encrypted_message_fabricator.rb b/spec/fabricators/encrypted_message_fabricator.rb deleted file mode 100644 index 349b659c2f..0000000000 --- a/spec/fabricators/encrypted_message_fabricator.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -Fabricator(:encrypted_message) do - device { Fabricate.build(:device) } - from_account { Fabricate.build(:account) } - from_device_id { Faker::Number.number(digits: 5) } -end diff --git a/spec/fabricators/one_time_key_fabricator.rb b/spec/fabricators/one_time_key_fabricator.rb deleted file mode 100644 index 505282e05d..0000000000 --- a/spec/fabricators/one_time_key_fabricator.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -Fabricator(:one_time_key) do - device { Fabricate.build(:device) } - key_id { Faker::Alphanumeric.alphanumeric(number: 10) } - key { Base64.strict_encode64(Ed25519::SigningKey.generate.verify_key.to_bytes) } - - signature do |attrs| - signing_key = Ed25519::SigningKey.generate - attrs[:device].update(fingerprint_key: Base64.strict_encode64(signing_key.verify_key.to_bytes)) - Base64.strict_encode64(signing_key.sign(attrs[:key])) - end -end diff --git a/spec/fabricators/system_key_fabricator.rb b/spec/fabricators/system_key_fabricator.rb deleted file mode 100644 index bcb3bd5577..0000000000 --- a/spec/fabricators/system_key_fabricator.rb +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true - -Fabricator(:system_key) diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb index dec17b916b..0986ae1715 100644 --- a/spec/lib/activitypub/activity/create_spec.rb +++ b/spec/lib/activitypub/activity/create_spec.rb @@ -936,64 +936,6 @@ RSpec.describe ActivityPub::Activity::Create do end end - context 'with an encrypted message' do - subject { described_class.new(json, sender, delivery: true, delivered_to_account_id: recipient.id) } - - let(:recipient) { Fabricate(:account) } - let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'EncryptedMessage', - attributedTo: { - type: 'Device', - deviceId: '1234', - }, - to: { - type: 'Device', - deviceId: target_device.device_id, - }, - messageType: 1, - cipherText: 'Foo', - messageFranking: 'Baz678', - digest: { - digestAlgorithm: 'Bar456', - digestValue: 'Foo123', - }, - } - end - let(:target_device) { Fabricate(:device, account: recipient) } - - before do - subject.perform - end - - it 'creates an encrypted message' do - encrypted_message = target_device.encrypted_messages.reload.first - - expect(encrypted_message) - .to be_present - .and have_attributes( - from_device_id: eq('1234'), - from_account: eq(sender), - type: eq(1), - body: eq('Foo'), - digest: eq('Foo123') - ) - end - - it 'creates a message franking' do - encrypted_message = target_device.encrypted_messages.reload.first - message_franking = encrypted_message.message_franking - - crypt = ActiveSupport::MessageEncryptor.new(SystemKey.current_key, serializer: Oj) - json = crypt.decrypt_and_verify(message_franking) - - expect(json['source_account_id']).to eq sender.id - expect(json['target_account_id']).to eq recipient.id - expect(json['original_franking']).to eq 'Baz678' - end - end - context 'when sender is followed by local users' do subject { described_class.new(json, sender, delivery: true) } diff --git a/spec/lib/vacuum/system_keys_vacuum_spec.rb b/spec/lib/vacuum/system_keys_vacuum_spec.rb deleted file mode 100644 index 84cae30411..0000000000 --- a/spec/lib/vacuum/system_keys_vacuum_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Vacuum::SystemKeysVacuum do - subject { described_class.new } - - describe '#perform' do - let!(:expired_system_key) { Fabricate(:system_key, created_at: (SystemKey::ROTATION_PERIOD * 4).ago) } - let!(:current_system_key) { Fabricate(:system_key) } - - before do - subject.perform - end - - it 'deletes the expired key' do - expect { expired_system_key.reload }.to raise_error ActiveRecord::RecordNotFound - end - - it 'does not delete the current key' do - expect { current_system_key.reload }.to_not raise_error - end - end -end diff --git a/spec/models/one_time_key_spec.rb b/spec/models/one_time_key_spec.rb deleted file mode 100644 index 17fcdf3788..0000000000 --- a/spec/models/one_time_key_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe OneTimeKey do - describe 'validations' do - context 'with an invalid signature' do - let(:one_time_key) { Fabricate.build(:one_time_key, signature: 'wrong!') } - - it 'is invalid' do - expect(one_time_key).to_not be_valid - end - end - - context 'with an invalid key' do - let(:one_time_key) { Fabricate.build(:one_time_key, key: 'wrong!') } - - it 'is invalid' do - expect(one_time_key).to_not be_valid - end - end - end -end diff --git a/spec/serializers/activitypub/device_serializer_spec.rb b/spec/serializers/activitypub/device_serializer_spec.rb deleted file mode 100644 index 226e136446..0000000000 --- a/spec/serializers/activitypub/device_serializer_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe ActivityPub::DeviceSerializer do - let(:serialization) { serialized_record_json(record, described_class) } - let(:record) { Fabricate(:device) } - - describe 'type' do - it 'returns correct serialized type' do - expect(serialization['type']).to eq('Device') - end - end -end diff --git a/spec/serializers/activitypub/one_time_key_serializer_spec.rb b/spec/serializers/activitypub/one_time_key_serializer_spec.rb deleted file mode 100644 index b9792ebae3..0000000000 --- a/spec/serializers/activitypub/one_time_key_serializer_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe ActivityPub::OneTimeKeySerializer do - let(:serialization) { serialized_record_json(record, described_class) } - let(:record) { Fabricate(:one_time_key) } - - describe 'type' do - it 'returns correct serialized type' do - expect(serialization['type']).to eq('Curve25519Key') - end - end -end diff --git a/spec/serializers/rest/encrypted_message_serializer_spec.rb b/spec/serializers/rest/encrypted_message_serializer_spec.rb deleted file mode 100644 index a4b8ee83b2..0000000000 --- a/spec/serializers/rest/encrypted_message_serializer_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe REST::EncryptedMessageSerializer do - let(:serialization) { serialized_record_json(record, described_class) } - let(:record) { Fabricate(:encrypted_message) } - - describe 'account' do - it 'returns the associated account' do - expect(serialization['account_id']).to eq(record.from_account.id.to_s) - end - end -end diff --git a/spec/serializers/rest/keys/claim_result_serializer_spec.rb b/spec/serializers/rest/keys/claim_result_serializer_spec.rb deleted file mode 100644 index e45112705b..0000000000 --- a/spec/serializers/rest/keys/claim_result_serializer_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe REST::Keys::ClaimResultSerializer do - let(:serialization) { serialized_record_json(record, described_class) } - let(:record) { Keys::ClaimService::Result.new(Account.new(id: 123), 456) } - - describe 'account' do - it 'returns the associated account' do - expect(serialization['account_id']).to eq('123') - end - end -end diff --git a/spec/serializers/rest/keys/device_serializer_spec.rb b/spec/serializers/rest/keys/device_serializer_spec.rb deleted file mode 100644 index b8370beac7..0000000000 --- a/spec/serializers/rest/keys/device_serializer_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe REST::Keys::DeviceSerializer do - let(:serialization) { serialized_record_json(record, described_class) } - let(:record) { Device.new(name: 'Device name') } - - describe 'name' do - it 'returns the name' do - expect(serialization['name']).to eq('Device name') - end - end -end diff --git a/spec/serializers/rest/keys/query_result_serializer_spec.rb b/spec/serializers/rest/keys/query_result_serializer_spec.rb deleted file mode 100644 index 41492f5e78..0000000000 --- a/spec/serializers/rest/keys/query_result_serializer_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe REST::Keys::QueryResultSerializer do - let(:serialization) { serialized_record_json(record, described_class) } - let(:record) { Keys::QueryService::Result.new(Account.new(id: 123), []) } - - describe 'account' do - it 'returns the associated account id' do - expect(serialization['account_id']).to eq('123') - end - end -end diff --git a/spec/workers/push_encrypted_message_worker_spec.rb b/spec/workers/push_encrypted_message_worker_spec.rb deleted file mode 100644 index 311545cf56..0000000000 --- a/spec/workers/push_encrypted_message_worker_spec.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe PushEncryptedMessageWorker do - let(:worker) { described_class.new } - - describe 'perform' do - it 'runs without error for missing record' do - expect { worker.perform(nil) }.to_not raise_error - end - end -end diff --git a/streaming/index.js b/streaming/index.js index b302565a4e..48ed56b29b 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -44,7 +44,6 @@ initializeLogLevel(process.env, environment); * @property {string[]} scopes * @property {string} accountId * @property {string[]} chosenLanguages - * @property {string} deviceId */ @@ -355,7 +354,7 @@ const startServer = async () => { * @returns {Promise} */ 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]); + 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 FROM oauth_access_tokens INNER JOIN users ON oauth_access_tokens.resource_owner_id = users.id WHERE oauth_access_tokens.token = $1 AND oauth_access_tokens.revoked_at IS NULL LIMIT 1', [token]); if (result.rows.length === 0) { throw new AuthenticationError('Invalid access token'); @@ -365,14 +364,12 @@ const startServer = async () => { 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 }; }; @@ -983,10 +980,6 @@ const startServer = async () => { const channelsForUserStream = req => { const arr = [`timeline:${req.accountId}`]; - if (isInScope(req, ['crypto']) && req.deviceId) { - arr.push(`timeline:${req.accountId}:${req.deviceId}`); - } - if (isInScope(req, ['read', 'read:notifications'])) { arr.push(`timeline:${req.accountId}:notifications`); } From 6f836c45aa5862cdfd65877c99252fcb28ab4da1 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 18 Sep 2024 05:27:50 -0400 Subject: [PATCH 12/93] Remove `crypto` values from doorkeeper application/token `scopes` (#31945) --- ...240916190140_remove_crypto_scope_values.rb | 33 +++++++++++++++++++ db/schema.rb | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20240916190140_remove_crypto_scope_values.rb diff --git a/db/migrate/20240916190140_remove_crypto_scope_values.rb b/db/migrate/20240916190140_remove_crypto_scope_values.rb new file mode 100644 index 0000000000..8caf5b801d --- /dev/null +++ b/db/migrate/20240916190140_remove_crypto_scope_values.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +class RemoveCryptoScopeValues < ActiveRecord::Migration[7.1] + def up + applications.in_batches do |records| + records.update_all(<<~SQL.squish) + scopes = TRIM(REPLACE(scopes, 'crypto', '')) + SQL + end + + tokens.in_batches do |records| + records.update_all(<<~SQL.squish) + scopes = TRIM(REPLACE(scopes, 'crypto', '')) + SQL + end + end + + def down + raise ActiveRecord::IrreversibleMigration + end + + private + + def applications + Doorkeeper::Application + .where("scopes LIKE '%crypto%'") + end + + def tokens + Doorkeeper::AccessToken + .where("scopes LIKE '%crypto%'") + end +end diff --git a/db/schema.rb b/db/schema.rb index e9aa78c205..f9f2d97395 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_09_09_014637) do +ActiveRecord::Schema[7.1].define(version: 2024_09_16_190140) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" From bf8eaaa9a505f86bf7c965f981f585af34337635 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 18 Sep 2024 05:42:36 -0400 Subject: [PATCH 13/93] Convert controller spec for security_key_options endpoint to request spec (#31938) --- .../auth/sessions_controller_spec.rb | 40 --------------- .../sessions/security_key_options_spec.rb | 50 +++++++++++++++++++ 2 files changed, 50 insertions(+), 40 deletions(-) create mode 100644 spec/requests/auth/sessions/security_key_options_spec.rb diff --git a/spec/controllers/auth/sessions_controller_spec.rb b/spec/controllers/auth/sessions_controller_spec.rb index 3cc3460718..713ea3ff16 100644 --- a/spec/controllers/auth/sessions_controller_spec.rb +++ b/spec/controllers/auth/sessions_controller_spec.rb @@ -412,44 +412,4 @@ RSpec.describe Auth::SessionsController do end end end - - describe 'GET #webauthn_options' do - subject { get :webauthn_options, session: { attempt_user_id: user.id } } - - let!(:user) do - Fabricate(:user, email: 'x@y.com', password: 'abcdefgh', otp_required_for_login: true, otp_secret: User.generate_otp_secret(32)) - end - - context 'with WebAuthn and OTP enabled as second factor' do - let(:domain) { "#{Rails.configuration.x.use_https ? 'https' : 'http'}://#{Rails.configuration.x.web_domain}" } - - let(:fake_client) { WebAuthn::FakeClient.new(domain) } - - before do - user.update(webauthn_id: WebAuthn.generate_user_id) - public_key_credential = WebAuthn::Credential.from_create(fake_client.create) - user.webauthn_credentials.create( - nickname: 'SecurityKeyNickname', - external_id: public_key_credential.id, - public_key: public_key_credential.public_key, - sign_count: '1000' - ) - post :create, params: { user: { email: user.email, password: user.password } } - end - - it 'returns http success' do - subject - - expect(response).to have_http_status 200 - end - end - - context 'when WebAuthn not enabled' do - it 'returns http unauthorized' do - subject - - expect(response).to have_http_status 401 - end - end - end end diff --git a/spec/requests/auth/sessions/security_key_options_spec.rb b/spec/requests/auth/sessions/security_key_options_spec.rb new file mode 100644 index 0000000000..6246e4beb3 --- /dev/null +++ b/spec/requests/auth/sessions/security_key_options_spec.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'webauthn/fake_client' + +RSpec.describe 'Security Key Options' do + describe 'GET /auth/sessions/security_key_options' do + let!(:user) do + Fabricate(:user, email: 'x@y.com', password: 'abcdefgh', otp_required_for_login: true, otp_secret: User.generate_otp_secret(32)) + end + + context 'with WebAuthn and OTP enabled as second factor' do + let(:domain) { "#{Rails.configuration.x.use_https ? 'https' : 'http'}://#{Rails.configuration.x.web_domain}" } + + let(:fake_client) { WebAuthn::FakeClient.new(domain) } + let(:public_key_credential) { WebAuthn::Credential.from_create(fake_client.create) } + + before do + user.update(webauthn_id: WebAuthn.generate_user_id) + Fabricate( + :webauthn_credential, + user_id: user.id, + external_id: public_key_credential.id, + public_key: public_key_credential.public_key + ) + post '/auth/sign_in', params: { user: { email: user.email, password: user.password } } + end + + it 'returns http success' do + get '/auth/sessions/security_key_options' + + expect(response) + .to have_http_status 200 + expect(response.content_type) + .to start_with('application/json') + end + end + + context 'when WebAuthn not enabled' do + it 'returns http unauthorized' do + get '/auth/sessions/security_key_options' + + expect(response) + .to have_http_status 401 + expect(response.content_type) + .to start_with('application/json') + end + end + end +end From 42f9f507b632c1b90fecfed5f38ce2dbb2d0c949 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 15:29:21 +0200 Subject: [PATCH 14/93] Update dependency pg to v8.13.0 (#31949) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/yarn.lock b/yarn.lock index 45ede6ca99..5372c39f47 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13001,7 +13001,7 @@ __metadata: languageName: node linkType: hard -"pg-connection-string@npm:^2.6.0, pg-connection-string@npm:^2.6.4": +"pg-connection-string@npm:^2.6.0, pg-connection-string@npm:^2.7.0": version: 2.7.0 resolution: "pg-connection-string@npm:2.7.0" checksum: 10c0/50a1496a1c858f9495d78a2c7a66d93ef3602e718aff2953bb5738f3ea616d7f727f32fc20513c9bed127650cd14c1ddc7b458396f4000e689d4b64c65c5c51e @@ -13022,19 +13022,19 @@ __metadata: languageName: node linkType: hard -"pg-pool@npm:^3.6.2": - version: 3.6.2 - resolution: "pg-pool@npm:3.6.2" +"pg-pool@npm:^3.7.0": + version: 3.7.0 + resolution: "pg-pool@npm:3.7.0" peerDependencies: pg: ">=8.0" - checksum: 10c0/14c524549490954b5e48457a4b808df8f619f6deeb3b395b0cd184a8f4ed65a9273fe0697ba0341a41d6745af197f1437eb1cf51fff0cbbf5b0fb3852ebe5392 + checksum: 10c0/9128673cf941f288c0cb1a74ca959a9b4f6075ef73b2cc7dece5d4db3dd7043784869e7c12bce2e69ca0df22132a419cc45c2050b4373632856fe8bae9eb94b5 languageName: node linkType: hard -"pg-protocol@npm:*, pg-protocol@npm:^1.6.1": - version: 1.6.1 - resolution: "pg-protocol@npm:1.6.1" - checksum: 10c0/7eadef4010ac0a3925c460be7332ca4098a5c6d5181725a62193fcfa800000ae6632d98d814f3989b42cf5fdc3b45e34c714a1959d29174e81e30730e140ae5f +"pg-protocol@npm:*, pg-protocol@npm:^1.7.0": + version: 1.7.0 + resolution: "pg-protocol@npm:1.7.0" + checksum: 10c0/c4af854d9b843c808231c0040fed89f2b9101006157df8da2bb2f62a7dde702de748d852228dc22df41cc7ffddfb526af3bcb34b278b581e9f76a060789186c1 languageName: node linkType: hard @@ -13067,13 +13067,13 @@ __metadata: linkType: hard "pg@npm:^8.5.0": - version: 8.12.0 - resolution: "pg@npm:8.12.0" + version: 8.13.0 + resolution: "pg@npm:8.13.0" dependencies: pg-cloudflare: "npm:^1.1.1" - pg-connection-string: "npm:^2.6.4" - pg-pool: "npm:^3.6.2" - pg-protocol: "npm:^1.6.1" + pg-connection-string: "npm:^2.7.0" + pg-pool: "npm:^3.7.0" + pg-protocol: "npm:^1.7.0" pg-types: "npm:^2.1.0" pgpass: "npm:1.x" peerDependencies: @@ -13084,7 +13084,7 @@ __metadata: peerDependenciesMeta: pg-native: optional: true - checksum: 10c0/973e49b5e7327c42fc62806efa8c824159ab7a0b676cefe6eeb51a59b6e226587911ec27697f36c18d69e58a7f4f0b76d0829364087194d13ed431ab7c9c417a + checksum: 10c0/1521189063d2293d62f3fac61e797a3096a62a69668c223827d00b83c17a320805f31f0b5316feb80f8d9eed0c6c32f95146d8aca866af05816a66fd2ba8e32a languageName: node linkType: hard From e3baa1cdda2af3d0ff7920e6249a3a9c0ccbe562 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 18 Sep 2024 09:29:57 -0400 Subject: [PATCH 15/93] Add coverage for `AccountDeletionRequest` class (#31937) --- spec/models/account_deletion_request_spec.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 spec/models/account_deletion_request_spec.rb diff --git a/spec/models/account_deletion_request_spec.rb b/spec/models/account_deletion_request_spec.rb new file mode 100644 index 0000000000..7dbfbed12a --- /dev/null +++ b/spec/models/account_deletion_request_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe AccountDeletionRequest do + describe 'Associations' do + it { is_expected.to belong_to(:account).required } + end + + describe '#due_at' do + before { stub_const 'AccountDeletionRequest::DELAY_TO_DELETION', 1.day } + + it 'returns time from created at with delay added' do + account_deletion_request = Fabricate :account_deletion_request, created_at: Date.current.at_midnight + expect(account_deletion_request.due_at) + .to be_within(0.1).of(Date.tomorrow.at_midnight) + end + end +end From 8b708340356e18696e5ab9e83067ce9297e481f7 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Wed, 18 Sep 2024 19:39:15 +0200 Subject: [PATCH 16/93] Fix the appearance of avatars when they do not load (#31966) --- app/javascript/styles/mastodon/components.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 5016480f32..495481622a 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2032,13 +2032,14 @@ body > [data-popper-placement] { display: block; position: relative; border-radius: var(--avatar-border-radius); + background-color: var(--surface-background-color); img { - display: block; width: 100%; height: 100%; object-fit: cover; border-radius: var(--avatar-border-radius); + display: inline-block; // to not show broken images } &-inline { From 29656cb9e0e5fbecdec5bc5f4e8fc2249e1b8c4e Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 18 Sep 2024 19:39:32 +0200 Subject: [PATCH 17/93] Fix sass deprecation warning (#31961) --- .../styles/mastodon-light/variables.scss | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/javascript/styles/mastodon-light/variables.scss b/app/javascript/styles/mastodon-light/variables.scss index 76ede26233..76bdc4022e 100644 --- a/app/javascript/styles/mastodon-light/variables.scss +++ b/app/javascript/styles/mastodon-light/variables.scss @@ -1,3 +1,5 @@ +@use 'sass:color'; + // Dependent colors $black: #000000; $white: #ffffff; @@ -47,11 +49,19 @@ $account-background-color: $white !default; // Invert darkened and lightened colors @function darken($color, $amount) { - @return hsl(hue($color), saturation($color), lightness($color) + $amount); + @return hsl( + hue($color), + color.channel($color, 'saturation', $space: hsl), + color.channel($color, 'lightness', $space: hsl) + $amount + ); } @function lighten($color, $amount) { - @return hsl(hue($color), saturation($color), lightness($color) - $amount); + @return hsl( + hue($color), + color.channel($color, 'saturation', $space: hsl), + color.channel($color, 'lightness', $space: hsl) - $amount + ); } $emojis-requiring-inversion: 'chains'; From 62a39d60cedc073b3190b9ba2f2f6f3173057eb2 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 19 Sep 2024 11:50:06 +0200 Subject: [PATCH 18/93] Fix rolling updates by moving DropEndToEndMessageTables to post-deployment migrations (#31963) --- .../20240720140205_drop_end_to_end_message_tables.rb | 0 .../20240916190140_remove_crypto_scope_values.rb | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename db/{migrate => post_migrate}/20240720140205_drop_end_to_end_message_tables.rb (100%) rename db/{migrate => post_migrate}/20240916190140_remove_crypto_scope_values.rb (100%) diff --git a/db/migrate/20240720140205_drop_end_to_end_message_tables.rb b/db/post_migrate/20240720140205_drop_end_to_end_message_tables.rb similarity index 100% rename from db/migrate/20240720140205_drop_end_to_end_message_tables.rb rename to db/post_migrate/20240720140205_drop_end_to_end_message_tables.rb diff --git a/db/migrate/20240916190140_remove_crypto_scope_values.rb b/db/post_migrate/20240916190140_remove_crypto_scope_values.rb similarity index 100% rename from db/migrate/20240916190140_remove_crypto_scope_values.rb rename to db/post_migrate/20240916190140_remove_crypto_scope_values.rb From 90db524a90ea5fee8b791b15501c7348f8a87c39 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 09:50:58 +0000 Subject: [PATCH 19/93] Update dependency puma to v6.4.3 (#31975) 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 5ed8fe78eb..b19c5bb578 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -609,7 +609,7 @@ GEM psych (5.1.2) stringio public_suffix (6.0.1) - puma (6.4.2) + puma (6.4.3) nio4r (~> 2.0) pundit (2.4.0) activesupport (>= 3.0.0) From 1fce55cf5dc63d944f45b938eb3df28742ca7d77 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 09:51:14 +0000 Subject: [PATCH 20/93] Update dependency aws-sdk-s3 to v1.163.0 (#31972) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index b19c5bb578..41a8b68fae 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -100,8 +100,8 @@ GEM attr_required (1.0.2) awrence (1.2.1) aws-eventstream (1.3.0) - aws-partitions (1.974.0) - aws-sdk-core (3.205.0) + aws-partitions (1.977.0) + aws-sdk-core (3.206.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.9) @@ -109,11 +109,11 @@ GEM aws-sdk-kms (1.91.0) aws-sdk-core (~> 3, >= 3.205.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.162.0) + aws-sdk-s3 (1.163.0) aws-sdk-core (~> 3, >= 3.205.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.9.1) + aws-sigv4 (1.10.0) aws-eventstream (~> 1, >= 1.0.2) azure-storage-blob (2.0.3) azure-storage-common (~> 2.0) From b071e618e7c53a89ee332ae4c7afe9c5b4e9d176 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 19 Sep 2024 06:15:21 -0400 Subject: [PATCH 21/93] Combine API request spec assertions (#31970) --- spec/requests/api/v1/admin/accounts_spec.rb | 6 +-- spec/requests/api/v1/admin/tags_spec.rb | 12 +----- spec/requests/api/v1/apps/credentials_spec.rb | 12 +----- spec/requests/api/v1/blocks_spec.rb | 7 +--- spec/requests/api/v1/bookmarks_spec.rb | 7 +--- spec/requests/api/v1/favourites_spec.rb | 14 +------ spec/requests/api/v1/featured_tags_spec.rb | 19 +--------- spec/requests/api/v1/followed_tags_spec.rb | 7 +--- .../api/v1/instances/languages_spec.rb | 5 +-- spec/requests/api/v1/lists/accounts_spec.rb | 7 +--- spec/requests/api/v1/media_spec.rb | 7 +--- spec/requests/api/v1/mutes_spec.rb | 14 +------ .../api/v1/notifications/requests_spec.rb | 7 +--- spec/requests/api/v1/profiles_spec.rb | 38 +------------------ .../api/v1/statuses/bookmarks_spec.rb | 18 ++------- .../api/v1/statuses/favourites_spec.rb | 18 ++------- spec/requests/api/v1/statuses/pins_spec.rb | 12 +----- spec/requests/api/v1/suggestions_spec.rb | 14 +------ spec/requests/api/v1/timelines/home_spec.rb | 12 +----- spec/requests/api/v1/timelines/link_spec.rb | 6 +-- spec/requests/api/v2/filters_spec.rb | 26 ++----------- 21 files changed, 36 insertions(+), 232 deletions(-) diff --git a/spec/requests/api/v1/admin/accounts_spec.rb b/spec/requests/api/v1/admin/accounts_spec.rb index 2dc45d5eb2..f557b61403 100644 --- a/spec/requests/api/v1/admin/accounts_spec.rb +++ b/spec/requests/api/v1/admin/accounts_spec.rb @@ -193,15 +193,11 @@ RSpec.describe 'Accounts' do it_behaves_like 'forbidden for wrong scope', 'write write:accounts read admin:read' it_behaves_like 'forbidden for wrong role', '' - it 'removes the user successfully', :aggregate_failures do + it 'removes the user successfully and logs action', :aggregate_failures do subject expect(response).to have_http_status(200) expect(User.where(id: account.user.id)).to_not exist - end - - it 'logs action', :aggregate_failures do - subject expect(latest_admin_action_log) .to be_present diff --git a/spec/requests/api/v1/admin/tags_spec.rb b/spec/requests/api/v1/admin/tags_spec.rb index 2f730cdeb8..fda9227acf 100644 --- a/spec/requests/api/v1/admin/tags_spec.rb +++ b/spec/requests/api/v1/admin/tags_spec.rb @@ -73,14 +73,10 @@ RSpec.describe 'Tags' do it_behaves_like 'forbidden for wrong scope', 'write:statuses' it_behaves_like 'forbidden for wrong role', '' - it 'returns http success' do + it 'returns http success and expected tag content' do subject expect(response).to have_http_status(200) - end - - it 'returns expected tag content' do - subject expect(response.parsed_body[:id].to_i).to eq(tag.id) expect(response.parsed_body[:name]).to eq(tag.name) @@ -107,14 +103,10 @@ RSpec.describe 'Tags' do it_behaves_like 'forbidden for wrong scope', 'admin:read' it_behaves_like 'forbidden for wrong role', '' - it 'returns http success' do + it 'returns http success and updates tag' do subject expect(response).to have_http_status(200) - end - - it 'returns updated tag' do - subject expect(response.parsed_body[:id].to_i).to eq(tag.id) expect(response.parsed_body[:name]).to eq(tag.name.upcase) diff --git a/spec/requests/api/v1/apps/credentials_spec.rb b/spec/requests/api/v1/apps/credentials_spec.rb index 1cd6a4178f..6fd885eb86 100644 --- a/spec/requests/api/v1/apps/credentials_spec.rb +++ b/spec/requests/api/v1/apps/credentials_spec.rb @@ -47,14 +47,10 @@ RSpec.describe 'Credentials' do let(:token) { Fabricate(:accessible_access_token, application: application) } let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } - it 'returns http success' do + it 'returns http success and returns app information' do subject expect(response).to have_http_status(200) - end - - it 'returns the app information correctly' do - subject expect(response.parsed_body).to match( a_hash_including( @@ -108,14 +104,10 @@ RSpec.describe 'Credentials' do let(:token) { Fabricate(:accessible_access_token, application: application) } let(:headers) { { 'Authorization' => "Bearer #{token.token}-invalid" } } - it 'returns http authorization error' do + it 'returns http authorization error with json error' do subject expect(response).to have_http_status(401) - end - - it 'returns the error in the json response' do - subject expect(response.parsed_body).to match( a_hash_including( diff --git a/spec/requests/api/v1/blocks_spec.rb b/spec/requests/api/v1/blocks_spec.rb index d2f1c46a5b..fc028f9bac 100644 --- a/spec/requests/api/v1/blocks_spec.rb +++ b/spec/requests/api/v1/blocks_spec.rb @@ -32,15 +32,10 @@ RSpec.describe 'Blocks' do context 'with limit param' do let(:params) { { limit: 2 } } - it 'returns only the requested number of blocked accounts' do + it 'returns only the requested number of blocked accounts and sets link header pagination' do subject expect(response.parsed_body.size).to eq(params[:limit]) - end - - it 'sets correct link header pagination' do - subject - expect(response) .to include_pagination_headers( prev: api_v1_blocks_url(limit: params[:limit], since_id: blocks.last.id), diff --git a/spec/requests/api/v1/bookmarks_spec.rb b/spec/requests/api/v1/bookmarks_spec.rb index 95a71abcac..5955de6652 100644 --- a/spec/requests/api/v1/bookmarks_spec.rb +++ b/spec/requests/api/v1/bookmarks_spec.rb @@ -24,15 +24,10 @@ RSpec.describe 'Bookmarks' do it_behaves_like 'forbidden for wrong scope', 'write' - it 'returns http success' do + it 'returns http success and the bookmarked statuses' do subject expect(response).to have_http_status(200) - end - - it 'returns the bookmarked statuses' do - subject - expect(response.parsed_body).to match_array(expected_response) end diff --git a/spec/requests/api/v1/favourites_spec.rb b/spec/requests/api/v1/favourites_spec.rb index 78e9d61551..2f8bef1190 100644 --- a/spec/requests/api/v1/favourites_spec.rb +++ b/spec/requests/api/v1/favourites_spec.rb @@ -24,30 +24,20 @@ RSpec.describe 'Favourites' do it_behaves_like 'forbidden for wrong scope', 'write' - it 'returns http success' do + it 'returns http success and includes the favourites' do subject expect(response).to have_http_status(200) - end - - it 'returns the favourites' do - subject - expect(response.parsed_body).to match_array(expected_response) end context 'with limit param' do let(:params) { { limit: 1 } } - it 'returns only the requested number of favourites' do + it 'returns only the requested number of favourites and sets pagination headers' do subject expect(response.parsed_body.size).to eq(params[:limit]) - end - - it 'sets the correct pagination headers' do - subject - expect(response) .to include_pagination_headers( prev: api_v1_favourites_url(limit: params[:limit], min_id: favourites.last.id), diff --git a/spec/requests/api/v1/featured_tags_spec.rb b/spec/requests/api/v1/featured_tags_spec.rb index 423cc0c560..f0e939d42a 100644 --- a/spec/requests/api/v1/featured_tags_spec.rb +++ b/spec/requests/api/v1/featured_tags_spec.rb @@ -58,15 +58,10 @@ RSpec.describe 'FeaturedTags' do describe 'POST /api/v1/featured_tags' do let(:params) { { name: 'tag' } } - it 'returns http success' do + it 'returns http success and includes correct tag name' do post '/api/v1/featured_tags', headers: headers, params: params expect(response).to have_http_status(200) - end - - it 'returns the correct tag name' do - post '/api/v1/featured_tags', headers: headers, params: params - expect(response.parsed_body) .to include( name: params[:name] @@ -132,23 +127,13 @@ RSpec.describe 'FeaturedTags' do let!(:featured_tag) { FeaturedTag.create(name: 'tag', account: user.account) } let(:id) { featured_tag.id } - it 'returns http success' do + it 'returns http success with an empty body and deletes the featured tag', :inline_jobs do delete "/api/v1/featured_tags/#{id}", headers: headers expect(response).to have_http_status(200) - end - - it 'returns an empty body' do - delete "/api/v1/featured_tags/#{id}", headers: headers - expect(response.parsed_body).to be_empty - end - - it 'deletes the featured tag', :inline_jobs do - delete "/api/v1/featured_tags/#{id}", headers: headers featured_tag = FeaturedTag.find_by(id: id) - expect(featured_tag).to be_nil end diff --git a/spec/requests/api/v1/followed_tags_spec.rb b/spec/requests/api/v1/followed_tags_spec.rb index f7787cb763..f15c0d7f44 100644 --- a/spec/requests/api/v1/followed_tags_spec.rb +++ b/spec/requests/api/v1/followed_tags_spec.rb @@ -28,15 +28,10 @@ RSpec.describe 'Followed tags' do it_behaves_like 'forbidden for wrong scope', 'write write:follows' - it 'returns http success' do + it 'returns http success and includes followed tags' do subject expect(response).to have_http_status(:success) - end - - it 'returns the followed tags correctly' do - subject - expect(response.parsed_body).to match_array(expected_response) end diff --git a/spec/requests/api/v1/instances/languages_spec.rb b/spec/requests/api/v1/instances/languages_spec.rb index 79ea62c599..3ab1ba57b8 100644 --- a/spec/requests/api/v1/instances/languages_spec.rb +++ b/spec/requests/api/v1/instances/languages_spec.rb @@ -8,11 +8,8 @@ RSpec.describe 'Languages' do get '/api/v1/instance/languages' end - it 'returns http success' do + it 'returns http success and includes supported languages' do expect(response).to have_http_status(200) - end - - it 'returns the supported languages' do expect(response.parsed_body.pluck(:code)).to match_array LanguagesHelper::SUPPORTED_LOCALES.keys.map(&:to_s) end end diff --git a/spec/requests/api/v1/lists/accounts_spec.rb b/spec/requests/api/v1/lists/accounts_spec.rb index d147b21ee7..6e5f9e4e9d 100644 --- a/spec/requests/api/v1/lists/accounts_spec.rb +++ b/spec/requests/api/v1/lists/accounts_spec.rb @@ -139,16 +139,11 @@ RSpec.describe 'Accounts' do list.accounts << [bob, peter] end - it 'removes the specified account from the list', :aggregate_failures do + it 'removes the specified account from the list but keeps other accounts in the list', :aggregate_failures do subject expect(response).to have_http_status(200) expect(list.accounts).to_not include(bob) - end - - it 'does not remove any other account from the list' do - subject - expect(list.accounts).to include(peter) end diff --git a/spec/requests/api/v1/media_spec.rb b/spec/requests/api/v1/media_spec.rb index d0af334825..a10bbb31ef 100644 --- a/spec/requests/api/v1/media_spec.rb +++ b/spec/requests/api/v1/media_spec.rb @@ -17,15 +17,10 @@ RSpec.describe 'Media' do it_behaves_like 'forbidden for wrong scope', 'read' - it 'returns http success' do + it 'returns http success with media information' do subject expect(response).to have_http_status(200) - end - - it 'returns the media information' do - subject - expect(response.parsed_body).to match( a_hash_including( id: media.id.to_s, diff --git a/spec/requests/api/v1/mutes_spec.rb b/spec/requests/api/v1/mutes_spec.rb index 6402c908ff..316d455d28 100644 --- a/spec/requests/api/v1/mutes_spec.rb +++ b/spec/requests/api/v1/mutes_spec.rb @@ -18,32 +18,22 @@ RSpec.describe 'Mutes' do it_behaves_like 'forbidden for wrong scope', 'write write:mutes' - it 'returns http success' do + it 'returns http success with muted accounts' do subject expect(response).to have_http_status(200) - end - - it 'returns the muted accounts' do - subject muted_accounts = mutes.map(&:target_account) - expect(response.parsed_body.pluck(:id)).to match_array(muted_accounts.map { |account| account.id.to_s }) end context 'with limit param' do let(:params) { { limit: 1 } } - it 'returns only the requested number of muted accounts' do + it 'returns only the requested number of muted accounts with pagination headers' do subject expect(response.parsed_body.size).to eq(params[:limit]) - end - - it 'sets the correct pagination headers', :aggregate_failures do - subject - expect(response) .to include_pagination_headers( prev: api_v1_mutes_url(limit: params[:limit], since_id: mutes.last.id), diff --git a/spec/requests/api/v1/notifications/requests_spec.rb b/spec/requests/api/v1/notifications/requests_spec.rb index dc125bc7aa..030b7cfa21 100644 --- a/spec/requests/api/v1/notifications/requests_spec.rb +++ b/spec/requests/api/v1/notifications/requests_spec.rb @@ -39,15 +39,10 @@ RSpec.describe 'Requests' do it_behaves_like 'forbidden for wrong scope', 'read read:notifications' - it 'returns http success' do + it 'returns http success and creates notification permission' do subject expect(response).to have_http_status(200) - end - - it 'creates notification permission' do - subject - expect(NotificationPermission.find_by(account: notification_request.account, from_account: notification_request.from_account)).to_not be_nil end diff --git a/spec/requests/api/v1/profiles_spec.rb b/spec/requests/api/v1/profiles_spec.rb index 26a9b848e5..9616f41559 100644 --- a/spec/requests/api/v1/profiles_spec.rb +++ b/spec/requests/api/v1/profiles_spec.rb @@ -28,31 +28,14 @@ RSpec.describe 'Deleting profile images' do it_behaves_like 'forbidden for wrong scope', 'read' end - it 'returns http success' do + it 'returns http success and deletes the avatar, preserves the header, queues up distribution' do delete '/api/v1/profile/avatar', headers: headers expect(response).to have_http_status(200) - end - - it 'deletes the avatar' do - delete '/api/v1/profile/avatar', headers: headers account.reload - expect(account.avatar).to_not exist - end - - it 'does not delete the header' do - delete '/api/v1/profile/avatar', headers: headers - - account.reload - expect(account.header).to exist - end - - it 'queues up an account update distribution' do - delete '/api/v1/profile/avatar', headers: headers - expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_async).with(account.id) end end @@ -66,31 +49,14 @@ RSpec.describe 'Deleting profile images' do it_behaves_like 'forbidden for wrong scope', 'read' end - it 'returns http success' do + it 'returns http success, preserves the avatar, deletes the header, queues up distribution' do delete '/api/v1/profile/header', headers: headers expect(response).to have_http_status(200) - end - - it 'does not delete the avatar' do - delete '/api/v1/profile/header', headers: headers account.reload - expect(account.avatar).to exist - end - - it 'deletes the header' do - delete '/api/v1/profile/header', headers: headers - - account.reload - expect(account.header).to_not exist - end - - it 'queues up an account update distribution' do - delete '/api/v1/profile/header', headers: headers - expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_async).with(account.id) end end diff --git a/spec/requests/api/v1/statuses/bookmarks_spec.rb b/spec/requests/api/v1/statuses/bookmarks_spec.rb index f1bcfda0ff..6401a4370d 100644 --- a/spec/requests/api/v1/statuses/bookmarks_spec.rb +++ b/spec/requests/api/v1/statuses/bookmarks_spec.rb @@ -18,15 +18,11 @@ RSpec.describe 'Bookmarks' do it_behaves_like 'forbidden for wrong scope', 'read' context 'with public status' do - it 'bookmarks the status successfully', :aggregate_failures do + it 'bookmarks the status successfully and includes updated json', :aggregate_failures do subject expect(response).to have_http_status(200) expect(user.account.bookmarked?(status)).to be true - end - - it 'returns json with updated attributes' do - subject expect(response.parsed_body).to match( a_hash_including(id: status.id.to_s, bookmarked: true) @@ -93,15 +89,11 @@ RSpec.describe 'Bookmarks' do Bookmark.find_or_create_by!(account: user.account, status: status) end - it 'unbookmarks the status successfully', :aggregate_failures do + it 'unbookmarks the status successfully and includes updated json', :aggregate_failures do subject expect(response).to have_http_status(200) expect(user.account.bookmarked?(status)).to be false - end - - it 'returns json with updated attributes' do - subject expect(response.parsed_body).to match( a_hash_including(id: status.id.to_s, bookmarked: false) @@ -117,15 +109,11 @@ RSpec.describe 'Bookmarks' do status.account.block!(user.account) end - it 'unbookmarks the status successfully', :aggregate_failures do + it 'unbookmarks the status successfully and includes updated json', :aggregate_failures do subject expect(response).to have_http_status(200) expect(user.account.bookmarked?(status)).to be false - end - - it 'returns json with updated attributes' do - subject expect(response.parsed_body).to match( a_hash_including(id: status.id.to_s, bookmarked: false) diff --git a/spec/requests/api/v1/statuses/favourites_spec.rb b/spec/requests/api/v1/statuses/favourites_spec.rb index f9f0ff6299..c3acf0413e 100644 --- a/spec/requests/api/v1/statuses/favourites_spec.rb +++ b/spec/requests/api/v1/statuses/favourites_spec.rb @@ -18,15 +18,11 @@ RSpec.describe 'Favourites', :inline_jobs do it_behaves_like 'forbidden for wrong scope', 'read read:favourites' context 'with public status' do - it 'favourites the status successfully', :aggregate_failures do + it 'favourites the status successfully and includes updated json', :aggregate_failures do subject expect(response).to have_http_status(200) expect(user.account.favourited?(status)).to be true - end - - it 'returns json with updated attributes' do - subject expect(response.parsed_body).to match( a_hash_including(id: status.id.to_s, favourites_count: 1, favourited: true) @@ -84,16 +80,12 @@ RSpec.describe 'Favourites', :inline_jobs do FavouriteService.new.call(user.account, status) end - it 'unfavourites the status successfully', :aggregate_failures do + it 'unfavourites the status successfully and includes updated json', :aggregate_failures do subject expect(response).to have_http_status(200) expect(user.account.favourited?(status)).to be false - end - - it 'returns json with updated attributes' do - subject expect(response.parsed_body).to match( a_hash_including(id: status.id.to_s, favourites_count: 0, favourited: false) @@ -107,16 +99,12 @@ RSpec.describe 'Favourites', :inline_jobs do status.account.block!(user.account) end - it 'unfavourites the status successfully', :aggregate_failures do + it 'unfavourites the status successfully and includes updated json', :aggregate_failures do subject expect(response).to have_http_status(200) expect(user.account.favourited?(status)).to be false - end - - it 'returns json with updated attributes' do - subject expect(response.parsed_body).to match( a_hash_including(id: status.id.to_s, favourites_count: 0, favourited: false) diff --git a/spec/requests/api/v1/statuses/pins_spec.rb b/spec/requests/api/v1/statuses/pins_spec.rb index 56e60c6d36..409c50e7c2 100644 --- a/spec/requests/api/v1/statuses/pins_spec.rb +++ b/spec/requests/api/v1/statuses/pins_spec.rb @@ -18,15 +18,11 @@ RSpec.describe 'Pins' do it_behaves_like 'forbidden for wrong scope', 'read read:accounts' context 'when the status is public' do - it 'pins the status successfully', :aggregate_failures do + it 'pins the status successfully and returns updated json', :aggregate_failures do subject expect(response).to have_http_status(200) expect(user.account.pinned?(status)).to be true - end - - it 'return json with updated attributes' do - subject expect(response.parsed_body).to match( a_hash_including(id: status.id.to_s, pinned: true) @@ -86,15 +82,11 @@ RSpec.describe 'Pins' do Fabricate(:status_pin, status: status, account: user.account) end - it 'unpins the status successfully', :aggregate_failures do + it 'unpins the status successfully and includes updated json', :aggregate_failures do subject expect(response).to have_http_status(200) expect(user.account.pinned?(status)).to be false - end - - it 'return json with updated attributes' do - subject expect(response.parsed_body).to match( a_hash_including(id: status.id.to_s, pinned: false) diff --git a/spec/requests/api/v1/suggestions_spec.rb b/spec/requests/api/v1/suggestions_spec.rb index 8267bb92a0..b971f88129 100644 --- a/spec/requests/api/v1/suggestions_spec.rb +++ b/spec/requests/api/v1/suggestions_spec.rb @@ -23,15 +23,10 @@ RSpec.describe 'Suggestions' do it_behaves_like 'forbidden for wrong scope', 'write' - it 'returns http success' do + it 'returns http success with accounts' do subject expect(response).to have_http_status(200) - end - - it 'returns accounts' do - subject - expect(response.parsed_body) .to contain_exactly(include(id: bob.id.to_s), include(id: jeff.id.to_s)) end @@ -72,15 +67,10 @@ RSpec.describe 'Suggestions' do it_behaves_like 'forbidden for wrong scope', 'read' - it 'returns http success' do + it 'returns http success and removes suggestion' do subject expect(response).to have_http_status(200) - end - - it 'removes the specified suggestion' do - subject - expect(FollowRecommendationMute.exists?(account: user.account, target_account: jeff)).to be true end diff --git a/spec/requests/api/v1/timelines/home_spec.rb b/spec/requests/api/v1/timelines/home_spec.rb index afad2988ca..19a6f3adbc 100644 --- a/spec/requests/api/v1/timelines/home_spec.rb +++ b/spec/requests/api/v1/timelines/home_spec.rb @@ -31,14 +31,10 @@ RSpec.describe 'Home', :inline_jobs do PostStatusService.new.call(ana, text: 'New toot from ana.') end - it 'returns http success' do + it 'returns http success and statuses of followed users' do subject expect(response).to have_http_status(200) - end - - it 'returns the statuses of followed users' do - subject expect(response.parsed_body.pluck(:id)).to match_array(home_statuses.map { |status| status.id.to_s }) end @@ -46,14 +42,10 @@ RSpec.describe 'Home', :inline_jobs do context 'with limit param' do let(:params) { { limit: 1 } } - it 'returns only the requested number of statuses' do + it 'returns only the requested number of statuses with pagination headers', :aggregate_failures do subject expect(response.parsed_body.size).to eq(params[:limit]) - end - - it 'sets the correct pagination headers', :aggregate_failures do - subject expect(response) .to include_pagination_headers( diff --git a/spec/requests/api/v1/timelines/link_spec.rb b/spec/requests/api/v1/timelines/link_spec.rb index 8999364703..e1d421fb7a 100644 --- a/spec/requests/api/v1/timelines/link_spec.rb +++ b/spec/requests/api/v1/timelines/link_spec.rb @@ -123,15 +123,11 @@ RSpec.describe 'Link' do context 'with limit param' do let(:params) { { limit: 1, url: url } } - it 'returns only the requested number of statuses', :aggregate_failures do + it 'returns only the requested number of statuses with pagination headers', :aggregate_failures do subject expect(response).to have_http_status(200) expect(response.parsed_body.size).to eq(params[:limit]) - end - - it 'sets the correct pagination headers', :aggregate_failures do - subject expect(response) .to include_pagination_headers( diff --git a/spec/requests/api/v2/filters_spec.rb b/spec/requests/api/v2/filters_spec.rb index 850c773df3..ad8b55973e 100644 --- a/spec/requests/api/v2/filters_spec.rb +++ b/spec/requests/api/v2/filters_spec.rb @@ -49,14 +49,10 @@ RSpec.describe 'Filters' do context 'with valid params' do let(:params) { { title: 'magic', context: %w(home), filter_action: 'hide', keywords_attributes: [keyword: 'magic'] } } - it 'returns http success' do + it 'returns http success with a filter with keywords in json and creates a filter', :aggregate_failures do subject expect(response).to have_http_status(200) - end - - it 'returns a filter with keywords', :aggregate_failures do - subject expect(response.parsed_body) .to include( @@ -67,10 +63,6 @@ RSpec.describe 'Filters' do include(keyword: 'magic', whole_word: true) ) ) - end - - it 'creates a filter', :aggregate_failures do - subject filter = user.account.custom_filters.first @@ -189,20 +181,12 @@ RSpec.describe 'Filters' do allow(redis).to receive_messages(publish: nil) end - it 'returns http success' do + it 'returns http success and updates keyword and sends a filters_changed event' do subject expect(response).to have_http_status(200) - end - - it 'updates the keyword' do - subject expect(keyword.reload.keyword).to eq 'updated' - end - - it 'sends exactly one filters_changed event' do - subject expect(redis).to have_received(:publish).with("timeline:#{user.account.id}", Oj.dump(event: :filters_changed)).once end @@ -229,14 +213,10 @@ RSpec.describe 'Filters' do it_behaves_like 'forbidden for wrong scope', 'read read:filters' it_behaves_like 'unauthorized for invalid token' - it 'returns http success' do + it 'returns http success and removes the filter' do subject expect(response).to have_http_status(200) - end - - it 'removes the filter' do - subject expect { filter.reload }.to raise_error ActiveRecord::RecordNotFound end From 5d573c976e8f02e67014154232d2c7f936d8f717 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 19 Sep 2024 06:23:58 -0400 Subject: [PATCH 22/93] Remove unused E2EE-related methods (#31964) --- app/javascript/mastodon/stream.js | 1 - app/presenters/activitypub/activity_presenter.rb | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/app/javascript/mastodon/stream.js b/app/javascript/mastodon/stream.js index 40d69136a8..59b2fd7582 100644 --- a/app/javascript/mastodon/stream.js +++ b/app/javascript/mastodon/stream.js @@ -209,7 +209,6 @@ const KNOWN_EVENT_TYPES = [ 'notification', 'conversation', 'filters_changed', - 'encrypted_message', 'announcement', 'announcement.delete', 'announcement.reaction', diff --git a/app/presenters/activitypub/activity_presenter.rb b/app/presenters/activitypub/activity_presenter.rb index 38e8527e8e..994cbeaf48 100644 --- a/app/presenters/activitypub/activity_presenter.rb +++ b/app/presenters/activitypub/activity_presenter.rb @@ -26,16 +26,5 @@ class ActivityPub::ActivityPresenter < ActiveModelSerializers::Model end end end - - def from_encrypted_message(encrypted_message) - new.tap do |presenter| - presenter.id = ActivityPub::TagManager.instance.generate_uri_for(nil) - presenter.type = 'Create' - presenter.actor = ActivityPub::TagManager.instance.uri_for(encrypted_message.source_account) - presenter.published = Time.now.utc - presenter.to = ActivityPub::TagManager.instance.uri_for(encrypted_message.target_account) - presenter.virtual_object = encrypted_message - end - end end end From efdc17513d4f929259f5d92b9ba2718e280295e3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:34:19 +0000 Subject: [PATCH 23/93] New Crowdin Translations (automated) (#31974) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/ko.json | 3 +++ app/javascript/mastodon/locales/lv.json | 1 + config/locales/activerecord.ko.yml | 6 ++++++ config/locales/ko.yml | 8 ++++++++ config/locales/lv.yml | 2 +- config/locales/simple_form.ko.yml | 2 ++ 6 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 6795bc647c..cf2082e105 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -778,6 +778,7 @@ "status.bookmark": "북마크", "status.cancel_reblog_private": "부스트 취소", "status.cannot_reblog": "이 게시물은 부스트 할 수 없습니다", + "status.continued_thread": "이어지는 글타래", "status.copy": "게시물 링크 복사", "status.delete": "삭제", "status.detailed_status": "대화 자세히 보기", @@ -786,6 +787,7 @@ "status.edit": "수정", "status.edited": "{date}에 마지막으로 편집됨", "status.edited_x_times": "{count, plural, other {{count}}} 번 수정됨", + "status.embed": "임베드 코드 받기", "status.favourite": "좋아요", "status.favourites": "{count, plural, other {좋아요}}", "status.filter": "이 게시물을 필터", @@ -810,6 +812,7 @@ "status.reblogs.empty": "아직 아무도 이 게시물을 부스트하지 않았습니다. 부스트 한 사람들이 여기에 표시 됩니다.", "status.redraft": "지우고 다시 쓰기", "status.remove_bookmark": "북마크 삭제", + "status.replied_in_thread": "글타래에 답장", "status.replied_to": "{name} 님에게", "status.reply": "답장", "status.replyAll": "글타래에 답장", diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json index b53f65ab43..53b3c0fcaa 100644 --- a/app/javascript/mastodon/locales/lv.json +++ b/app/javascript/mastodon/locales/lv.json @@ -409,6 +409,7 @@ "lists.subheading": "Tavi saraksti", "load_pending": "{count, plural, one {# jauna lieta} other {# jaunas lietas}}", "loading_indicator.label": "Ielādē…", + "media_gallery.hide": "Paslēpt", "moved_to_account_banner.text": "Tavs konts {disabledAccount} pašlaik ir atspējots, jo Tu pārcēlies uz kontu {movedToAccount}.", "mute_modal.hide_from_notifications": "Paslēpt paziņojumos", "mute_modal.hide_options": "Paslēpt iespējas", diff --git a/config/locales/activerecord.ko.yml b/config/locales/activerecord.ko.yml index 294d614bca..6d437b72b0 100644 --- a/config/locales/activerecord.ko.yml +++ b/config/locales/activerecord.ko.yml @@ -15,6 +15,12 @@ ko: user/invite_request: text: 이유 errors: + attributes: + domain: + invalid: 올바른 도메인 네임이 아닙니다 + messages: + invalid_domain_on_line: "%{value}는 올바른 도메인 네임이 아닙니다" + too_many_lines: "%{limit}줄 제한을 초과합니다" models: account: attributes: diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 3217a8e49a..216e468762 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -22,6 +22,7 @@ ko: admin: account_actions: action: 조치 취하기 + already_silenced: 이 계정은 이미 제한되었습니다. already_suspended: 이 계정은 이미 정지되었습니다. title: "%{acct} 계정에 중재 취하기" account_moderation_notes: @@ -1143,6 +1144,12 @@ ko: view_strikes: 내 계정에 대한 과거 중재 기록 보기 too_fast: 너무 빠르게 양식이 제출되었습니다, 다시 시도하세요. use_security_key: 보안 키 사용 + author_attribution: + example_title: 예시 텍스트 + hint_html: 링크가 마스토돈에 공유될 때 내가 어떻게 표시될 지를 제어합니다. + more_from_html: "%{name}의 게시물 더 보기" + s_blog: "%{name}의 블로그" + title: 작성자 기여 challenge: confirm: 계속 hint_html: "팁: 한 시간 동안 다시 암호를 묻지 않을 것입니다." @@ -1906,6 +1913,7 @@ ko: instructions_html: 웹사이트에 아래 코드를 복사해 붙여 넣으세요. 그리고 "프로필 수정" 탭에서 그 웹사이트 주소를 프로필의 추가 필드 중 하나에 넣고 변경사항을 저장하세요. verification: 검증 verified_links: 인증된 링크들 + website_verification: 웹사이트 인증 webauthn_credentials: add: 보안 키 추가 create: diff --git a/config/locales/lv.yml b/config/locales/lv.yml index be6555a386..4aeec5ceca 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -1114,7 +1114,7 @@ lv: new_confirmation_instructions_sent: Pēc dažām minūtēm saņemsi jaunu e-pasta ziņojumu ar apstiprinājuma saiti. title: Pārbaudi savu iesūtni sign_in: - preamble_html: Jāpiesakās ar saviem %{domain} piekļuves datiem. Ja Tavs konts tiek mitināts citā serverī, Tu nevarēsi šeit pieteikties. + preamble_html: Jāpiesakās ar saviem %{domain} piekļuves datiem. Ja konts tiek mitināts citā serverī, šeit nevarēs pieteikties. title: Pierakstīties %{domain} sign_up: manual_review: Reģistrācijas domēnā %{domain} manuāli pārbauda mūsu moderatori. Lai palīdzētu mums apstrādāt tavu reģistrāciju, uzraksti mazliet par sevi un to, kāpēc vēlies kontu %{domain}. diff --git a/config/locales/simple_form.ko.yml b/config/locales/simple_form.ko.yml index fee07fa5e0..a649b4ec5a 100644 --- a/config/locales/simple_form.ko.yml +++ b/config/locales/simple_form.ko.yml @@ -3,6 +3,7 @@ ko: simple_form: hints: account: + attribution_domains_as_text: 가짜 기여로부터 보호합니다. discoverable: 내 공개 게시물과 프로필이 마스토돈의 다양한 추천 기능에 나타날 수 있고 프로필이 다른 사용자에게 제안될 수 있습니다 display_name: 진짜 이름 또는 재미난 이름. fields: 홈페이지, 호칭, 나이, 뭐든지 적고 싶은 것들. @@ -143,6 +144,7 @@ ko: url: 이벤트가 어디로 전송될 지 labels: account: + attribution_domains_as_text: 특정 웹사이트만 허용하기 discoverable: 발견하기 알고리즘에 프로필과 게시물을 추천하기 fields: name: 라벨 From ef4d6ab98875891716fa2b9ce22ed34afc58a53f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 19 Sep 2024 12:52:46 +0200 Subject: [PATCH 24/93] Fix browser glitch caused by two overlapping scroll animations in web UI (#31960) --- .../features/ui/components/columns_area.jsx | 32 +------------------ app/javascript/mastodon/scroll.ts | 25 +++++++++------ app/javascript/mastodon/test_helpers.tsx | 8 +++++ 3 files changed, 25 insertions(+), 40 deletions(-) diff --git a/app/javascript/mastodon/features/ui/components/columns_area.jsx b/app/javascript/mastodon/features/ui/components/columns_area.jsx index 5a2ea8c2c8..ff76d5bcb2 100644 --- a/app/javascript/mastodon/features/ui/components/columns_area.jsx +++ b/app/javascript/mastodon/features/ui/components/columns_area.jsx @@ -4,8 +4,6 @@ import { Children, cloneElement, useCallback } from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { supportsPassiveEvents } from 'detect-passive-events'; - import { scrollRight } from '../../../scroll'; import BundleContainer from '../containers/bundle_container'; import { @@ -71,10 +69,6 @@ export default class ColumnsArea extends ImmutablePureComponent { }; componentDidMount() { - if (!this.props.singleColumn) { - this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false); - } - if (this.mediaQuery) { if (this.mediaQuery.addEventListener) { this.mediaQuery.addEventListener('change', this.handleLayoutChange); @@ -87,23 +81,7 @@ export default class ColumnsArea extends ImmutablePureComponent { this.isRtlLayout = document.getElementsByTagName('body')[0].classList.contains('rtl'); } - UNSAFE_componentWillUpdate(nextProps) { - if (this.props.singleColumn !== nextProps.singleColumn && nextProps.singleColumn) { - this.node.removeEventListener('wheel', this.handleWheel); - } - } - - componentDidUpdate(prevProps) { - if (this.props.singleColumn !== prevProps.singleColumn && !this.props.singleColumn) { - this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false); - } - } - componentWillUnmount () { - if (!this.props.singleColumn) { - this.node.removeEventListener('wheel', this.handleWheel); - } - if (this.mediaQuery) { if (this.mediaQuery.removeEventListener) { this.mediaQuery.removeEventListener('change', this.handleLayoutChange); @@ -116,7 +94,7 @@ export default class ColumnsArea extends ImmutablePureComponent { handleChildrenContentChange() { if (!this.props.singleColumn) { const modifier = this.isRtlLayout ? -1 : 1; - this._interruptScrollAnimation = scrollRight(this.node, (this.node.scrollWidth - window.innerWidth) * modifier); + scrollRight(this.node, (this.node.scrollWidth - window.innerWidth) * modifier); } } @@ -124,14 +102,6 @@ export default class ColumnsArea extends ImmutablePureComponent { this.setState({ renderComposePanel: !e.matches }); }; - handleWheel = () => { - if (typeof this._interruptScrollAnimation !== 'function') { - return; - } - - this._interruptScrollAnimation(); - }; - setRef = (node) => { this.node = node; }; diff --git a/app/javascript/mastodon/scroll.ts b/app/javascript/mastodon/scroll.ts index 35e13a4527..0756edb4ce 100644 --- a/app/javascript/mastodon/scroll.ts +++ b/app/javascript/mastodon/scroll.ts @@ -38,13 +38,20 @@ const scroll = ( const isScrollBehaviorSupported = 'scrollBehavior' in document.documentElement.style; -export const scrollRight = (node: Element, position: number) => { - if (isScrollBehaviorSupported) - node.scrollTo({ left: position, behavior: 'smooth' }); - else scroll(node, 'scrollLeft', position); -}; +export const scrollRight = (node: Element, position: number) => + requestIdleCallback(() => { + if (isScrollBehaviorSupported) { + node.scrollTo({ left: position, behavior: 'smooth' }); + } else { + scroll(node, 'scrollLeft', position); + } + }); -export const scrollTop = (node: Element) => { - if (isScrollBehaviorSupported) node.scrollTo({ top: 0, behavior: 'smooth' }); - else scroll(node, 'scrollTop', 0); -}; +export const scrollTop = (node: Element) => + requestIdleCallback(() => { + if (isScrollBehaviorSupported) { + node.scrollTo({ top: 0, behavior: 'smooth' }); + } else { + scroll(node, 'scrollTop', 0); + } + }); diff --git a/app/javascript/mastodon/test_helpers.tsx b/app/javascript/mastodon/test_helpers.tsx index f405090730..8a6f5a3377 100644 --- a/app/javascript/mastodon/test_helpers.tsx +++ b/app/javascript/mastodon/test_helpers.tsx @@ -8,6 +8,14 @@ import { render as rtlRender } from '@testing-library/react'; import { IdentityContext } from './identity_context'; +beforeEach(() => { + global.requestIdleCallback = jest + .fn() + .mockImplementation((fn: () => void) => { + fn(); + }); +}); + function render( ui: React.ReactElement, { From 6801afa12f3611c86aeec78a2d43ffa8323ecec6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:56:09 +0000 Subject: [PATCH 25/93] Update dependency devise-two-factor to v6 [SECURITY] (#31957) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: David Roetzel --- Gemfile.lock | 2 +- spec/models/user_spec.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 41a8b68fae..79e542014c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -197,7 +197,7 @@ GEM railties (>= 4.1.0) responders warden (~> 1.2.3) - devise-two-factor (5.1.0) + devise-two-factor (6.0.0) activesupport (~> 7.0) devise (~> 4.0) railties (~> 7.0) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index fcff4c0d3b..972453cd69 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -4,6 +4,8 @@ require 'rails_helper' require 'devise_two_factor/spec_helpers' RSpec.describe User do + subject { described_class.new(account: account) } + let(:password) { 'abcd1234' } let(:account) { Fabricate(:account, username: 'alice') } From 2946a9286b1b4a5323f9f7ea6e7b29b6ca5d309d Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 19 Sep 2024 09:38:32 -0400 Subject: [PATCH 26/93] Use `headers` shorthand in mailers (#31956) --- app/mailers/admin_mailer.rb | 8 +++++--- app/mailers/application_mailer.rb | 8 +++++--- app/mailers/notification_mailer.rb | 28 ++++++++++++++++------------ 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/app/mailers/admin_mailer.rb b/app/mailers/admin_mailer.rb index 8dd7b6e59f..72a2c2e64e 100644 --- a/app/mailers/admin_mailer.rb +++ b/app/mailers/admin_mailer.rb @@ -56,9 +56,11 @@ class AdminMailer < ApplicationMailer def new_critical_software_updates @software_updates = SoftwareUpdate.where(urgent: true).to_a.sort_by(&:gem_version) - headers['Priority'] = 'urgent' - headers['X-Priority'] = '1' - headers['Importance'] = 'high' + headers( + 'Importance' => 'high', + 'Priority' => 'urgent', + 'X-Priority' => '1' + ) locale_for_account(@me) do mail subject: default_i18n_subject(instance: @instance) diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 35f0b5fee1..9a209aa77b 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -16,8 +16,10 @@ class ApplicationMailer < ActionMailer::Base end def set_autoreply_headers! - headers['Precedence'] = 'list' - headers['X-Auto-Response-Suppress'] = 'All' - headers['Auto-Submitted'] = 'auto-generated' + headers( + 'Auto-Submitted' => 'auto-generated', + 'Precedence' => 'list', + 'X-Auto-Response-Suppress' => 'All' + ) end end diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb index 4eb38ec340..6b21b4bedd 100644 --- a/app/mailers/notification_mailer.rb +++ b/app/mailers/notification_mailer.rb @@ -6,7 +6,10 @@ class NotificationMailer < ApplicationMailer :routing before_action :process_params - before_action :set_status, only: [:mention, :favourite, :reblog] + with_options only: %i(mention favourite reblog) do + before_action :set_status + after_action :thread_by_conversation! + end before_action :set_account, only: [:follow, :favourite, :reblog, :follow_request] after_action :set_list_headers! @@ -18,7 +21,6 @@ class NotificationMailer < ApplicationMailer return unless @user.functional? && @status.present? locale_for_account(@me) do - thread_by_conversation(@status.conversation) mail subject: default_i18n_subject(name: @status.account.acct) end end @@ -35,7 +37,6 @@ class NotificationMailer < ApplicationMailer return unless @user.functional? && @status.present? locale_for_account(@me) do - thread_by_conversation(@status.conversation) mail subject: default_i18n_subject(name: @account.acct) end end @@ -44,7 +45,6 @@ class NotificationMailer < ApplicationMailer return unless @user.functional? && @status.present? locale_for_account(@me) do - thread_by_conversation(@status.conversation) mail subject: default_i18n_subject(name: @account.acct) end end @@ -76,17 +76,21 @@ class NotificationMailer < ApplicationMailer end def set_list_headers! - headers['List-ID'] = "<#{@type}.#{@me.username}.#{Rails.configuration.x.local_domain}>" - headers['List-Unsubscribe'] = "<#{@unsubscribe_url}>" - headers['List-Unsubscribe-Post'] = 'List-Unsubscribe=One-Click' + headers( + 'List-ID' => "<#{@type}.#{@me.username}.#{Rails.configuration.x.local_domain}>", + 'List-Unsubscribe-Post' => 'List-Unsubscribe=One-Click', + 'List-Unsubscribe' => "<#{@unsubscribe_url}>" + ) end - def thread_by_conversation(conversation) - return if conversation.nil? + def thread_by_conversation! + return if @status.conversation.nil? - msg_id = "" + conversation_message_id = "" - headers['In-Reply-To'] = msg_id - headers['References'] = msg_id + headers( + 'In-Reply-To' => conversation_message_id, + 'References' => conversation_message_id + ) end end From 5a8f2fe31d15a2a07384d118b5cbcd10ad714be4 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 19 Sep 2024 09:43:40 -0400 Subject: [PATCH 27/93] Convert `settings/exports` controller spec to system/request specs (#31965) --- .../settings/exports_controller_spec.rb | 47 ------------------- spec/requests/settings/exports_spec.rb | 25 ++++++++++ spec/system/settings/exports_spec.rb | 40 ++++++++++++++++ 3 files changed, 65 insertions(+), 47 deletions(-) delete mode 100644 spec/controllers/settings/exports_controller_spec.rb create mode 100644 spec/requests/settings/exports_spec.rb create mode 100644 spec/system/settings/exports_spec.rb diff --git a/spec/controllers/settings/exports_controller_spec.rb b/spec/controllers/settings/exports_controller_spec.rb deleted file mode 100644 index 1eafabc7e5..0000000000 --- a/spec/controllers/settings/exports_controller_spec.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Settings::ExportsController do - render_views - - describe 'GET #show' do - context 'when signed in' do - let(:user) { Fabricate(:user) } - - before do - sign_in user, scope: :user - get :show - end - - it 'returns http success with private cache control headers', :aggregate_failures do - expect(response).to have_http_status(200) - expect(response.headers['Cache-Control']).to include('private, no-store') - end - end - - context 'when not signed in' do - it 'redirects' do - get :show - expect(response).to redirect_to '/auth/sign_in' - end - end - end - - describe 'POST #create' do - before do - sign_in Fabricate(:user), scope: :user - end - - it 'redirects to settings_export_path' do - post :create - expect(response).to redirect_to(settings_export_path) - end - - it 'queues BackupWorker job by 1' do - expect do - post :create - end.to change(BackupWorker.jobs, :size).by(1) - end - end -end diff --git a/spec/requests/settings/exports_spec.rb b/spec/requests/settings/exports_spec.rb new file mode 100644 index 0000000000..db823ac770 --- /dev/null +++ b/spec/requests/settings/exports_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Settings / Exports' do + context 'when not signed in' do + describe 'GET /settings/export' do + it 'redirects to sign in page' do + get settings_export_path + + expect(response) + .to redirect_to new_user_session_path + end + end + + describe 'POST /settings/export' do + it 'redirects to sign in page' do + post settings_export_path + + expect(response) + .to redirect_to new_user_session_path + end + end + end +end diff --git a/spec/system/settings/exports_spec.rb b/spec/system/settings/exports_spec.rb new file mode 100644 index 0000000000..2cc2961a0b --- /dev/null +++ b/spec/system/settings/exports_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Export page' do + let(:user) { Fabricate :user } + + before { sign_in user } + + describe 'Viewing the export page' do + context 'when signed in' do + it 'shows the export page', :aggregate_failures do + visit settings_export_path + + expect(page) + .to have_content(takeout_summary) + .and have_private_cache_control + end + end + end + + describe 'Creating a new archive' do + it 'queues a worker and redirects' do + visit settings_export_path + + expect { request_archive } + .to change(BackupWorker.jobs, :size).by(1) + expect(page) + .to have_content(takeout_summary) + end + + def request_archive + click_on I18n.t('exports.archive_takeout.request') + end + end + + def takeout_summary + I18n.t('settings.export') + end +end From 57a38f071b0ffd74b813516e24c9e86a23c4d467 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 19 Sep 2024 16:58:33 +0200 Subject: [PATCH 28/93] Fix custom `history.push` and `history.replace` building bogus location if path is omitted (#31980) --- app/javascript/mastodon/components/router.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/components/router.tsx b/app/javascript/mastodon/components/router.tsx index 33fb60abb7..558d0307e7 100644 --- a/app/javascript/mastodon/components/router.tsx +++ b/app/javascript/mastodon/components/router.tsx @@ -51,7 +51,8 @@ function normalizePath( if ( layoutFromWindow() === 'multi-column' && - !location.pathname?.startsWith('/deck') + location.pathname && + !location.pathname.startsWith('/deck') ) { location.pathname = `/deck${location.pathname}`; } From ae03e4ffc6a25c8a3e3c61701180fdc1ea194141 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Thu, 19 Sep 2024 17:34:08 +0200 Subject: [PATCH 29/93] Update directory page options to use URL params (#31977) --- app/javascript/hooks/useSearchParam.ts | 31 ++++++++++++++++ .../mastodon/features/directory/index.tsx | 35 +++++++++++-------- 2 files changed, 51 insertions(+), 15 deletions(-) create mode 100644 app/javascript/hooks/useSearchParam.ts diff --git a/app/javascript/hooks/useSearchParam.ts b/app/javascript/hooks/useSearchParam.ts new file mode 100644 index 0000000000..2df8c0b3a9 --- /dev/null +++ b/app/javascript/hooks/useSearchParam.ts @@ -0,0 +1,31 @@ +import { useMemo, useCallback } from 'react'; + +import { useLocation, useHistory } from 'react-router'; + +export function useSearchParams() { + const { search } = useLocation(); + + return useMemo(() => new URLSearchParams(search), [search]); +} + +export function useSearchParam(name: string, defaultValue?: string) { + const searchParams = useSearchParams(); + const history = useHistory(); + + const value = searchParams.get(name) ?? defaultValue; + + const setValue = useCallback( + (value: string | null) => { + if (value === null) { + searchParams.delete(name); + } else { + searchParams.set(name, value); + } + + history.push({ search: searchParams.toString() }); + }, + [history, name, searchParams], + ); + + return [value, setValue] as const; +} diff --git a/app/javascript/mastodon/features/directory/index.tsx b/app/javascript/mastodon/features/directory/index.tsx index 51d283a482..d0e57600bb 100644 --- a/app/javascript/mastodon/features/directory/index.tsx +++ b/app/javascript/mastodon/features/directory/index.tsx @@ -1,5 +1,5 @@ import type { ChangeEventHandler } from 'react'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef } from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -23,6 +23,8 @@ import { RadioButton } from 'mastodon/components/radio_button'; import ScrollContainer from 'mastodon/containers/scroll_container'; import { useAppDispatch, useAppSelector } from 'mastodon/store'; +import { useSearchParam } from '../../../hooks/useSearchParam'; + import { AccountCard } from './components/account_card'; const messages = defineMessages({ @@ -47,18 +49,19 @@ export const Directory: React.FC<{ const intl = useIntl(); const dispatch = useAppDispatch(); - const [state, setState] = useState<{ - order: string | null; - local: boolean | null; - }>({ - order: null, - local: null, - }); - const column = useRef(null); - const order = state.order ?? params?.order ?? 'active'; - const local = state.local ?? params?.local ?? false; + const [orderParam, setOrderParam] = useSearchParam('order'); + const [localParam, setLocalParam] = useSearchParam('local'); + + let localParamBool: boolean | undefined; + + if (localParam === 'false') { + localParamBool = false; + } + + const order = orderParam ?? params?.order ?? 'active'; + const local = localParamBool ?? params?.local ?? true; const handlePin = useCallback(() => { if (columnId) { @@ -101,10 +104,10 @@ export const Directory: React.FC<{ if (columnId) { dispatch(changeColumnParams(columnId, ['order'], e.target.value)); } else { - setState((s) => ({ order: e.target.value, local: s.local })); + setOrderParam(e.target.value); } }, - [dispatch, columnId], + [dispatch, columnId, setOrderParam], ); const handleChangeLocal = useCallback>( @@ -113,11 +116,13 @@ export const Directory: React.FC<{ dispatch( changeColumnParams(columnId, ['local'], e.target.value === '1'), ); + } else if (e.target.value === '1') { + setLocalParam('true'); } else { - setState((s) => ({ local: e.target.value === '1', order: s.order })); + setLocalParam('false'); } }, - [dispatch, columnId], + [dispatch, columnId, setLocalParam], ); const handleLoadMore = useCallback(() => { From 09459ed0008b42098549c0213916e85f567b0579 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 10:15:14 +0200 Subject: [PATCH 30/93] Update dependency react-select to v5.8.1 (#31982) 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 5372c39f47..99977274a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14689,8 +14689,8 @@ __metadata: linkType: hard "react-select@npm:^5.7.3": - version: 5.8.0 - resolution: "react-select@npm:5.8.0" + version: 5.8.1 + resolution: "react-select@npm:5.8.1" dependencies: "@babel/runtime": "npm:^7.12.0" "@emotion/cache": "npm:^11.4.0" @@ -14704,7 +14704,7 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 10c0/b4b98aaf117ee5cc4642871b7bd51fd0e2697988d0b880f30b21e933ca90258959147117d8aada36713b622e0e4cb06bd18ec02069f3f108896e0d31e69e3c16 + checksum: 10c0/0fd73e1e472105f980e09c86f0e6adbdc9f2f5c1befa275b08c71653becdd1829f596155a81b5085cb86f18b20bf4f4cc439ab5fe23e68f326e169dcfe00ccf6 languageName: node linkType: hard From 9a03902ab602fe49ea8bfbc56cdbed0831e5963f Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 20 Sep 2024 04:16:19 -0400 Subject: [PATCH 31/93] Capture actual behavior in v2/notifications "someone else" dismiss scenario (#31985) --- spec/requests/api/v2/notifications_spec.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/spec/requests/api/v2/notifications_spec.rb b/spec/requests/api/v2/notifications_spec.rb index edf333ecd8..9522a39e0f 100644 --- a/spec/requests/api/v2/notifications_spec.rb +++ b/spec/requests/api/v2/notifications_spec.rb @@ -312,12 +312,15 @@ RSpec.describe 'Notifications' do end context 'when notification belongs to someone else' do - let(:notification) { Fabricate(:notification) } + let(:notification) { Fabricate(:notification, group_key: 'foobar') } - it 'returns http not found' do - subject + it 'leaves the notification alone' do + expect { subject } + .to_not change(Notification, :count) - expect(response).to have_http_status(404) + expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end From 840fd697306ddb8d54768bdc083b1c93cb16fc3a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 10:20:27 +0200 Subject: [PATCH 32/93] Update dependency sass to v1.79.2 (#31992) 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 99977274a1..c90ada78f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15475,15 +15475,15 @@ __metadata: linkType: hard "sass@npm:^1.62.1": - version: 1.79.1 - resolution: "sass@npm:1.79.1" + version: 1.79.2 + resolution: "sass@npm:1.79.2" dependencies: chokidar: "npm:^4.0.0" immutable: "npm:^4.0.0" source-map-js: "npm:>=0.6.2 <2.0.0" bin: sass: sass.js - checksum: 10c0/187bc885bad2e81e5414a146c9e14a28f622b5aba5c1425f4dd6d8ef2045b66c69de93689503886a8d2cc55d6bcce97b05ee87b074628fefd03e762789f931d0 + checksum: 10c0/b637daf133da4fbafbb7e6ae07b01ff7c73e406f3134e66749bf6f712dcc0056c6971d8629d8cc2b186df5ffb2282baa8f1818f35e326b3558ab284e31fdd87d languageName: node linkType: hard From 162f9a3c90dd688cd042f241add098c1ef6c6625 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 08:31:28 +0000 Subject: [PATCH 33/93] New Crowdin Translations (automated) (#31993) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/es-MX.json | 10 +- app/javascript/mastodon/locales/ru.json | 67 +++++++++- config/locales/activerecord.ru.yml | 6 + config/locales/doorkeeper.es-MX.yml | 2 +- config/locales/es-MX.yml | 6 +- config/locales/ru.yml | 145 +++++++++++++++++++++ config/locales/simple_form.es-MX.yml | 2 +- config/locales/simple_form.ru.yml | 9 ++ 8 files changed, 236 insertions(+), 11 deletions(-) diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index 7b09ada9f8..5c8f44d4a7 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -537,11 +537,11 @@ "notification_requests.accept": "Aceptar", "notification_requests.accept_multiple": "{count, plural, one {Aceptar # solicitud…} other {Aceptar # solicitudes…}}", "notification_requests.confirm_accept_multiple.button": "{count, plural, one {Solicitud aceptada} other {Solicitudes aceptadas}}", - "notification_requests.confirm_accept_multiple.message": "Vas a aceptar {count, plural, one {una solicitud} other {# solicitudes}}. ¿Quieres continuar?", - "notification_requests.confirm_accept_multiple.title": "¿Aceptar las solicitudes?", - "notification_requests.confirm_dismiss_multiple.button": "{count, plural, one {Descartar solicitud} other {Descartar solicitudes}}", - "notification_requests.confirm_dismiss_multiple.message": "Vas a descartar {count, plural, one {una solicitud} other {# solicitudes}}. No podrás volver a acceder fácilmente a {count, plural, one {ella} other {ellas}} de nuevo. ¿Seguro que quieres continuar?", - "notification_requests.confirm_dismiss_multiple.title": "¿Descartar las solicitudes?", + "notification_requests.confirm_accept_multiple.message": "Estás por aceptar {count, plural, one {una solicitud de notificación} other {# solicitudes de notificación}}. ¿Estás seguro de que quieres continuar?", + "notification_requests.confirm_accept_multiple.title": "¿Deseas aceptar las solicitudes de notificación?", + "notification_requests.confirm_dismiss_multiple.button": "{count, plural, one {Solicitud descartada} other {Solicitudes descartadas}}", + "notification_requests.confirm_dismiss_multiple.message": "Estás por descartar {count, plural, one {una solicitud de notificación} other {# solicitudes de notificación}}. No serás capaz de acceder fácilmente a {count, plural, one {ella} other {ellas}} de nuevo. ¿Estás seguro de que quieres continuar?", + "notification_requests.confirm_dismiss_multiple.title": "¿Deseas descartar las solicitudes de notificación?", "notification_requests.dismiss": "Descartar", "notification_requests.dismiss_multiple": "{count, plural, one {Descartar # solicitud…} other {Descartar # solicitudes…}}", "notification_requests.edit_selection": "Editar", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index 4a03f2b6e8..d97b1658c5 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -34,7 +34,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}", @@ -48,7 +50,7 @@ "account.moved_to": "У {name} теперь новый аккаунт:", "account.mute": "Игнорировать @{name}", "account.mute_notifications_short": "Отключить уведомления", - "account.mute_short": "Глохни!", + "account.mute_short": "Приглушить", "account.muted": "Игнорируется", "account.mutual": "Взаимно", "account.no_bio": "Описание не предоставлено.", @@ -94,6 +96,8 @@ "block_modal.title": "Заблокировать пользователя?", "block_modal.you_wont_see_mentions": "Вы не увидите записи, которые упоминают его.", "boost_modal.combo": "{combo}, чтобы пропустить это в следующий раз", + "boost_modal.reblog": "Повысить пост?", + "boost_modal.undo_reblog": "Разгрузить пост?", "bundle_column_error.copy_stacktrace": "Скопировать отчет об ошибке", "bundle_column_error.error.body": "Запрошенная страница не может быть отображена. Это может быть вызвано ошибкой в нашем коде или проблемой совместимости браузера.", "bundle_column_error.error.title": "О нет!", @@ -298,6 +302,8 @@ "filter_modal.select_filter.subtitle": "Используйте существующую категорию или создайте новую", "filter_modal.select_filter.title": "Фильтровать этот пост", "filter_modal.title.status": "Фильтровать пост", + "filter_warning.matches_filter": "Соответствует фильтру \"{title}\"", + "filtered_notifications_banner.pending_requests": "Вы можете знать {count, plural, =0 {ни один} one {один человек} other {# люди}}", "filtered_notifications_banner.title": "Отфильтрованные уведомления", "firehose.all": "Все", "firehose.local": "Текущий сервер", @@ -346,6 +352,14 @@ "hashtag.follow": "Подписаться на новые посты", "hashtag.unfollow": "Отписаться", "hashtags.and_other": "...и {count, plural, other {# ещё}}", + "hints.profiles.followers_may_be_missing": "Последователи для этого профиля могут отсутствовать.", + "hints.profiles.follows_may_be_missing": "Фолловеры для этого профиля могут отсутствовать.", + "hints.profiles.posts_may_be_missing": "Некоторые сообщения из этого профиля могут отсутствовать.", + "hints.profiles.see_more_followers": "Посмотреть больше подписчиков на {domain}", + "hints.profiles.see_more_follows": "Смотрите другие материалы по теме {domain}", + "hints.profiles.see_more_posts": "Посмотреть другие сообщения на {domain}", + "hints.threads.replies_may_be_missing": "Ответы с других серверов могут отсутствовать.", + "hints.threads.see_more": "Посмотреть другие ответы на {domain}", "home.column_settings.show_reblogs": "Показывать продвижения", "home.column_settings.show_replies": "Показывать ответы", "home.hide_announcements": "Скрыть объявления", @@ -353,7 +367,17 @@ "home.pending_critical_update.link": "Посмотреть обновления", "home.pending_critical_update.title": "Доступно критическое обновление безопасности!", "home.show_announcements": "Показать объявления", + "ignore_notifications_modal.disclaimer": "Mastodon не может сообщить пользователям, что вы проигнорировали их уведомления. Игнорирование уведомлений не остановит отправку самих сообщений.", + "ignore_notifications_modal.filter_instead": "Фильтр вместо", "ignore_notifications_modal.filter_to_act_users": "Вы и далее сможете принять, отвергнуть и жаловаться на пользователей", + "ignore_notifications_modal.filter_to_avoid_confusion": "Фильтрация помогает избежать потенциальной путаницы", + "ignore_notifications_modal.filter_to_review_separately": "Вы можете просматривать отфильтрованные уведомления отдельно", + "ignore_notifications_modal.ignore": "Игнорировать уведомления", + "ignore_notifications_modal.limited_accounts_title": "Игнорировать уведомления от модерируемых аккаунтов?", + "ignore_notifications_modal.new_accounts_title": "Игнорировать уведомления от новых аккаунтов?", + "ignore_notifications_modal.not_followers_title": "Игнорировать уведомления от людей, которые не следят за вами?", + "ignore_notifications_modal.not_following_title": "Игнорировать уведомления от людей, за которыми вы не следите?", + "ignore_notifications_modal.private_mentions_title": "Игнорировать уведомления о нежелательных личных сообщениях?", "interaction_modal.description.favourite": "С учётной записью Mastodon, вы можете добавить этот пост в избранное, чтобы сохранить его на будущее и дать автору знать, что пост вам понравился.", "interaction_modal.description.follow": "С учётной записью Mastodon вы можете подписаться на {name}, чтобы получать их посты в своей домашней ленте.", "interaction_modal.description.reblog": "С учётной записью Mastodon, вы можете продвинуть этот пост, чтобы поделиться им со своими подписчиками.", @@ -432,6 +456,7 @@ "lists.subheading": "Ваши списки", "load_pending": "{count, plural, one {# новый элемент} few {# новых элемента} other {# новых элементов}}", "loading_indicator.label": "Загрузка…", + "media_gallery.hide": "Скрыть", "moved_to_account_banner.text": "Ваша учетная запись {disabledAccount} в настоящее время заморожена, потому что вы переехали на {movedToAccount}.", "mute_modal.hide_from_notifications": "Скрыть из уведомлений", "mute_modal.hide_options": "Скрыть параметры", @@ -443,6 +468,7 @@ "mute_modal.you_wont_see_mentions": "Вы не увидите постов, которые их упоминают.", "mute_modal.you_wont_see_posts": "Они по-прежнему смогут видеть ваши посты, но вы не сможете видеть их посты.", "navigation_bar.about": "О проекте", + "navigation_bar.administration": "Администрация", "navigation_bar.advanced_interface": "Включить многоколоночный интерфейс", "navigation_bar.blocks": "Заблокированные пользователи", "navigation_bar.bookmarks": "Закладки", @@ -459,6 +485,7 @@ "navigation_bar.follows_and_followers": "Подписки и подписчики", "navigation_bar.lists": "Списки", "navigation_bar.logout": "Выйти", + "navigation_bar.moderation": "Модерация", "navigation_bar.mutes": "Игнорируемые пользователи", "navigation_bar.opened_in_classic_interface": "Сообщения, учётные записи и другие специфические страницы по умолчанию открываются в классическом веб-интерфейсе.", "navigation_bar.personal": "Личное", @@ -469,10 +496,22 @@ "navigation_bar.security": "Безопасность", "not_signed_in_indicator.not_signed_in": "Вам нужно войти, чтобы иметь доступ к этому ресурсу.", "notification.admin.report": "{name} сообщил о {target}", + "notification.admin.report_account": "{name} сообщил {count, plural, one {один пост} other {# постов}} от {target} для {category}", + "notification.admin.report_account_other": "{name} сообщил {count, plural, one {одно сообщение} other {# сообщений}} от {target}", + "notification.admin.report_statuses": "{name} сообщил {target} для {category}", + "notification.admin.report_statuses_other": "{name} сообщает {target}", "notification.admin.sign_up": "{name} зарегистрирован", + "notification.admin.sign_up.name_and_others": "{name} и {count, plural, one {# другой} other {# другие}} подписались", "notification.favourite": "{name} добавил(а) ваш пост в избранное", + "notification.favourite.name_and_others_with_link": "{name} и {count, plural, one {# другие} other {# другие}} отдали предпочтение вашему посту", "notification.follow": "{name} подписался (-лась) на вас", "notification.follow_request": "{name} отправил запрос на подписку", + "notification.follow_request.name_and_others": "{name} и {count, plural, one {# другие} other {# другие}} последовали за тобой", + "notification.label.mention": "Упоминание", + "notification.label.private_mention": "Частное упоминание", + "notification.label.private_reply": "Частный ответ", + "notification.label.reply": "Ответить", + "notification.mention": "Упоминание", "notification.moderation-warning.learn_more": "Узнать больше", "notification.moderation_warning": "Вы получили предупреждение от модерации", "notification.moderation_warning.action_delete_statuses": "Некоторые из ваших публикаций были удалены.", @@ -483,7 +522,9 @@ "notification.moderation_warning.action_silence": "Ваша учётная запись была ограничена.", "notification.moderation_warning.action_suspend": "Действие вашей учётной записи приостановлено.", "notification.own_poll": "Ваш опрос закончился", + "notification.poll": "Голосование, в котором вы приняли участие, завершилось", "notification.reblog": "{name} продвинул(а) ваш пост", + "notification.reblog.name_and_others_with_link": "{name} и {count, plural, one {# other} other {# others}} увеличили ваш пост", "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 {# аккаунт} few {# аккаунта} other {# аккаунтов}}, на которые вы подписаны.", @@ -492,10 +533,19 @@ "notification.status": "{name} только что запостил", "notification.update": "{name} изменил(а) пост", "notification_requests.accept": "Принять", + "notification_requests.confirm_accept_multiple.button": "{count, plural, one {Принять запрос} other {Принять запросы}}", + "notification_requests.confirm_accept_multiple.title": "Принимать запросы на уведомления?", + "notification_requests.confirm_dismiss_multiple.title": "Отклонять запросы на уведомления?", "notification_requests.dismiss": "Отклонить", + "notification_requests.edit_selection": "Редактировать", + "notification_requests.exit_selection": "Готово", + "notification_requests.explainer_for_limited_account": "Уведомления от этой учетной записи были отфильтрованы, поскольку учетная запись была ограничена модератором.", + "notification_requests.explainer_for_limited_remote_account": "Уведомления от этой учетной записи были отфильтрованы, поскольку учетная запись или ее сервер были ограничены модератором.", "notification_requests.maximize": "Развернуть", + "notification_requests.minimize_banner": "Минимизация баннера отфильтрованных уведомлений", "notification_requests.notifications_from": "Уведомления от {name}", "notification_requests.title": "Отфильтрованные уведомления", + "notification_requests.view": "Просмотр уведомлений", "notifications.clear": "Очистить уведомления", "notifications.clear_confirmation": "Вы уверены, что хотите очистить все уведомления?", "notifications.clear_title": "Сбросить уведомления?", @@ -530,7 +580,14 @@ "notifications.permission_denied": "Уведомления на рабочем столе недоступны, так как вы запретили их отправку в браузере. Проверьте настройки для сайта, чтобы включить их обратно.", "notifications.permission_denied_alert": "Уведомления на рабочем столе недоступны, так как вы ранее отклонили запрос на их отправку.", "notifications.permission_required": "Чтобы включить уведомления на рабочем столе, необходимо разрешить их в браузере.", + "notifications.policy.accept": "Принять", + "notifications.policy.accept_hint": "Показать в уведомлениях", "notifications.policy.drop": "Игнорируем", + "notifications.policy.drop_hint": "Отправить в пустоту, чтобы никогда больше не увидеть", + "notifications.policy.filter": "Фильтр", + "notifications.policy.filter_hint": "Отправка в папку фильтрованных уведомлений", + "notifications.policy.filter_limited_accounts_hint": "Ограничено модераторами сервера", + "notifications.policy.filter_limited_accounts_title": "Модерируемые аккаунты", "notifications.policy.filter_new_accounts.hint": "Создано в течение последних {days, plural, one {один день} few {# дней} many {# дней} other {# дня}}", "notifications.policy.filter_new_accounts_title": "Новые учётные записи", "notifications.policy.filter_not_followers_title": "Люди, не подписанные на вас", @@ -538,6 +595,7 @@ "notifications.policy.filter_not_following_title": "Люди, на которых вы не подписаны", "notifications.policy.filter_private_mentions_hint": "Фильтруется, если только это не ответ на ваше собственное упоминание или если вы подписаны на отправителя", "notifications.policy.filter_private_mentions_title": "Нежелательные личные упоминания", + "notifications.policy.title": "………Управлять уведомлениями от…", "notifications_permission_banner.enable": "Включить уведомления", "notifications_permission_banner.how_to_control": "Получайте уведомления даже когда Mastodon закрыт, включив уведомления на рабочем столе. А чтобы лишний шум не отвлекал, вы можете настроить какие уведомления вы хотите получать, нажав на кнопку {icon} выше.", "notifications_permission_banner.title": "Будьте в курсе происходящего", @@ -666,6 +724,7 @@ "report_notification.categories.legal": "Правовая информация", "report_notification.categories.legal_sentence": "срамной контент", "report_notification.categories.other": "Прочее", + "report_notification.categories.other_sentence": "другое", "report_notification.categories.spam": "Спам", "report_notification.categories.spam_sentence": "спам", "report_notification.categories.violation": "Нарушение правил", @@ -696,8 +755,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}", @@ -707,6 +769,7 @@ "status.bookmark": "Сохранить в закладки", "status.cancel_reblog_private": "Не продвигать", "status.cannot_reblog": "Этот пост не может быть продвинут", + "status.continued_thread": "Продолжение темы", "status.copy": "Скопировать ссылку на пост", "status.delete": "Удалить", "status.detailed_status": "Подробный просмотр обсуждения", @@ -715,6 +778,7 @@ "status.edit": "Изменить", "status.edited": "Дата последнего изменения: {date}", "status.edited_x_times": "{count, plural, one {{count} изменение} many {{count} изменений} other {{count} изменения}}", + "status.embed": "Получить код для встраивания", "status.favourite": "Избранное", "status.filter": "Фильтровать этот пост", "status.history.created": "{name} создал {date}", @@ -737,6 +801,7 @@ "status.reblogs.empty": "Никто ещё не продвинул этот пост. Как только кто-то это сделает, они появятся здесь.", "status.redraft": "Создать заново", "status.remove_bookmark": "Убрать из закладок", + "status.replied_in_thread": "Ответил в теме", "status.replied_to": "Ответил(а) {name}", "status.reply": "Ответить", "status.replyAll": "Ответить всем", diff --git a/config/locales/activerecord.ru.yml b/config/locales/activerecord.ru.yml index 92d85af4d9..203d8e2c34 100644 --- a/config/locales/activerecord.ru.yml +++ b/config/locales/activerecord.ru.yml @@ -15,6 +15,12 @@ ru: user/invite_request: text: Причина errors: + attributes: + domain: + invalid: не является действующим доменным именем + messages: + invalid_domain_on_line: "%{value} Не является действительным доменным именем" + too_many_lines: Превышает предел %{limit} строк models: account: attributes: diff --git a/config/locales/doorkeeper.es-MX.yml b/config/locales/doorkeeper.es-MX.yml index b5987676d2..49ff9a1e43 100644 --- a/config/locales/doorkeeper.es-MX.yml +++ b/config/locales/doorkeeper.es-MX.yml @@ -167,7 +167,7 @@ es-MX: admin:write:reports: realizar acciones de moderación en informes crypto: usar cifrado de extremo a extremo follow: seguir, bloquear, desbloquear y dejar de seguir cuentas - profile: leer sólo la información del perfil de tu cuenta + profile: leer solamente la información del perfil de tu cuenta push: recibir tus notificaciones push read: leer los datos de tu cuenta read:accounts: ver información de cuentas diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml index b39b23c617..8aac7cbb47 100644 --- a/config/locales/es-MX.yml +++ b/config/locales/es-MX.yml @@ -202,10 +202,10 @@ es-MX: destroy_user_role: Destruir Rol disable_2fa_user: Deshabilitar 2FA disable_custom_emoji: Deshabilitar Emoji Personalizado - disable_sign_in_token_auth_user: Deshabilitar la Autenticación por Token de Correo Electrónico para el Usuario + disable_sign_in_token_auth_user: Deshabilitar la autenticación por token de correo electrónico para el usuario disable_user: Deshabilitar Usuario enable_custom_emoji: Habilitar Emoji Personalizado - enable_sign_in_token_auth_user: Habilitar la Autenticación por Token de Correo Electrónico para el Usuario + enable_sign_in_token_auth_user: Habilitar la autenticación por token de correo electrónico para el usuario enable_user: Habilitar Usuario memorialize_account: Transformar en Cuenta Conmemorativa promote_user: Promover Usuario @@ -896,7 +896,7 @@ es-MX: reviewed: Revisada title: Estado trendable: Puede ser tendencia - unreviewed: Sin revisar + unreviewed: No revisado usable: Disponible name: Nombre newest: Más reciente diff --git a/config/locales/ru.yml b/config/locales/ru.yml index f1198c3f16..3877727490 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -28,6 +28,8 @@ ru: admin: account_actions: action: Выполнить действие + already_silenced: Эта учетная запись уже ограничена. + already_suspended: Действие этой учетной записи уже приостановлено. title: Произвести модерацию учётной записи %{acct} account_moderation_notes: create: Создать @@ -49,6 +51,7 @@ ru: title: Сменить e-mail для %{username} change_role: changed_msg: Роль успешно изменена! + edit_roles: Управление ролями пользователей label: Изменить роль no_role: Нет роли title: Изменить роль %{username} @@ -61,6 +64,7 @@ ru: demote: Разжаловать destroyed_msg: Данные %{username} поставлены в очередь на удаление disable: Заморозка + disable_sign_in_token_auth: Отключите аутентификацию с помощью маркера электронной почты disable_two_factor_authentication: Отключить 2FA disabled: Отключено display_name: Отображаемое имя @@ -69,6 +73,7 @@ ru: email: E-mail email_status: Статус e-mail enable: Включить + enable_sign_in_token_auth: Включите аутентификацию с помощью маркера электронной почты enabled: Включен enabled_msg: Учётная запись %{username} успешно разморожена followers: Подписчики @@ -135,6 +140,7 @@ ru: resubscribe: Переподписаться role: Роль search: Поиск + search_same_email_domain: Другие пользователи с тем же почтовым доменом search_same_ip: Другие пользователи с таким же IP security: Безопасность security_measures: @@ -180,17 +186,21 @@ ru: confirm_user: Подтверждение пользователей create_account_warning: Выдача предупреждения create_announcement: Создание объявлений + create_canonical_email_block: Создать блок электронной почты create_custom_emoji: Добавление эмодзи create_domain_allow: Разрешение доменов create_domain_block: Блокировка доменов + create_email_domain_block: Создать блок домена электронной почты create_ip_block: Создание правил для IP-адресов create_unavailable_domain: Добавление домена в список недоступных create_user_role: Создать роль demote_user: Разжалование пользователей destroy_announcement: Удаление объявлений + destroy_canonical_email_block: Удалить блок электронной почты destroy_custom_emoji: Удаление эмодзи destroy_domain_allow: Отзыв разрешений для доменов destroy_domain_block: Разблокировка доменов + destroy_email_domain_block: Удалить блок домена электронной почты destroy_instance: Очистить домен destroy_ip_block: Удаление правил для IP-адресов destroy_status: Удаление постов @@ -198,8 +208,10 @@ ru: destroy_user_role: Удалить роль disable_2fa_user: Отключение 2FA disable_custom_emoji: Отключение эмодзи + disable_sign_in_token_auth_user: Отключить аутентификацию пользователя с помощью токена электронной почты disable_user: Заморозка пользователей enable_custom_emoji: Включение эмодзи + enable_sign_in_token_auth_user: Включить аутентификацию пользователя с помощью токена электронной почты enable_user: Разморозка пользователей memorialize_account: Присвоение пользователям статуса «мемориала» promote_user: Повышение пользователей @@ -229,20 +241,26 @@ ru: approve_appeal_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}" + confirm_user_html: "%{name} подтвержденный адрес электронной почты пользователя %{target}" create_account_warning_html: "%{name} выдал(а) предупреждение %{target}" create_announcement_html: "%{name} создал(а) новое объявление %{target}" + create_canonical_email_block_html: "%{name} заблокировал письмо с хэшем %{target}" create_custom_emoji_html: "%{name} загрузил(а) новый эмодзи %{target}" create_domain_allow_html: "%{name} разрешил(а) федерацию с доменом %{target}" create_domain_block_html: "%{name} заблокировал(а) домен %{target}" + create_email_domain_block_html: "%{name} заблокированный почтовый домен %{target}" create_ip_block_html: "%{name} создал(а) правило для IP %{target}" create_unavailable_domain_html: "%{name} приостановил доставку на узел %{target}" create_user_role_html: "%{name} создал(а) роль %{target}" demote_user_html: "%{name} разжаловал(а) пользователя %{target}" destroy_announcement_html: "%{name} удалил(а) объявление %{target}" + destroy_canonical_email_block_html: "%{name} разблокированное письмо с хэшем %{target}" destroy_custom_emoji_html: "%{name} удалил(а) эмодзи %{target}" destroy_domain_allow_html: "%{name} запретил(а) федерацию с доменом %{target}" destroy_domain_block_html: "%{name} снял(а) блокировку с домена %{target}" + destroy_email_domain_block_html: "%{name} разблокированный почтовый домен %{target}" destroy_instance_html: "%{name} очистил(а) данные для домена %{target}" destroy_ip_block_html: "%{name} удалил(а) правило для IP %{target}" destroy_status_html: "%{name} удалил(а) пост пользователя %{target}" @@ -250,8 +268,10 @@ ru: destroy_user_role_html: "%{name} удалил(а) роль %{target}" disable_2fa_user_html: "%{name} отключил(а) требование двухэтапной авторизации для пользователя %{target}" disable_custom_emoji_html: "%{name} отключил(а) эмодзи %{target}" + disable_sign_in_token_auth_user_html: "%{name} отключил аутентификацию по маркеру электронной почты для %{target}" disable_user_html: "%{name} заморозил(а) пользователя %{target}" enable_custom_emoji_html: "%{name} включил(а) эмодзи %{target}" + enable_sign_in_token_auth_user_html: "%{name} включил аутентификацию с помощью маркера электронной почты для %{target}" enable_user_html: "%{name} разморозил(а) пользователя %{target}" memorialize_account_html: "%{name} перевел(а) учётную запись пользователя %{target} в статус памятника" promote_user_html: "%{name} повысил(а) пользователя %{target}" @@ -259,6 +279,7 @@ ru: reject_user_html: "%{name} отклонил(а) регистрацию %{target}" remove_avatar_user_html: "%{name} убрал(а) аватарку пользователя %{target}" reopen_report_html: "%{name} повторно открыл(а) жалобу %{target}" + resend_user_html: "%{name} повторно отправил письмо с подтверждением для %{target}" reset_password_user_html: "%{name} сбросил(а) пароль пользователя %{target}" resolve_report_html: "%{name} решил(а) жалобу %{target}" sensitive_account_html: "%{name} установил(а) отметку файлов %{target} как «деликатного характера»" @@ -273,6 +294,7 @@ ru: update_custom_emoji_html: "%{name} обновил(а) эмодзи %{target}" update_domain_block_html: "%{name} обновил(а) блокировку домена для %{target}" update_ip_block_html: "%{name} изменил(а) правило для IP %{target}" + update_report_html: "%{name} обновленный отчет %{target}" update_status_html: "%{name} изменил(а) пост пользователя %{target}" update_user_role_html: "%{name} изменил(а) роль %{target}" deleted_account: удалённая учётная запись @@ -437,6 +459,8 @@ ru: new: create: Создать блокировку resolve: Проверить домен + title: Блокировка нового почтового домена + no_email_domain_block_selected: Блоки почтовых доменов не были изменены, так как ни один из них не был выбран not_permitted: Не разрешено resolved_through_html: Разрешено через %{domain} title: Заблокированные e-mail домены @@ -464,6 +488,9 @@ ru: title: Рекомендации подписок unsuppress: Восстановить рекомендацию instances: + audit_log: + title: Последние журналы аудита + view_all: Просмотр полных журналов аудита availability: description_html: few: Если доставка в домен завершается сбоем %{count} дня, дальнейшие попытки доставки предприниматься не будут, пока не будет получена доставка из домена. @@ -599,6 +626,7 @@ ru: silence_description_html: Учетная запись будет видна только тем пользователям, которые уже подписаны на неё, либо открыли его вручную. Это действие можно отменить в любой момент, и отменяет все жалобы против аккаунта. suspend_description_html: Аккаунт и все его содержимое будут недоступны и в конечном итоге удалены, и взаимодействие с ним будет невозможно. Это действие можно отменить в течение 30 дней. Отменяет все жалобы против этого аккаунта. actions_description_remote_html: Решите вопрос о том, какие меры необходимо принять для урегулирования этой жалобы. Это повлияет только на то, как ваш сервер взаимодействует с этим удаленным аккаунтом и обрабатывает его содержимое. + actions_no_posts: У этого отчета нет связанных с ним сообщений для удаления add_to_report: Прикрепить ещё already_suspended_badges: local: На этом сервере уже забанен @@ -639,6 +667,7 @@ ru: report: Жалоба №%{id} reported_account: Учётная запись нарушителя reported_by: Отправитель жалобы + reported_with_application: Сообщается с приложением resolved: Решённые resolved_msg: Жалоба обработана, спасибо! skip_to_actions: Перейти к действиям @@ -661,6 +690,7 @@ ru: delete_data_html: Удалить профиль и контент @%{acct} через 30 дней, если за это время они не будут разблокированы preview_preamble_html: "@%{acct} получит предупреждение со следующим содержанием:" record_strike_html: Запишите замечание против @%{acct}, чтобы помочь вам в решении будущих нарушений с этого аккаунта + send_email_html: Отправить @%{acct} предупреждающее письмо warning_placeholder: Необязательное дополнительное обоснование действия модерации. target_origin: Происхождение объекта жалобы title: Жалобы @@ -704,6 +734,7 @@ ru: manage_appeals: Управление апелляциями manage_appeals_description: Позволяет пользователям просматривать апелляции на действия модерации manage_blocks: Управление блоками + manage_blocks_description: Позволить пользователям блокировать провайдеров электронной почты и IP-адреса manage_custom_emojis: Управление смайлами manage_custom_emojis_description: Позволяет пользователям управлять пользовательскими эмодзи на сервере manage_federation: Управление Федерацией @@ -776,6 +807,7 @@ ru: disabled: Никому users: Залогиненным локальным пользователям registrations: + moderation_recommandation: Убедитесь, что у вас есть адекватная и оперативная команда модераторов, прежде чем открывать регистрацию для всех желающих! preamble: Контролируйте, кто может создать учетную запись на вашем сервере. title: Регистрации registrations_mode: @@ -783,6 +815,7 @@ ru: approved: Для регистрации требуется подтверждение none: Никто не может регистрироваться open: Все могут регистрироваться + warning_hint: Мы рекомендуем использовать "Требуется одобрение для регистрации", если вы не уверены, что ваша команда модераторов сможет своевременно справиться со спамом и вредоносными регистрациями. security: authorized_fetch: Требовать аутентификацию от федеративных серверов authorized_fetch_hint: Требование аутентификации от федеративных серверов позволяет более строго соблюдать блокировки как на уровне пользователя, так и на уровне сервера. Однако при этом снижается производительность, уменьшается охват ваших ответов и могут возникнуть проблемы совместимости с некоторыми федеративными сервисами. Кроме того, это не помешает специальным исполнителям получать ваши публичные сообщения и учётные записи. @@ -794,6 +827,7 @@ ru: destroyed_msg: Файл успешно удалён. software_updates: critical_update: Критично — пожалуйста, обновите как можно скорее + description: Рекомендуется поддерживать установку Mastodon в актуальном состоянии, чтобы воспользоваться последними исправлениями и функциями. Кроме того, иногда очень важно своевременно обновлять Mastodon, чтобы избежать проблем с безопасностью. По этим причинам Mastodon проверяет наличие обновлений каждые 30 минут и уведомляет вас об этом в соответствии с вашими предпочтениями в отношении уведомлений по электронной почте. documentation_link: Узнать больше release_notes: Примечания к выпуску title: Доступные обновления @@ -881,17 +915,38 @@ ru: message_html: "Ваше хранилище объектов неправильно настроено. Безопасность ваших пользователей находится под угрозой" tags: moderation: + not_trendable: Не в тренде + not_usable: Невозможно использовать + pending_review: В ожидании обзора + review_requested: Обзор запрошен + reviewed: Рассмотрено title: Статус + trendable: Трендовый + unreviewed: Без рецензии + usable: Полезное + name: Название + newest: Новейший + oldest: Старейший + open: Посмотреть публично + reset: Сброс review: Состояние проверки + search: Поиск + title: Хэштеги updated_msg: Настройки хэштега обновлены title: Администрирование trends: allow: Разрешить approved: Принятые + confirm_allow: Вы уверены, что хотите разрешить выбранные теги? + confirm_disallow: Вы уверены, что хотите запретить выбранные теги? disallow: Отклонить links: allow: Разрешить ссылку allow_provider: Разрешить издание + confirm_allow: Вы уверены, что хотите разрешить выбранные ссылки? + confirm_allow_provider: Вы уверены, что хотите разрешить выбранных провайдеров? + confirm_disallow: Вы уверены, что хотите запретить выбранные ссылки? + confirm_disallow_provider: Вы уверены, что хотите запретить выбранных поставщиков? description_html: Это ссылки, которыми в настоящее время много пользуются аккаунты, с которых ваш сервер видит сообщения. Это может помочь вашим пользователям узнать, что происходит в мире. Никакие ссылки не отображаются публично, пока вы не одобрите издателя. Вы также можете разрешить или отклонить индивидуальные ссылки. disallow: Запретить ссылку disallow_provider: Отклонить издание @@ -917,6 +972,10 @@ ru: statuses: allow: Разрешить пост allow_account: Разрешить автора + confirm_allow: Вы уверены, что хотите разрешить выбранные статусы? + confirm_allow_account: Вы уверены, что хотите разрешить выбранные учетные записи? + confirm_disallow: Вы уверены, что хотите запретить выбранные статусы? + confirm_disallow_account: Вы уверены, что хотите запретить выбранные учетные записи? description_html: Это посты, которыми на вашем сервере в данный момент часто делятся и предпочитают, что может помочь вашим новым и постоянным пользователям найти больше людей, чтобы на них подписаться. Посты не будут отображаться публично, пока вы не одобрите автора, а автор не разрешит предлагать его аккаунт другим. Вы также можете разрешить или отклонить отдельные сообщения. disallow: Запретить пост disallow_account: Запретить автора @@ -953,12 +1012,14 @@ ru: many: За последнюю неделю использовало %{count} человек one: За последнюю неделю использовал один человек other: За последнюю неделю использовал %{count} человек + title: Рекомендации и тенденции trending: Популярное warning_presets: add_new: Добавить delete: Удалить edit_preset: Удалить шаблон предупреждения empty: Вы еще не определили пресеты предупреждений. + title: Предупреждающие пред установки webhooks: add_new: Добавить конечную точку delete: Удалить @@ -982,6 +1043,9 @@ ru: title: Вебхуки webhook: Вебхук admin_mailer: + auto_close_registrations: + body: В связи с отсутствием активности модераторов в последнее время, регистрация на %{instance} была автоматически переведена в режим, требующий ручной проверки, чтобы предотвратить использование %{instance} в качестве платформы для потенциальных плохих игроков. Вы можете в любой момент переключить его обратно на открытые регистрации. + subject: Регистрации для %{instance} были автоматически переведены в разряд требующих одобрения new_appeal: actions: delete_statuses: удалить их посты @@ -1035,7 +1099,9 @@ ru: guide_link_text: Каждый может внести свой вклад. sensitive_content: Содержимое деликатного характера application_mailer: + notification_preferences: Изменение предпочтений электронной почты salutation: "%{name}," + settings: 'Измените настройки электронной почты: %{link}' unsubscribe: Отписаться view: 'Просмотр:' view_profile: Просмотреть профиль @@ -1055,6 +1121,7 @@ ru: hint_html: Еще одна вещь! Нам нужно подтвердить, что вы человек (так что мы можем держать спам!). Решите капчу ниже и нажмите кнопку «Продолжить». title: Проверка безопасности confirmations: + awaiting_review: Ваш адрес электронной почты подтвержден! Сотрудники %{domain} сейчас рассматривают вашу регистрацию. Вы получите письмо, если они одобрят вашу учетную запись! awaiting_review_title: Ваша регистрация проверяется clicking_this_link: нажатие на эту ссылку login_link: войти @@ -1062,6 +1129,7 @@ ru: redirect_to_app_html: Вы должны были перенаправлены на приложение %{app_name}. Если этого не произошло, попробуйте %{clicking_this_link} или вернитесь к приложению вручную. registration_complete: Ваша регистрация на %{domain} завершена! welcome_title: Добро пожаловать, %{name}! + wrong_email_hint: Если этот адрес электронной почты неверен, вы можете изменить его в настройках аккаунта. delete_account: Удалить учётную запись delete_account_html: Удалить свою учётную запись можно в два счёта здесь, но прежде у вас будет спрошено подтверждение. description: @@ -1082,6 +1150,7 @@ ru: or_log_in_with: Или войти с помощью privacy_policy_agreement_html: Мной прочитана и принята политика конфиденциальности progress: + confirm: Подтвердите электронную почту details: Ваши данные review: Наш обзор rules: Принять правила @@ -1103,8 +1172,10 @@ ru: security: Безопасность set_new_password: Задать новый пароль setup: + email_below_hint_html: Проверьте папку "Спам" или запросите другую. Вы можете исправить свой адрес электронной почты, если он неправильный. email_settings_hint_html: Нажмите на ссылку, которую мы отправили вам для проверки %{email}. Мы будем ждать прямо здесь. link_not_received: Не получили ссылку? + new_confirmation_instructions_sent: Через несколько минут вы получите новое письмо со ссылкой для подтверждения! title: Проверьте свой почтовый ящик sign_in: preamble_html: Войдите, используя ваши учётные данные %{domain}. Если ваша учётная запись размещена на другом сервере, вы не сможете здесь войти. @@ -1115,12 +1186,20 @@ ru: title: Зарегистрируйтесь в %{domain}. status: account_status: Статус учётной записи + confirming: Жду подтверждения по электронной почте. functional: Ваша учётная запись в полном порядке. + pending: Ваша заявка находится на рассмотрении у наших сотрудников. Это может занять некоторое время. Вы получите электронное письмо, если ваша заявка будет одобрена. redirecting_to: Ваша учётная запись деактивированна, потому что вы настроили перенаправление на %{acct}. self_destruct: Поскольку %{domain} закрывается, вы получите ограниченный доступ к вашей учетной записи. view_strikes: Просмотр предыдущих замечаний в адрес вашей учетной записи too_fast: Форма отправлена слишком быстро, попробуйте еще раз. use_security_key: Использовать ключ безопасности + author_attribution: + example_title: Образец текста + hint_html: Контролируйте, как вы будете отмечены при обмене ссылками на Mastodon. + more_from_html: Больше от %{name} + s_blog: "%{name}'S Блог" + title: Авторская атрибуция challenge: confirm: Продолжить hint_html: "Подсказка: мы не будем спрашивать пароль повторно в течение часа." @@ -1157,6 +1236,9 @@ ru: before: 'Внимательно прочитайте следующую информацию перед началом:' caches: Некоторые данные, обработанные другими узлами, однако, могут храниться ещё какое-то время data_removal: Все ваши золотые посты, шикарный профиль и прочие данные будут безвозвратно уничтожены + email_change_html: Вы можете изменить свой адрес электронной почты, не удаляя свою учетную запись + email_contact_html: Если оно все еще не пришло, вы можете обратиться за помощью по электронной почте %{email} + email_reconfirmation_html: Если вы не получили подтверждение по электронной почте, вы можете запросить его снова irreversible: После удаления восстановить или повторно активировать учётную запись не получится more_details_html: За всеми подробностями, изучите политику конфиденциальности. username_available: Ваше имя пользователя снова станет доступным @@ -1403,6 +1485,7 @@ ru: authentication_methods: otp: приложение двухфакторной аутентификации password: пароль + sign_in_token: код безопасности электронной почты webauthn: ключи безопасности description_html: Если вы видите неопознанное действие, смените пароль и/или включите двухфакторную авторизацию. empty: Нет доступной истории входов @@ -1413,10 +1496,20 @@ ru: unsubscribe: action: Да, отписаться complete: Подписка отменена + emails: + notification_emails: + favourite: любимые электронные письма с уведомлениями + follow: Следить за электронными сообщениями + follow_request: Письма с просьбой о помощи + mention: Упоминание электронных писем с уведомлениями + reblog: Уведомления по электронной почте + resubscribe_html: Если вы отписались от рассылки по ошибке, вы можете повторно подписаться на рассылку в настройках настроек почтовых уведомлений. + success_html: Вы больше не будете получать %{type} для Mastodon на %{domain} на вашу электронную почту %{email}. title: Отписаться media_attachments: validations: images_and_video: Нельзя добавить видео к посту с изображениями + not_found: Медиа %{ids} не найдено или уже прикреплено к другому сообщению not_ready: Не удаётся прикрепить файлы, обработка которых не завершена. Повторите попытку чуть позже! too_many: Нельзя добавить более 4 файлов migrations: @@ -1493,6 +1586,8 @@ ru: update: subject: "%{name} изменил(а) пост" notifications: + administration_emails: Уведомления администратора по электронной почте + email_events: События для уведомлений по электронной почте email_events_hint: 'Выберите события, для которых вы хотели бы получать уведомления:' number: human: @@ -1651,16 +1746,26 @@ ru: import: Импорт import_and_export: Импорт и экспорт migrate: Миграция учётной записи + notifications: E-mail уведомление preferences: Настройки profile: Профиль relationships: Подписки и подписчики + severed_relationships: Разорванные отношения statuses_cleanup: Авто-удаление постов strikes: Замечания модерации two_factor_authentication: Подтверждение входа webauthn_authentication: Ключи безопасности severed_relationships: + download: Скачать (%{count}) event_type: + account_suspension: Приостановка аккаунта (%{target_name}) + domain_block: Приостановка сервера (%{target_name}) user_domain_block: Вы заблокировали %{target_name} + lost_followers: Потерянные подписчики + lost_follows: Потерянный следует + preamble: Вы можете потерять подписчиков и последователей, если заблокируете домен или, если ваши модераторы решат приостановить работу удаленного сервера. Когда это произойдет, вы сможете загрузить списки разорванных отношений, чтобы проверить их и, возможно, импортировать на другой сервер. + purged: Информация об этом сервере была удалена администраторами вашего сервера. + type: Событие statuses: attached: audio: @@ -1751,6 +1856,7 @@ ru: contrast: Mastodon (высококонтрастная) default: Mastodon (тёмная) mastodon-light: Mastodon (светлая) + system: Автоматически (используйте системную тему) time: formats: default: "%d %b %Y, %H:%M" @@ -1839,8 +1945,45 @@ ru: suspend: Учётная запись заблокирована welcome: apps_android_action: Скачать на Google Play + apps_ios_action: Скачать в App Store + apps_step: Загрузите наши официальные приложения. + apps_title: Приложения Mastodon + checklist_subtitle: 'Давайте начнем знакомство с этим новым социальным рубежом:' + checklist_title: Приветственный контрольный список + edit_profile_action: Персонализация + edit_profile_step: Усильте взаимодействие, заполнив полный профиль. + edit_profile_title: Персонализируйте свой профиль explanation: Вот несколько советов для новичков feature_action: Подробнее + feature_audience: Mastodon предоставляет вам уникальную возможность управлять своей аудиторией без посредников. Mastodon, развернутый на вашей собственной инфраструктуре, позволяет вам следить и быть преследуемым с любого другого сервера Mastodon в Интернете и не контролируется никем, кроме вас. + feature_audience_title: Создайте уверенную аудиторию + feature_control: Вы сами знаете, что хотите видеть в своей ленте. Никаких алгоритмов или рекламы, чтобы тратить ваше время. Следите за любым человеком на любом сервере Mastodon с одного аккаунта и получайте его сообщения в хронологическом порядке, а также сделайте свой уголок интернета немного больше похожим на себя. + feature_control_title: Контролируйте свой график + feature_creativity: Mastodon поддерживает аудио-, видео- и фотопосты, описания доступности, опросы, предупреждения о содержании, анимированные аватары, пользовательские emojis, управление обрезкой миниатюр и многое другое, чтобы помочь вам выразить себя в Интернете. Публикуете ли вы свои работы, музыку или подкаст, Mastodon всегда готов помочь вам. + feature_creativity_title: Непревзойденная креативность + feature_moderation: Mastodon возвращает принятие решений в ваши руки. Каждый сервер создает свои собственные правила и нормы, которые соблюдаются локально, а не сверху вниз, как в корпоративных социальных сетях, что позволяет наиболее гибко реагировать на потребности различных групп людей. Присоединяйтесь к серверу с правилами, с которыми вы согласны, или создайте свой собственный. + feature_moderation_title: Модерирование, каким оно должно быть + follow_action: Следуйте за + follow_step: Следить за интересными людьми - вот что такое Mastodon. + follow_title: Персонализируйте свою домашнюю ленту + follows_subtitle: Следите за известными аккаунтами + follows_title: За кем следить + follows_view_more: Посмотреть больше людей, за которыми стоит следить + hashtags_recent_count: + few: "%{people} человека за последние 2 дня" + many: "%{people} человек за последние 2 дня" + one: "%{people} человек за последние 2 дня" + other: "%{people} человек за последние 2 дня" + hashtags_subtitle: Изучите, что было в тренде за последние 2 дня + hashtags_title: Модные хэштеги + hashtags_view_more: Посмотреть другие трендовые хэштеги + post_action: Составить + post_step: Поприветствуйте мир с помощью текста, фотографий, видео или опросов. + post_title: Сделайте свой первый пост + share_action: Поделиться + share_step: Пусть ваши друзья знают, как найти вас на Mastodon. + share_title: Поделитесь информацией о компании Mastodon + sign_in_action: Зарегистрироваться subject: Добро пожаловать в Mastodon title: Добро пожаловать на борт, %{name}! users: @@ -1849,6 +1992,7 @@ ru: invalid_otp_token: Введен неверный код двухфакторной аутентификации otp_lost_help_html: Если Вы потеряли доступ к обоим, свяжитесь с %{email} rate_limited: Слишком много попыток аутентификации, повторите попытку позже. + seamless_external_login: Вы вошли в систему через внешнюю службу, поэтому настройки пароля и электронной почты недоступны. signed_in_as: 'Выполнен вход под именем:' verification: extra_instructions_html: Подсказка: Ссылка на вашем сайте может быть невидимой. Важной частью является rel="me", который предотвращает выдачу себя за другое лицо на сайтах с пользовательским контентом. Вы даже можете использовать тег link в заголовке страницы вместо a, но HTML должен быть доступен без выполнения JavaScript. @@ -1857,6 +2001,7 @@ ru: instructions_html: Скопируйте и вставьте код ниже в HTML вашего сайта. Затем, добавьте адрес вашего веб сайта в одно из дополнительных полей на вкладке "Редактировать профиль" и сохраните изменения. verification: Верификация ссылок verified_links: Ваши ссылки подтверждения + website_verification: Проверка веб-сайта webauthn_credentials: add: Добавить новый ключ безопасности create: diff --git a/config/locales/simple_form.es-MX.yml b/config/locales/simple_form.es-MX.yml index 3271b75668..f2108828d5 100644 --- a/config/locales/simple_form.es-MX.yml +++ b/config/locales/simple_form.es-MX.yml @@ -84,7 +84,7 @@ es-MX: closed_registrations_message: Mostrado cuando los registros están cerrados content_cache_retention_period: Todas las publicaciones de otros servidores (incluso impulsos y respuestas) se eliminarán después del número de días especificado, sin tener en cuenta la interacción del usuario local con esos mensajes. Esto incluye mensajes donde un usuario local los ha marcado como marcadores o favoritos. Las menciones privadas entre usuarios de diferentes instancias también se perderán sin posibilidad de recuperación. El uso de esta configuración está destinado a instancias de propósito especial, y rompe muchas expectativas de los usuarios cuando se implementa para un uso de propósito general. custom_css: Puedes aplicar estilos personalizados a la versión web de Mastodon. - favicon: WEBP, PNG, GIF o JPG. Reemplaza el favicon predeterminado de Mastodon con un icono personalizado. + favicon: WEBP, PNG, GIF o JPG. Reemplaza el icono predeterminado de Mastodon con un icono personalizado. mascot: Reemplaza la ilustración en la interfaz web avanzada. media_cache_retention_period: Los archivos multimedia de las publicaciones creadas por usuarios remotos se almacenan en caché en tu servidor. Cuando se establece un valor positivo, estos archivos se eliminarán después del número especificado de días. Si los datos multimedia se solicitan después de eliminarse, se volverán a descargar, si el contenido fuente todavía está disponible. Debido a restricciones en la frecuencia con la que las tarjetas de previsualización de enlaces realizan peticiones a sitios de terceros, se recomienda establecer este valor a al menos 14 días, o las tarjetas de previsualización de enlaces no se actualizarán bajo demanda antes de ese momento. peers_api_enabled: Una lista de nombres de dominio que este servidor ha encontrado en el fediverso. Aquí no se incluye ningún dato sobre si usted federa con un servidor determinado, sólo que su servidor lo sabe. Esto es utilizado por los servicios que recopilan estadísticas sobre la federación en un sentido general. diff --git a/config/locales/simple_form.ru.yml b/config/locales/simple_form.ru.yml index b41457e86a..3ff746451b 100644 --- a/config/locales/simple_form.ru.yml +++ b/config/locales/simple_form.ru.yml @@ -3,6 +3,7 @@ ru: simple_form: hints: account: + attribution_domains_as_text: Защищает от ложных атрибуций. discoverable: Ваши публичные сообщения и профиль могут быть показаны или рекомендованы в различных разделах Mastodon, и ваш профиль может быть предложен другим пользователям. display_name: Ваше полное имя или псевдоним. fields: Ваша домашняя страница, местоимения, возраст - все, что угодно. @@ -77,11 +78,15 @@ ru: warn: Скрыть отфильтрованный контент за предупреждением с указанием названия фильтра form_admin_settings: activity_api_enabled: Подсчёт количества локальных постов, активных пользователей и новых регистраций на еженедельной основе + app_icon: WEBP, PNG, GIF или JPG. Замените значок приложения по умолчанию на мобильных устройствах пользовательским значком. backups_retention_period: Пользователи могут создавать архивы своих постов, чтобы потом их забрать. Если задать положительное значение, эти архивы автоматически удалятся с вашего хранилища через указанное число дней. bootstrap_timeline_accounts: Эти аккаунты будут рекомендованы для подписки новым пользователям. closed_registrations_message: Отображается, когда регистрация закрыта + content_cache_retention_period: Все сообщения с других серверов (включая бусты и ответы) будут удалены через указанное количество дней, независимо от того, как локальный пользователь взаимодействовал с этими сообщениями. Это касается и тех сообщений, которые локальный пользователь пометил в закладки или избранное. Приватные упоминания между пользователями из разных инстансов также будут потеряны и не смогут быть восстановлены. Использование этой настройки предназначено для экземпляров специального назначения и нарушает многие ожидания пользователей при использовании в общих целях. custom_css: Вы можете применять пользовательские стили в веб-версии Mastodon. + favicon: WEBP, PNG, GIF или JPG. Заменяет стандартный фавикон Mastodon на собственный значок. mascot: Заменяет иллюстрацию в расширенном веб-интерфейсе. + media_cache_retention_period: Медиафайлы из сообщений, сделанных удаленными пользователями, кэшируются на вашем сервере. При положительном значении медиафайлы будут удалены через указанное количество дней. Если медиаданные будут запрошены после удаления, они будут загружены повторно, если исходный контент все еще доступен. В связи с ограничениями на частоту опроса карточек предварительного просмотра ссылок на сторонних сайтах рекомендуется устанавливать значение не менее 14 дней, иначе карточки предварительного просмотра ссылок не будут обновляться по запросу до этого времени. peers_api_enabled: Список доменных имен, с которыми сервер столкнулся в fediverse. Здесь нет данных о том, федерировались ли вы с данным сервером, только что ваш сервер знает об этом. Это используется службами, которые собирают статистику по федерации в общем смысле. profile_directory: В каталоге профилей перечислены все пользователи, которые согласились быть доступными для обнаружения. require_invite_text: Когда регистрация требует ручного одобрения, сделайте текстовый ввод "Почему вы хотите присоединиться?" обязательным, а не опциональным @@ -114,6 +119,7 @@ ru: sign_up_requires_approval: Новые регистрации потребуют вашего одобрения severity: Выберите, что будет происходить с запросами с этого IP rule: + hint: Необязательно. Предоставьте дополнительные сведения о правиле text: Опишите правило или требование для пользователей на этом сервере. Постарайтесь сделать его коротким и простым sessions: otp: 'Введите код двухфакторной аутентификации, сгенерированный в мобильном приложении, или используйте один из ваших кодов восстановления:' @@ -125,6 +131,7 @@ ru: name: Вы можете изменить только регистр букв чтобы, например, сделать тег более читаемым user: chosen_languages: Если выбрано, то в публичных лентах будут показаны только посты на выбранных языках. + role: Роль определяет, какими правами обладает пользователь. user_role: color: Цвет, который будет использоваться для роли в интерфейсе (UI), как RGB в формате HEX highlighted: Это действие сделает роль публичной @@ -137,6 +144,7 @@ ru: url: Куда события будут отправляться labels: account: + attribution_domains_as_text: Разрешить только определенные сайты discoverable: Профиль и сообщения в алгоритмах обнаружения fields: name: Пункт @@ -241,6 +249,7 @@ ru: backups_retention_period: Период хранения архива пользователя bootstrap_timeline_accounts: Всегда рекомендовать эти учетные записи новым пользователям closed_registrations_message: Сообщение, когда регистрация недоступна + content_cache_retention_period: Период хранения удаленного содержимого custom_css: Пользовательский CSS favicon: Favicon mascot: Пользовательский маскот (устаревшее) From c922af27375ec9e902981c6b66c96a7ae57c7513 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 20 Sep 2024 04:31:58 -0400 Subject: [PATCH 34/93] Add `LIMIT` constant for `api/v1/peers/search` endpoint (#31989) --- app/controllers/api/v1/peers/search_controller.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/peers/search_controller.rb b/app/controllers/api/v1/peers/search_controller.rb index 1780554c5d..d9c8232702 100644 --- a/app/controllers/api/v1/peers/search_controller.rb +++ b/app/controllers/api/v1/peers/search_controller.rb @@ -7,6 +7,8 @@ class Api::V1::Peers::SearchController < Api::BaseController skip_before_action :require_authenticated_user!, unless: :limited_federation_mode? skip_around_action :set_locale + LIMIT = 10 + vary_by '' def index @@ -35,10 +37,10 @@ class Api::V1::Peers::SearchController < Api::BaseController field: 'accounts_count', modifier: 'log2p', }, - }).limit(10).pluck(:domain) + }).limit(LIMIT).pluck(:domain) else domain = normalized_domain - @domains = Instance.searchable.domain_starts_with(domain).limit(10).pluck(:domain) + @domains = Instance.searchable.domain_starts_with(domain).limit(LIMIT).pluck(:domain) end rescue Addressable::URI::InvalidURIError @domains = [] From 04a939d6407d4ae5873757b8ab8d7187110fe6e7 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 20 Sep 2024 04:51:37 -0400 Subject: [PATCH 35/93] Add `reviewed` and `unreviewed` scopes to `Reviewable` model concern (#31988) --- app/models/concerns/reviewable.rb | 5 ++++ app/models/preview_card_provider.rb | 2 -- app/models/tag.rb | 4 +-- .../trends/preview_card_provider_filter.rb | 2 +- .../preview_card_providers/index.html.haml | 2 +- spec/models/preview_card_provider_spec.rb | 16 ------------ .../examples/models/concerns/reviewable.rb | 25 +++++++++++++++++++ 7 files changed, 33 insertions(+), 23 deletions(-) diff --git a/app/models/concerns/reviewable.rb b/app/models/concerns/reviewable.rb index 1f70474b35..d08b473342 100644 --- a/app/models/concerns/reviewable.rb +++ b/app/models/concerns/reviewable.rb @@ -3,6 +3,11 @@ module Reviewable extend ActiveSupport::Concern + included do + scope :reviewed, -> { where.not(reviewed_at: nil) } + scope :unreviewed, -> { where(reviewed_at: nil) } + end + def requires_review? reviewed_at.nil? end diff --git a/app/models/preview_card_provider.rb b/app/models/preview_card_provider.rb index 48944fe638..889176036c 100644 --- a/app/models/preview_card_provider.rb +++ b/app/models/preview_card_provider.rb @@ -34,8 +34,6 @@ class PreviewCardProvider < ApplicationRecord scope :trendable, -> { where(trendable: true) } scope :not_trendable, -> { where(trendable: false) } - scope :reviewed, -> { where.not(reviewed_at: nil) } - scope :pending_review, -> { where(reviewed_at: nil) } def self.matching_domain(domain) segments = domain.split('.') diff --git a/app/models/tag.rb b/app/models/tag.rb index acf514919b..93210eb307 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -50,8 +50,6 @@ class Tag < ApplicationRecord validate :validate_name_change, if: -> { !new_record? && name_changed? } validate :validate_display_name_change, if: -> { !new_record? && display_name_changed? } - scope :reviewed, -> { where.not(reviewed_at: nil) } - scope :unreviewed, -> { where(reviewed_at: nil) } scope :pending_review, -> { unreviewed.where.not(requested_review_at: nil) } scope :usable, -> { where(usable: [true, nil]) } scope :not_usable, -> { where(usable: false) } @@ -127,7 +125,7 @@ class Tag < ApplicationRecord query = Tag.matches_name(stripped_term) query = query.merge(Tag.listable) if options[:exclude_unlistable] - query = query.merge(matching_name(stripped_term).or(where.not(reviewed_at: nil))) if options[:exclude_unreviewed] + query = query.merge(matching_name(stripped_term).or(reviewed)) if options[:exclude_unreviewed] query.order(Arel.sql('length(name) ASC, name ASC')) .limit(limit) diff --git a/app/models/trends/preview_card_provider_filter.rb b/app/models/trends/preview_card_provider_filter.rb index 219793f01e..33f4e97912 100644 --- a/app/models/trends/preview_card_provider_filter.rb +++ b/app/models/trends/preview_card_provider_filter.rb @@ -41,7 +41,7 @@ class Trends::PreviewCardProviderFilter when 'rejected' PreviewCardProvider.not_trendable when 'pending_review' - PreviewCardProvider.pending_review + PreviewCardProvider.unreviewed else raise Mastodon::InvalidParameterError, "Unknown status: #{value}" end diff --git a/app/views/admin/trends/links/preview_card_providers/index.html.haml b/app/views/admin/trends/links/preview_card_providers/index.html.haml index b43b8dfff9..93daf25f31 100644 --- a/app/views/admin/trends/links/preview_card_providers/index.html.haml +++ b/app/views/admin/trends/links/preview_card_providers/index.html.haml @@ -12,7 +12,7 @@ %li= filter_link_to t('generic.all'), status: nil %li= filter_link_to t('admin.trends.approved'), status: 'approved' %li= filter_link_to t('admin.trends.rejected'), status: 'rejected' - %li= filter_link_to safe_join([t('admin.accounts.moderation.pending'), "(#{PreviewCardProvider.pending_review.count})"], ' '), status: 'pending_review' + %li= filter_link_to safe_join([t('admin.accounts.moderation.pending'), "(#{PreviewCardProvider.unreviewed.count})"], ' '), status: 'pending_review' .back-link = link_to admin_trends_links_path do = material_symbol 'chevron_left' diff --git a/spec/models/preview_card_provider_spec.rb b/spec/models/preview_card_provider_spec.rb index 12bca83440..a3bd4f49ad 100644 --- a/spec/models/preview_card_provider_spec.rb +++ b/spec/models/preview_card_provider_spec.rb @@ -24,21 +24,5 @@ RSpec.describe PreviewCardProvider do expect(results).to eq([not_trendable_and_not_reviewed]) end end - - describe 'reviewed' do - it 'returns the relevant records' do - results = described_class.reviewed - - expect(results).to eq([trendable_and_reviewed]) - end - end - - describe 'pending_review' do - it 'returns the relevant records' do - results = described_class.pending_review - - expect(results).to eq([not_trendable_and_not_reviewed]) - end - end end end diff --git a/spec/support/examples/models/concerns/reviewable.rb b/spec/support/examples/models/concerns/reviewable.rb index b63e44b43f..144019d969 100644 --- a/spec/support/examples/models/concerns/reviewable.rb +++ b/spec/support/examples/models/concerns/reviewable.rb @@ -6,6 +6,31 @@ RSpec.shared_examples 'Reviewable' do let(:reviewed_at) { nil } let(:requested_review_at) { nil } + describe 'Scopes' do + let!(:reviewed_record) { Fabricate factory_name, reviewed_at: 10.days.ago } + let!(:un_reviewed_record) { Fabricate factory_name, reviewed_at: nil } + + describe '.reviewed' do + it 'returns reviewed records' do + expect(described_class.reviewed) + .to include(reviewed_record) + .and not_include(un_reviewed_record) + end + end + + describe '.unreviewed' do + it 'returns non reviewed records' do + expect(described_class.unreviewed) + .to include(un_reviewed_record) + .and not_include(reviewed_record) + end + end + + def factory_name + described_class.name.underscore.to_sym + end + end + describe '#requires_review?' do it { is_expected.to be_requires_review } From e7fd0985c9cd14437443345adfe725ecd3b038a6 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 20 Sep 2024 11:42:02 +0200 Subject: [PATCH 36/93] Change zoom icon in web UI (#29683) --- .../mastodon/components/modal_root.jsx | 2 +- .../features/ui/components/image_loader.jsx | 7 +- .../features/ui/components/media_modal.jsx | 52 +++++-- .../features/ui/components/zoomable_image.jsx | 129 +++++------------- app/javascript/mastodon/locales/en.json | 4 +- .../400-24px/fit_screen-fill.svg | 1 + .../material-icons/400-24px/fit_screen.svg | 1 + .../styles/mastodon/components.scss | 59 ++++---- app/javascript/svg-icons/actual_size.svg | 4 + 9 files changed, 114 insertions(+), 145 deletions(-) create mode 100644 app/javascript/material-icons/400-24px/fit_screen-fill.svg create mode 100644 app/javascript/material-icons/400-24px/fit_screen.svg create mode 100644 app/javascript/svg-icons/actual_size.svg diff --git a/app/javascript/mastodon/components/modal_root.jsx b/app/javascript/mastodon/components/modal_root.jsx index fd13564af2..e7fa5e6f9a 100644 --- a/app/javascript/mastodon/components/modal_root.jsx +++ b/app/javascript/mastodon/components/modal_root.jsx @@ -148,7 +148,7 @@ class ModalRoot extends PureComponent { return (
-
+
{children}
diff --git a/app/javascript/mastodon/features/ui/components/image_loader.jsx b/app/javascript/mastodon/features/ui/components/image_loader.jsx index 9dabc621b4..b1417deda7 100644 --- a/app/javascript/mastodon/features/ui/components/image_loader.jsx +++ b/app/javascript/mastodon/features/ui/components/image_loader.jsx @@ -17,7 +17,7 @@ export default class ImageLoader extends PureComponent { width: PropTypes.number, height: PropTypes.number, onClick: PropTypes.func, - zoomButtonHidden: PropTypes.bool, + zoomedIn: PropTypes.bool, }; static defaultProps = { @@ -134,7 +134,7 @@ export default class ImageLoader extends PureComponent { }; render () { - const { alt, lang, src, width, height, onClick } = this.props; + const { alt, lang, src, width, height, onClick, zoomedIn } = this.props; const { loading } = this.state; const className = classNames('image-loader', { @@ -149,6 +149,7 @@ export default class ImageLoader extends PureComponent {
+ )}
diff --git a/app/javascript/mastodon/features/ui/components/media_modal.jsx b/app/javascript/mastodon/features/ui/components/media_modal.jsx index 0f6e8a727b..d69ceba539 100644 --- a/app/javascript/mastodon/features/ui/components/media_modal.jsx +++ b/app/javascript/mastodon/features/ui/components/media_modal.jsx @@ -12,6 +12,8 @@ import ReactSwipeableViews from 'react-swipeable-views'; import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react'; import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react'; import CloseIcon from '@/material-icons/400-24px/close.svg?react'; +import FitScreenIcon from '@/material-icons/400-24px/fit_screen.svg?react'; +import ActualSizeIcon from '@/svg-icons/actual_size.svg?react'; import { getAverageFromBlurhash } from 'mastodon/blurhash'; import { GIFV } from 'mastodon/components/gifv'; import { Icon } from 'mastodon/components/icon'; @@ -26,6 +28,8 @@ const messages = defineMessages({ close: { id: 'lightbox.close', defaultMessage: 'Close' }, previous: { id: 'lightbox.previous', defaultMessage: 'Previous' }, next: { id: 'lightbox.next', defaultMessage: 'Next' }, + zoomIn: { id: 'lightbox.zoom_in', defaultMessage: 'Zoom to actual size' }, + zoomOut: { id: 'lightbox.zoom_out', defaultMessage: 'Zoom to fit' }, }); class MediaModal extends ImmutablePureComponent { @@ -46,30 +50,39 @@ class MediaModal extends ImmutablePureComponent { state = { index: null, navigationHidden: false, - zoomButtonHidden: false, + zoomedIn: false, + }; + + handleZoomClick = () => { + this.setState(prevState => ({ + zoomedIn: !prevState.zoomedIn, + })); }; handleSwipe = (index) => { - this.setState({ index: index % this.props.media.size }); + this.setState({ + index: index % this.props.media.size, + zoomedIn: false, + }); }; handleTransitionEnd = () => { this.setState({ - zoomButtonHidden: false, + zoomedIn: false, }); }; handleNextClick = () => { this.setState({ index: (this.getIndex() + 1) % this.props.media.size, - zoomButtonHidden: true, + zoomedIn: false, }); }; handlePrevClick = () => { this.setState({ index: (this.props.media.size + this.getIndex() - 1) % this.props.media.size, - zoomButtonHidden: true, + zoomedIn: false, }); }; @@ -78,7 +91,7 @@ class MediaModal extends ImmutablePureComponent { this.setState({ index: index % this.props.media.size, - zoomButtonHidden: true, + zoomedIn: false, }); }; @@ -130,15 +143,22 @@ class MediaModal extends ImmutablePureComponent { return this.state.index !== null ? this.state.index : this.props.index; } - toggleNavigation = () => { + handleToggleNavigation = () => { this.setState(prevState => ({ navigationHidden: !prevState.navigationHidden, })); }; + setRef = c => { + this.setState({ + viewportWidth: c?.clientWidth, + viewportHeight: c?.clientHeight, + }); + }; + render () { const { media, statusId, lang, intl, onClose } = this.props; - const { navigationHidden } = this.state; + const { navigationHidden, zoomedIn, viewportWidth, viewportHeight } = this.state; const index = this.getIndex(); @@ -160,8 +180,8 @@ class MediaModal extends ImmutablePureComponent { alt={description} lang={lang} key={image.get('url')} - onClick={this.toggleNavigation} - zoomButtonHidden={this.state.zoomButtonHidden} + onClick={this.handleToggleNavigation} + zoomedIn={zoomedIn} /> ); } else if (image.get('type') === 'video') { @@ -230,9 +250,12 @@ class MediaModal extends ImmutablePureComponent { )); } + const currentMedia = media.get(index); + const zoomable = currentMedia.get('type') === 'image' && (currentMedia.getIn(['meta', 'original', 'width']) > viewportWidth || currentMedia.getIn(['meta', 'original', 'height']) > viewportHeight); + return ( -
-
+
+
- +
+ {zoomable && } + +
{leftNav} {rightNav} diff --git a/app/javascript/mastodon/features/ui/components/zoomable_image.jsx b/app/javascript/mastodon/features/ui/components/zoomable_image.jsx index 272a3cff00..c4129bf260 100644 --- a/app/javascript/mastodon/features/ui/components/zoomable_image.jsx +++ b/app/javascript/mastodon/features/ui/components/zoomable_image.jsx @@ -1,17 +1,6 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; -import { defineMessages, injectIntl } from 'react-intl'; - -import FullscreenExitIcon from '@/material-icons/400-24px/fullscreen_exit.svg?react'; -import RectangleIcon from '@/material-icons/400-24px/rectangle.svg?react'; -import { IconButton } from 'mastodon/components/icon_button'; - -const messages = defineMessages({ - compress: { id: 'lightbox.compress', defaultMessage: 'Compress image view box' }, - expand: { id: 'lightbox.expand', defaultMessage: 'Expand image view box' }, -}); - const MIN_SCALE = 1; const MAX_SCALE = 4; const NAV_BAR_HEIGHT = 66; @@ -104,8 +93,7 @@ class ZoomableImage extends PureComponent { width: PropTypes.number, height: PropTypes.number, onClick: PropTypes.func, - zoomButtonHidden: PropTypes.bool, - intl: PropTypes.object.isRequired, + zoomedIn: PropTypes.bool, }; static defaultProps = { @@ -131,8 +119,6 @@ class ZoomableImage extends PureComponent { translateX: null, translateY: null, }, - zoomState: 'expand', // 'expand' 'compress' - navigationHidden: false, dragPosition: { top: 0, left: 0, x: 0, y: 0 }, dragged: false, lockScroll: { x: 0, y: 0 }, @@ -169,35 +155,20 @@ class ZoomableImage extends PureComponent { this.container.addEventListener('DOMMouseScroll', handler); this.removers.push(() => this.container.removeEventListener('DOMMouseScroll', handler)); - this.initZoomMatrix(); + this._initZoomMatrix(); } componentWillUnmount () { - this.removeEventListeners(); + this._removeEventListeners(); } - componentDidUpdate () { - this.setState({ zoomState: this.state.scale >= this.state.zoomMatrix.rate ? 'compress' : 'expand' }); - - if (this.state.scale === MIN_SCALE) { - this.container.style.removeProperty('cursor'); + componentDidUpdate (prevProps) { + if (prevProps.zoomedIn !== this.props.zoomedIn) { + this._toggleZoom(); } } - UNSAFE_componentWillReceiveProps () { - // reset when slide to next image - if (this.props.zoomButtonHidden) { - this.setState({ - scale: MIN_SCALE, - lockTranslate: { x: 0, y: 0 }, - }, () => { - this.container.scrollLeft = 0; - this.container.scrollTop = 0; - }); - } - } - - removeEventListeners () { + _removeEventListeners () { this.removers.forEach(listeners => listeners()); this.removers = []; } @@ -220,9 +191,6 @@ class ZoomableImage extends PureComponent { }; mouseDownHandler = e => { - this.container.style.cursor = 'grabbing'; - this.container.style.userSelect = 'none'; - this.setState({ dragPosition: { left: this.container.scrollLeft, top: this.container.scrollTop, @@ -246,9 +214,6 @@ class ZoomableImage extends PureComponent { }; mouseUpHandler = () => { - this.container.style.cursor = 'grab'; - this.container.style.removeProperty('user-select'); - this.image.removeEventListener('mousemove', this.mouseMoveHandler); this.image.removeEventListener('mouseup', this.mouseUpHandler); }; @@ -276,13 +241,13 @@ class ZoomableImage extends PureComponent { const _MAX_SCALE = Math.max(MAX_SCALE, this.state.zoomMatrix.rate); const scale = clamp(MIN_SCALE, _MAX_SCALE, this.state.scale * distance / this.lastDistance); - this.zoom(scale, midpoint); + this._zoom(scale, midpoint); this.lastMidpoint = midpoint; this.lastDistance = distance; }; - zoom(nextScale, midpoint) { + _zoom(nextScale, midpoint) { const { scale, zoomMatrix } = this.state; const { scrollLeft, scrollTop } = this.container; @@ -318,14 +283,13 @@ class ZoomableImage extends PureComponent { if (dragged) return; const handler = this.props.onClick; if (handler) handler(); - this.setState({ navigationHidden: !this.state.navigationHidden }); }; handleMouseDown = e => { e.preventDefault(); }; - initZoomMatrix = () => { + _initZoomMatrix = () => { const { width, height } = this.props; const { clientWidth, clientHeight } = this.container; const { offsetWidth, offsetHeight } = this.image; @@ -357,10 +321,7 @@ class ZoomableImage extends PureComponent { }); }; - handleZoomClick = e => { - e.preventDefault(); - e.stopPropagation(); - + _toggleZoom () { const { scale, zoomMatrix } = this.state; if ( scale >= zoomMatrix.rate ) { @@ -394,10 +355,7 @@ class ZoomableImage extends PureComponent { this.container.scrollTop = zoomMatrix.scrollTop; }); } - - this.container.style.cursor = 'grab'; - this.container.style.removeProperty('user-select'); - }; + } setContainerRef = c => { this.container = c; @@ -408,52 +366,37 @@ class ZoomableImage extends PureComponent { }; render () { - const { alt, lang, src, width, height, intl } = this.props; - const { scale, lockTranslate } = this.state; + const { alt, lang, src, width, height } = this.props; + const { scale, lockTranslate, dragged } = this.state; const overflow = scale === MIN_SCALE ? 'hidden' : 'scroll'; - const zoomButtonShouldHide = this.state.navigationHidden || this.props.zoomButtonHidden || this.state.zoomMatrix.rate <= MIN_SCALE ? 'media-modal__zoom-button--hidden' : ''; - const zoomButtonTitle = this.state.zoomState === 'compress' ? intl.formatMessage(messages.compress) : intl.formatMessage(messages.expand); + const cursor = scale === MIN_SCALE ? null : (dragged ? 'grabbing' : 'grab'); return ( - <> - + {alt} -
- {alt} -
- +
); } - } -export default injectIntl(ZoomableImage); +export default ZoomableImage; diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 7f2e88d440..d0563bb1b2 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "Unfocus compose textarea/search", "keyboard_shortcuts.up": "Move up in the list", "lightbox.close": "Close", - "lightbox.compress": "Compress image view box", - "lightbox.expand": "Expand image view box", "lightbox.next": "Next", "lightbox.previous": "Previous", + "lightbox.zoom_in": "Zoom to actual size", + "lightbox.zoom_out": "Zoom to fit", "limited_account_hint.action": "Show profile anyway", "limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.", "link_preview.author": "By {name}", diff --git a/app/javascript/material-icons/400-24px/fit_screen-fill.svg b/app/javascript/material-icons/400-24px/fit_screen-fill.svg new file mode 100644 index 0000000000..a2ed8ca581 --- /dev/null +++ b/app/javascript/material-icons/400-24px/fit_screen-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/fit_screen.svg b/app/javascript/material-icons/400-24px/fit_screen.svg new file mode 100644 index 0000000000..d8d06f6e8b --- /dev/null +++ b/app/javascript/material-icons/400-24px/fit_screen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 495481622a..53becb3b92 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -5764,19 +5764,34 @@ a.status-card { height: 100%; position: relative; - &__close, - &__zoom-button { - color: rgba($white, 0.7); + &__buttons { + position: absolute; + inset-inline-end: 8px; + top: 8px; + z-index: 100; + display: flex; + gap: 8px; + align-items: center; - &:hover, - &:focus, - &:active { - color: $white; - background-color: rgba($white, 0.15); - } + .icon-button { + color: rgba($white, 0.7); + padding: 8px; - &:focus { - background-color: rgba($white, 0.3); + .icon { + width: 24px; + height: 24px; + } + + &:hover, + &:focus, + &:active { + color: $white; + background-color: rgba($white, 0.15); + } + + &:focus { + background-color: rgba($white, 0.3); + } } } } @@ -5937,28 +5952,6 @@ a.status-card { } } -.media-modal__close { - position: absolute; - inset-inline-end: 8px; - top: 8px; - z-index: 100; -} - -.media-modal__zoom-button { - position: absolute; - inset-inline-end: 64px; - top: 8px; - z-index: 100; - pointer-events: auto; - transition: opacity 0.3s linear; - will-change: opacity; -} - -.media-modal__zoom-button--hidden { - pointer-events: none; - opacity: 0; -} - .onboarding-modal, .error-modal, .embed-modal { diff --git a/app/javascript/svg-icons/actual_size.svg b/app/javascript/svg-icons/actual_size.svg new file mode 100644 index 0000000000..75939cac87 --- /dev/null +++ b/app/javascript/svg-icons/actual_size.svg @@ -0,0 +1,4 @@ + + + + From 8afa3bb2fa872c2413cd264ca3321a7509a5ffdf Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 20 Sep 2024 12:10:09 +0200 Subject: [PATCH 37/93] Change Mastodon to issue correctly-signed queries by default (#31994) --- app/lib/request.rb | 2 +- app/services/activitypub/fetch_replies_service.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/lib/request.rb b/app/lib/request.rb index ab42e82300..d7da9fe63c 100644 --- a/app/lib/request.rb +++ b/app/lib/request.rb @@ -77,7 +77,7 @@ class Request @url = Addressable::URI.parse(url).normalize @http_client = options.delete(:http_client) @allow_local = options.delete(:allow_local) - @full_path = options.delete(:with_query_string) + @full_path = !options.delete(:omit_query_string) @options = options.merge(socket_class: use_proxy? || @allow_local ? ProxySocket : Socket) @options = @options.merge(timeout_class: PerOperationWithDeadline, timeout_options: TIMEOUT) @options = @options.merge(proxy_url) if use_proxy? diff --git a/app/services/activitypub/fetch_replies_service.rb b/app/services/activitypub/fetch_replies_service.rb index e2ecdef165..46cab6caf9 100644 --- a/app/services/activitypub/fetch_replies_service.rb +++ b/app/services/activitypub/fetch_replies_service.rb @@ -49,7 +49,7 @@ class ActivityPub::FetchRepliesService < BaseService rescue Mastodon::UnexpectedResponseError => e raise unless e.response && e.response.code == 401 && Addressable::URI.parse(collection_or_uri).query.present? - fetch_resource_without_id_validation(collection_or_uri, nil, true, request_options: { with_query_string: true }) + fetch_resource_without_id_validation(collection_or_uri, nil, true, request_options: { omit_query_string: false }) end end From bdf83c353f8ff8c9794a11a4133f74129fac8670 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 20 Sep 2024 08:39:48 -0400 Subject: [PATCH 38/93] Move default embed size knowledge into `OEmbedSerializer` (#31990) Co-authored-by: Claire --- app/controllers/api/oembed_controller.rb | 10 +--------- app/controllers/api/web/embeds_controller.rb | 2 +- app/serializers/oembed_serializer.rb | 6 ++++-- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/app/controllers/api/oembed_controller.rb b/app/controllers/api/oembed_controller.rb index 66da65beda..b7f22824a7 100644 --- a/app/controllers/api/oembed_controller.rb +++ b/app/controllers/api/oembed_controller.rb @@ -7,7 +7,7 @@ class Api::OEmbedController < Api::BaseController before_action :require_public_status! def show - render json: @status, serializer: OEmbedSerializer, width: maxwidth_or_default, height: maxheight_or_default + render json: @status, serializer: OEmbedSerializer, width: params[:maxwidth], height: params[:maxheight] end private @@ -23,12 +23,4 @@ class Api::OEmbedController < Api::BaseController def status_finder StatusFinder.new(params[:url]) end - - def maxwidth_or_default - (params[:maxwidth].presence || 400).to_i - end - - def maxheight_or_default - params[:maxheight].present? ? params[:maxheight].to_i : nil - end end diff --git a/app/controllers/api/web/embeds_controller.rb b/app/controllers/api/web/embeds_controller.rb index 63c3f2d90a..f82c1c50d7 100644 --- a/app/controllers/api/web/embeds_controller.rb +++ b/app/controllers/api/web/embeds_controller.rb @@ -9,7 +9,7 @@ class Api::Web::EmbedsController < Api::Web::BaseController return not_found if @status.hidden? if @status.local? - render json: @status, serializer: OEmbedSerializer, width: 400 + render json: @status, serializer: OEmbedSerializer else return not_found unless user_signed_in? diff --git a/app/serializers/oembed_serializer.rb b/app/serializers/oembed_serializer.rb index 19fa5ddec7..a693a961f4 100644 --- a/app/serializers/oembed_serializer.rb +++ b/app/serializers/oembed_serializer.rb @@ -8,6 +8,8 @@ class OEmbedSerializer < ActiveModel::Serializer div1: 'font-weight: 500;', }.freeze + DEFAULT_WIDTH = 400 + include RoutingHelper include ActionView::Helpers::TagHelper @@ -57,10 +59,10 @@ class OEmbedSerializer < ActiveModel::Serializer end def width - instance_options[:width] + (instance_options[:width] || DEFAULT_WIDTH).to_i end def height - instance_options[:height] + instance_options[:height].presence&.to_i end end From a7dbf6f5a5f3bddbc40d8b4d9067bec0a7025169 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 20 Sep 2024 08:50:51 -0400 Subject: [PATCH 39/93] Use heredoc/squish for inline css styles in oembed serializer (#31991) --- app/serializers/oembed_serializer.rb | 40 +++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/app/serializers/oembed_serializer.rb b/app/serializers/oembed_serializer.rb index a693a961f4..c87f14f26b 100644 --- a/app/serializers/oembed_serializer.rb +++ b/app/serializers/oembed_serializer.rb @@ -2,10 +2,36 @@ class OEmbedSerializer < ActiveModel::Serializer INLINE_STYLES = { - blockquote: 'max-width: 540px; min-width: 270px; background:#FCF8FF; border: 1px solid #C9C4DA; border-radius: 8px; overflow: hidden; margin: 0; padding: 0;', - a: "color: #1C1A25; text-decoration: none; display: flex; align-items: center; justify-content: center; flex-direction: column; padding: 24px; font-size: 14px; line-height: 20px; letter-spacing: 0.25px; font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', Roboto, sans-serif;", # rubocop:disable Layout/LineLength - div0: 'margin-top: 16px; color: #787588;', - div1: 'font-weight: 500;', + blockquote: <<~CSS.squish, + background: #FCF8FF; + border-radius: 8px; + border: 1px solid #C9C4DA; + margin: 0; + max-width: 540px; + min-width: 270px; + overflow: hidden; + padding: 0; + CSS + status_link: <<~CSS.squish, + align-items: center; + color: #1C1A25; + display: flex; + flex-direction: column; + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', Roboto, sans-serif; + font-size: 14px; + justify-content: center; + letter-spacing: 0.25px; + line-height: 20px; + padding: 24px; + text-decoration: none; + CSS + div_account: <<~CSS.squish, + color: #787588; + margin-top: 16px; + CSS + div_view: <<~CSS.squish, + font-weight: 500; + CSS }.freeze DEFAULT_WIDTH = 400 @@ -48,10 +74,10 @@ class OEmbedSerializer < ActiveModel::Serializer def html <<~HTML.squish
- + -
Post by @#{object.account.pretty_acct}@#{provider_name}
-
View on Mastodon
+
Post by @#{object.account.pretty_acct}@#{provider_name}
+
View on Mastodon
From 66326065b096fdfa4f8589747dc1717be03b5626 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 20 Sep 2024 09:13:04 -0400 Subject: [PATCH 40/93] Add `response.content_type` checks for JSON to `api/v1` request specs (#31981) --- spec/requests/api/oembed_spec.rb | 4 ++ .../api/v1/accounts/credentials_spec.rb | 10 +++++ .../v1/accounts/familiar_followers_spec.rb | 2 + .../api/v1/accounts/featured_tags_spec.rb | 4 ++ .../api/v1/accounts/follower_accounts_spec.rb | 4 ++ .../v1/accounts/following_accounts_spec.rb | 4 ++ .../api/v1/accounts/identity_proofs_spec.rb | 2 + spec/requests/api/v1/accounts/lists_spec.rb | 2 + spec/requests/api/v1/accounts/lookup_spec.rb | 2 + spec/requests/api/v1/accounts/notes_spec.rb | 4 ++ spec/requests/api/v1/accounts/pins_spec.rb | 4 ++ .../api/v1/accounts/relationships_spec.rb | 10 +++++ spec/requests/api/v1/accounts/search_spec.rb | 2 + .../requests/api/v1/accounts/statuses_spec.rb | 14 +++++++ spec/requests/api/v1/accounts_spec.rb | 32 ++++++++++++++ .../api/v1/admin/account_actions_spec.rb | 14 +++++++ spec/requests/api/v1/admin/accounts_spec.rb | 42 +++++++++++++++++++ .../v1/admin/canonical_email_blocks_spec.rb | 26 ++++++++++++ spec/requests/api/v1/admin/dimensions_spec.rb | 5 +++ .../api/v1/admin/domain_allows_spec.rb | 16 +++++++ .../api/v1/admin/domain_blocks_spec.rb | 24 +++++++++++ .../api/v1/admin/email_domain_blocks_spec.rb | 18 ++++++++ spec/requests/api/v1/admin/ip_blocks_spec.rb | 24 +++++++++++ spec/requests/api/v1/admin/measures_spec.rb | 4 ++ spec/requests/api/v1/admin/reports_spec.rb | 14 +++++++ spec/requests/api/v1/admin/retention_spec.rb | 4 ++ spec/requests/api/v1/admin/tags_spec.rb | 12 ++++++ .../api/v1/admin/trends/links/links_spec.rb | 14 +++++++ .../links/preview_card_providers_spec.rb | 6 +++ .../api/v1/admin/trends/statuses_spec.rb | 6 +++ .../requests/api/v1/admin/trends/tags_spec.rb | 6 +++ .../api/v1/announcements/reactions_spec.rb | 12 +++++- spec/requests/api/v1/announcements_spec.rb | 12 +++++- spec/requests/api/v1/annual_reports_spec.rb | 6 +++ spec/requests/api/v1/apps/credentials_spec.rb | 12 ++++++ spec/requests/api/v1/apps_spec.rb | 32 ++++++++++++++ spec/requests/api/v1/blocks_spec.rb | 4 ++ spec/requests/api/v1/bookmarks_spec.rb | 6 +++ spec/requests/api/v1/conversations_spec.rb | 2 + spec/requests/api/v1/custom_emojis_spec.rb | 4 ++ spec/requests/api/v1/directories_spec.rb | 8 ++++ spec/requests/api/v1/domain_blocks_spec.rb | 12 ++++++ .../api/v1/emails/confirmations_spec.rb | 24 +++++++++++ spec/requests/api/v1/endorsements_spec.rb | 6 +++ spec/requests/api/v1/favourites_spec.rb | 6 +++ .../api/v1/featured_tags/suggestions_spec.rb | 2 + spec/requests/api/v1/featured_tags_spec.rb | 22 ++++++++++ spec/requests/api/v1/filters_spec.rb | 12 ++++++ spec/requests/api/v1/follow_requests_spec.rb | 6 +++ spec/requests/api/v1/followed_tags_spec.rb | 10 ++--- spec/requests/api/v1/instance_spec.rb | 4 ++ .../api/v1/instances/activity_spec.rb | 3 ++ .../api/v1/instances/domain_blocks_spec.rb | 3 ++ .../instances/extended_descriptions_spec.rb | 2 + .../api/v1/instances/languages_spec.rb | 2 + spec/requests/api/v1/instances/peers_spec.rb | 2 + .../api/v1/instances/privacy_policies_spec.rb | 2 + spec/requests/api/v1/instances/rules_spec.rb | 2 + .../instances/translation_languages_spec.rb | 4 ++ spec/requests/api/v1/lists/accounts_spec.rb | 18 ++++++++ spec/requests/api/v1/lists_spec.rb | 24 +++++++++++ spec/requests/api/v1/markers_spec.rb | 8 ++++ spec/requests/api/v1/media_spec.rb | 18 ++++++++ spec/requests/api/v1/mutes_spec.rb | 6 +++ .../api/v1/notifications/policies_spec.rb | 4 ++ .../api/v1/notifications/requests_spec.rb | 18 ++++++++ spec/requests/api/v1/notifications_spec.rb | 32 ++++++++++++++ spec/requests/api/v1/peers/search_spec.rb | 6 +++ spec/requests/api/v1/polls/votes_spec.rb | 4 ++ spec/requests/api/v1/polls_spec.rb | 4 ++ spec/requests/api/v1/preferences_spec.rb | 5 +++ spec/requests/api/v1/profiles_spec.rb | 4 ++ .../api/v1/push/subscriptions_spec.rb | 2 + spec/requests/api/v1/reports_spec.rb | 4 ++ spec/requests/api/v1/scheduled_status_spec.rb | 8 ++++ .../api/v1/statuses/bookmarks_spec.rb | 18 ++++++++ .../statuses/favourited_by_accounts_spec.rb | 6 +++ .../api/v1/statuses/favourites_spec.rb | 16 +++++++ .../api/v1/statuses/histories_spec.rb | 2 + spec/requests/api/v1/statuses/mutes_spec.rb | 4 ++ spec/requests/api/v1/statuses/pins_spec.rb | 18 ++++++++ .../v1/statuses/reblogged_by_accounts_spec.rb | 6 +++ spec/requests/api/v1/statuses/reblogs_spec.rb | 10 +++++ spec/requests/api/v1/statuses/sources_spec.rb | 8 ++++ .../api/v1/statuses/translations_spec.rb | 4 ++ spec/requests/api/v1/statuses_spec.rb | 38 +++++++++++++++++ spec/requests/api/v1/streaming_spec.rb | 2 + spec/requests/api/v1/suggestions_spec.rb | 6 +++ spec/requests/api/v1/tags_spec.rb | 20 +++++++++ spec/requests/api/v1/timelines/home_spec.rb | 10 +++++ spec/requests/api/v1/timelines/link_spec.rb | 14 +++++++ spec/requests/api/v1/timelines/list_spec.rb | 4 ++ spec/requests/api/v1/timelines/public_spec.rb | 14 ++++--- spec/requests/api/v1/timelines/tag_spec.rb | 6 +++ spec/requests/api/v1/trends/links_spec.rb | 4 ++ spec/requests/api/v1/trends/statuses_spec.rb | 4 ++ spec/requests/api/v1/trends/tags_spec.rb | 4 ++ spec/requests/api/web/settings_spec.rb | 4 ++ 98 files changed, 930 insertions(+), 14 deletions(-) diff --git a/spec/requests/api/oembed_spec.rb b/spec/requests/api/oembed_spec.rb index b9578b37c8..767f20dddf 100644 --- a/spec/requests/api/oembed_spec.rb +++ b/spec/requests/api/oembed_spec.rb @@ -14,6 +14,8 @@ RSpec.describe 'API OEmbed' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.headers['Cache-Control']) .to include('private, no-store') end @@ -27,6 +29,8 @@ RSpec.describe 'API OEmbed' do expect(response) .to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/accounts/credentials_spec.rb b/spec/requests/api/v1/accounts/credentials_spec.rb index cc82e1892e..0bd3ace132 100644 --- a/spec/requests/api/v1/accounts/credentials_spec.rb +++ b/spec/requests/api/v1/accounts/credentials_spec.rb @@ -20,6 +20,8 @@ RSpec.describe 'credentials API' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to include({ source: hash_including({ discoverable: false, @@ -36,6 +38,8 @@ RSpec.describe 'credentials API' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to include({ locked: true, @@ -75,6 +79,8 @@ RSpec.describe 'credentials API' do it 'returns http success' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -84,6 +90,8 @@ RSpec.describe 'credentials API' do it 'returns http unprocessable entity' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -92,6 +100,8 @@ RSpec.describe 'credentials API' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to include({ source: hash_including({ diff --git a/spec/requests/api/v1/accounts/familiar_followers_spec.rb b/spec/requests/api/v1/accounts/familiar_followers_spec.rb index 8edfa4c883..c698c2d689 100644 --- a/spec/requests/api/v1/accounts/familiar_followers_spec.rb +++ b/spec/requests/api/v1/accounts/familiar_followers_spec.rb @@ -14,6 +14,8 @@ RSpec.describe 'Accounts Familiar Followers API' do get '/api/v1/accounts/familiar_followers', params: { account_id: account.id, limit: 2 }, headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end context 'when there are duplicate account IDs in the params' do diff --git a/spec/requests/api/v1/accounts/featured_tags_spec.rb b/spec/requests/api/v1/accounts/featured_tags_spec.rb index f48ed01def..632db65c44 100644 --- a/spec/requests/api/v1/accounts/featured_tags_spec.rb +++ b/spec/requests/api/v1/accounts/featured_tags_spec.rb @@ -23,6 +23,8 @@ RSpec.describe 'account featured tags API' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to contain_exactly(a_hash_including({ name: 'bar', url: "https://cb6e6126.ngrok.io/@#{account.username}/tagged/bar", @@ -37,6 +39,8 @@ RSpec.describe 'account featured tags API' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to contain_exactly(a_hash_including({ name: 'bar', url: "https://cb6e6126.ngrok.io/@#{account.pretty_acct}/tagged/bar", diff --git a/spec/requests/api/v1/accounts/follower_accounts_spec.rb b/spec/requests/api/v1/accounts/follower_accounts_spec.rb index 2672615390..7db9884a57 100644 --- a/spec/requests/api/v1/accounts/follower_accounts_spec.rb +++ b/spec/requests/api/v1/accounts/follower_accounts_spec.rb @@ -21,6 +21,8 @@ RSpec.describe 'API V1 Accounts FollowerAccounts' do get "/api/v1/accounts/#{account.id}/followers", params: { limit: 2 }, headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to eq 2 expect([response.parsed_body[0][:id], response.parsed_body[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s) end @@ -30,6 +32,8 @@ RSpec.describe 'API V1 Accounts FollowerAccounts' do get "/api/v1/accounts/#{account.id}/followers", params: { limit: 2 }, headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to eq 1 expect(response.parsed_body[0][:id]).to eq alice.id.to_s end diff --git a/spec/requests/api/v1/accounts/following_accounts_spec.rb b/spec/requests/api/v1/accounts/following_accounts_spec.rb index 19105ebf2f..ffb7332c4e 100644 --- a/spec/requests/api/v1/accounts/following_accounts_spec.rb +++ b/spec/requests/api/v1/accounts/following_accounts_spec.rb @@ -21,6 +21,8 @@ RSpec.describe 'API V1 Accounts FollowingAccounts' do get "/api/v1/accounts/#{account.id}/following", params: { limit: 2 }, headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to eq 2 expect([response.parsed_body[0][:id], response.parsed_body[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s) end @@ -30,6 +32,8 @@ RSpec.describe 'API V1 Accounts FollowingAccounts' do get "/api/v1/accounts/#{account.id}/following", params: { limit: 2 }, headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to eq 1 expect(response.parsed_body[0][:id]).to eq alice.id.to_s end diff --git a/spec/requests/api/v1/accounts/identity_proofs_spec.rb b/spec/requests/api/v1/accounts/identity_proofs_spec.rb index d1d9db8e73..ba04ed45b9 100644 --- a/spec/requests/api/v1/accounts/identity_proofs_spec.rb +++ b/spec/requests/api/v1/accounts/identity_proofs_spec.rb @@ -14,6 +14,8 @@ RSpec.describe 'Accounts Identity Proofs API' do get "/api/v1/accounts/#{account.id}/identity_proofs", params: { limit: 2 }, headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/accounts/lists_spec.rb b/spec/requests/api/v1/accounts/lists_spec.rb index 8b04f07f65..cb1ff6b9f2 100644 --- a/spec/requests/api/v1/accounts/lists_spec.rb +++ b/spec/requests/api/v1/accounts/lists_spec.rb @@ -20,6 +20,8 @@ RSpec.describe 'Accounts Lists API' do get "/api/v1/accounts/#{account.id}/lists", headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/accounts/lookup_spec.rb b/spec/requests/api/v1/accounts/lookup_spec.rb index dfd9fad49d..77c09c0902 100644 --- a/spec/requests/api/v1/accounts/lookup_spec.rb +++ b/spec/requests/api/v1/accounts/lookup_spec.rb @@ -14,6 +14,8 @@ RSpec.describe 'Accounts Lookup API' do get '/api/v1/accounts/lookup', params: { account_id: account.id, acct: account.acct }, headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/accounts/notes_spec.rb b/spec/requests/api/v1/accounts/notes_spec.rb index b8c493abcc..1677ec07e3 100644 --- a/spec/requests/api/v1/accounts/notes_spec.rb +++ b/spec/requests/api/v1/accounts/notes_spec.rb @@ -22,6 +22,8 @@ RSpec.describe 'Accounts Notes API' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(AccountNote.find_by(account_id: user.account.id, target_account_id: account.id).comment).to eq comment end end @@ -33,6 +35,8 @@ RSpec.describe 'Accounts Notes API' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') expect(AccountNote.where(account_id: user.account.id, target_account_id: account.id)).to_not exist end end diff --git a/spec/requests/api/v1/accounts/pins_spec.rb b/spec/requests/api/v1/accounts/pins_spec.rb index c66b80c7fd..8ebcb27d28 100644 --- a/spec/requests/api/v1/accounts/pins_spec.rb +++ b/spec/requests/api/v1/accounts/pins_spec.rb @@ -21,6 +21,8 @@ RSpec.describe 'Accounts Pins API' do subject end.to change { AccountPin.where(account: user.account, target_account: kevin.account).count }.by(1) expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -36,6 +38,8 @@ RSpec.describe 'Accounts Pins API' do subject end.to change { AccountPin.where(account: user.account, target_account: kevin.account).count }.by(-1) expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/accounts/relationships_spec.rb b/spec/requests/api/v1/accounts/relationships_spec.rb index 9570d1214c..52aeb01328 100644 --- a/spec/requests/api/v1/accounts/relationships_spec.rb +++ b/spec/requests/api/v1/accounts/relationships_spec.rb @@ -29,6 +29,8 @@ RSpec.describe 'GET /api/v1/accounts/relationships' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_an(Enumerable) .and contain_exactly( @@ -50,6 +52,8 @@ RSpec.describe 'GET /api/v1/accounts/relationships' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_an(Enumerable) .and have_attributes( @@ -70,6 +74,8 @@ RSpec.describe 'GET /api/v1/accounts/relationships' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_an(Enumerable) .and have_attributes( @@ -149,6 +155,8 @@ RSpec.describe 'GET /api/v1/accounts/relationships' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_an(Enumerable) @@ -171,6 +179,8 @@ RSpec.describe 'GET /api/v1/accounts/relationships' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_an(Enumerable) diff --git a/spec/requests/api/v1/accounts/search_spec.rb b/spec/requests/api/v1/accounts/search_spec.rb index f6ab7a8531..dc24813e73 100644 --- a/spec/requests/api/v1/accounts/search_spec.rb +++ b/spec/requests/api/v1/accounts/search_spec.rb @@ -13,6 +13,8 @@ RSpec.describe 'Accounts Search API' do get '/api/v1/accounts/search', params: { q: 'query' }, headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/accounts/statuses_spec.rb b/spec/requests/api/v1/accounts/statuses_spec.rb index e056a78901..1e21950287 100644 --- a/spec/requests/api/v1/accounts/statuses_spec.rb +++ b/spec/requests/api/v1/accounts/statuses_spec.rb @@ -19,6 +19,8 @@ RSpec.describe 'API V1 Accounts Statuses' do prev: api_v1_account_statuses_url(limit: 1, min_id: status.id), next: api_v1_account_statuses_url(limit: 1, max_id: status.id) ) + expect(response.content_type) + .to start_with('application/json') end context 'with only media' do @@ -26,6 +28,8 @@ RSpec.describe 'API V1 Accounts Statuses' do get "/api/v1/accounts/#{user.account.id}/statuses", params: { only_media: true }, headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -41,6 +45,8 @@ RSpec.describe 'API V1 Accounts Statuses' do it 'returns posts along with self replies', :aggregate_failures do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to have_attributes(size: 2) .and contain_exactly( @@ -61,6 +67,8 @@ RSpec.describe 'API V1 Accounts Statuses' do expect(response) .to have_http_status(200) .and include_pagination_headers(prev: api_v1_account_statuses_url(pinned: true, min_id: Status.first.id)) + expect(response.content_type) + .to start_with('application/json') end end @@ -79,6 +87,8 @@ RSpec.describe 'API V1 Accounts Statuses' do 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) ) + expect(response.content_type) + .to start_with('application/json') end end @@ -96,6 +106,8 @@ RSpec.describe 'API V1 Accounts Statuses' do get "/api/v1/accounts/#{account.id}/statuses", params: { pinned: true }, headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end context 'when user does not follow account' do @@ -122,6 +134,8 @@ RSpec.describe 'API V1 Accounts Statuses' do a_hash_including(id: status.id.to_s), a_hash_including(id: private_status.id.to_s) ) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/accounts_spec.rb b/spec/requests/api/v1/accounts_spec.rb index 2ebe56fa7d..45e66f0744 100644 --- a/spec/requests/api/v1/accounts_spec.rb +++ b/spec/requests/api/v1/accounts_spec.rb @@ -17,6 +17,8 @@ RSpec.describe '/api/v1/accounts' do get '/api/v1/accounts', headers: headers, params: { id: [account.id, other_account.id, 123_123] } expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to contain_exactly( hash_including(id: account.id.to_s), hash_including(id: other_account.id.to_s) @@ -32,6 +34,8 @@ RSpec.describe '/api/v1/accounts' do get "/api/v1/accounts/#{account.id}" expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:id]).to eq(account.id.to_s) end end @@ -41,6 +45,8 @@ RSpec.describe '/api/v1/accounts' do get '/api/v1/accounts/1' expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:error]).to eq('Record not found') end end @@ -57,6 +63,8 @@ RSpec.describe '/api/v1/accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:id]).to eq(account.id.to_s) end @@ -80,6 +88,8 @@ RSpec.describe '/api/v1/accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:access_token]).to_not be_blank user = User.find_by(email: 'hello@world.tld') @@ -93,6 +103,8 @@ RSpec.describe '/api/v1/accounts' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end end @@ -113,6 +125,8 @@ RSpec.describe '/api/v1/accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to include( @@ -133,6 +147,8 @@ RSpec.describe '/api/v1/accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to include( @@ -203,6 +219,8 @@ RSpec.describe '/api/v1/accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.following?(other_account)).to be false end @@ -225,6 +243,8 @@ RSpec.describe '/api/v1/accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.followed_by?(other_account)).to be false end @@ -247,6 +267,8 @@ RSpec.describe '/api/v1/accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.following?(other_account)).to be false expect(user.account.blocking?(other_account)).to be true end @@ -270,6 +292,8 @@ RSpec.describe '/api/v1/accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.blocking?(other_account)).to be false end @@ -292,6 +316,8 @@ RSpec.describe '/api/v1/accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.following?(other_account)).to be true expect(user.account.muting?(other_account)).to be true expect(user.account.muting_notifications?(other_account)).to be true @@ -316,6 +342,8 @@ RSpec.describe '/api/v1/accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.following?(other_account)).to be true expect(user.account.muting?(other_account)).to be true expect(user.account.muting_notifications?(other_account)).to be false @@ -340,6 +368,8 @@ RSpec.describe '/api/v1/accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.following?(other_account)).to be true expect(user.account.muting?(other_account)).to be true expect(user.account.muting_notifications?(other_account)).to be true @@ -364,6 +394,8 @@ RSpec.describe '/api/v1/accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.muting?(other_account)).to be false end diff --git a/spec/requests/api/v1/admin/account_actions_spec.rb b/spec/requests/api/v1/admin/account_actions_spec.rb index 5bcf809401..4884dba9c7 100644 --- a/spec/requests/api/v1/admin/account_actions_spec.rb +++ b/spec/requests/api/v1/admin/account_actions_spec.rb @@ -61,6 +61,8 @@ RSpec.describe 'Account actions' do it 'disables the target account' do expect { subject }.to change { target_account.reload.user_disabled? }.from(false).to(true) expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -75,6 +77,8 @@ RSpec.describe 'Account actions' do it 'marks the target account as sensitive' do expect { subject }.to change { target_account.reload.sensitized? }.from(false).to(true) expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -89,6 +93,8 @@ RSpec.describe 'Account actions' do it 'marks the target account as silenced' do expect { subject }.to change { target_account.reload.silenced? }.from(false).to(true) expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -103,6 +109,8 @@ RSpec.describe 'Account actions' do it 'marks the target account as suspended' do expect { subject }.to change { target_account.reload.suspended? }.from(false).to(true) expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -115,6 +123,8 @@ RSpec.describe 'Account actions' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -125,6 +135,8 @@ RSpec.describe 'Account actions' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -135,6 +147,8 @@ RSpec.describe 'Account actions' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/admin/accounts_spec.rb b/spec/requests/api/v1/admin/accounts_spec.rb index f557b61403..6a681f9c5e 100644 --- a/spec/requests/api/v1/admin/accounts_spec.rb +++ b/spec/requests/api/v1/admin/accounts_spec.rb @@ -19,6 +19,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.pluck(:id)).to match_array(expected_results.map { |a| a.id.to_s }) end end @@ -93,6 +95,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to eq(params[:limit]) end end @@ -112,6 +116,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match( a_hash_including(id: account.id.to_s, username: account.username, email: account.user.email) ) @@ -122,6 +128,8 @@ RSpec.describe 'Accounts' do get '/api/v1/admin/accounts/-1', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -145,6 +153,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(account.reload.user_approved?).to be(true) end @@ -166,6 +176,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(403) + expect(response.content_type) + .to start_with('application/json') end end @@ -174,6 +186,8 @@ RSpec.describe 'Accounts' do post '/api/v1/admin/accounts/-1/approve', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -197,6 +211,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(User.where(id: account.user.id)).to_not exist expect(latest_admin_action_log) @@ -214,6 +230,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(403) + expect(response.content_type) + .to start_with('application/json') end end @@ -222,6 +240,8 @@ RSpec.describe 'Accounts' do post '/api/v1/admin/accounts/-1/reject', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -244,6 +264,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(account.reload.user_disabled?).to be false end @@ -252,6 +274,8 @@ RSpec.describe 'Accounts' do post '/api/v1/admin/accounts/-1/enable', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -275,6 +299,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(account.reload.suspended?).to be false end end @@ -284,6 +310,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(403) + expect(response.content_type) + .to start_with('application/json') end end @@ -292,6 +320,8 @@ RSpec.describe 'Accounts' do post '/api/v1/admin/accounts/-1/unsuspend', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -314,6 +344,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(account.reload.sensitized?).to be false end @@ -322,6 +354,8 @@ RSpec.describe 'Accounts' do post '/api/v1/admin/accounts/-1/unsensitive', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -344,6 +378,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(account.reload.silenced?).to be false end @@ -352,6 +388,8 @@ RSpec.describe 'Accounts' do post '/api/v1/admin/accounts/-1/unsilence', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -376,6 +414,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(Admin::AccountDeletionWorker).to have_received(:perform_async).with(account.id).once end end @@ -393,6 +433,8 @@ RSpec.describe 'Accounts' do delete '/api/v1/admin/accounts/-1', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb b/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb index dd7e119911..eaa011d516 100644 --- a/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb +++ b/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb @@ -24,6 +24,8 @@ RSpec.describe 'Canonical Email Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end context 'when there is no canonical email block' do @@ -96,6 +98,8 @@ RSpec.describe 'Canonical Email Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to include( id: eq(canonical_email_block.id.to_s), @@ -109,6 +113,8 @@ RSpec.describe 'Canonical Email Blocks' do get '/api/v1/admin/canonical_email_blocks/-1', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -131,6 +137,8 @@ RSpec.describe 'Canonical Email Blocks' do subject expect(response).to have_http_status(400) + expect(response.content_type) + .to start_with('application/json') end end @@ -142,6 +150,8 @@ RSpec.describe 'Canonical Email Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.first[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash) end end @@ -151,6 +161,8 @@ RSpec.describe 'Canonical Email Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to be_empty end end @@ -173,6 +185,8 @@ RSpec.describe 'Canonical Email Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash) end @@ -183,6 +197,8 @@ RSpec.describe 'Canonical Email Blocks' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -193,6 +209,8 @@ RSpec.describe 'Canonical Email Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:canonical_email_hash]).to eq(params[:canonical_email_hash]) end end @@ -204,6 +222,8 @@ RSpec.describe 'Canonical Email Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash) end end @@ -217,6 +237,8 @@ RSpec.describe 'Canonical Email Blocks' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end end @@ -237,6 +259,8 @@ RSpec.describe 'Canonical Email Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(CanonicalEmailBlock.find_by(id: canonical_email_block.id)).to be_nil end @@ -245,6 +269,8 @@ RSpec.describe 'Canonical Email Blocks' do delete '/api/v1/admin/canonical_email_blocks/0', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/admin/dimensions_spec.rb b/spec/requests/api/v1/admin/dimensions_spec.rb index a28c2a9e3e..81fb580ba7 100644 --- a/spec/requests/api/v1/admin/dimensions_spec.rb +++ b/spec/requests/api/v1/admin/dimensions_spec.rb @@ -15,6 +15,8 @@ RSpec.describe 'Admin Dimensions' do expect(response) .to have_http_status(403) + expect(response.content_type) + .to start_with('application/json') end end @@ -27,6 +29,9 @@ RSpec.describe 'Admin Dimensions' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) .to be_an(Array) end diff --git a/spec/requests/api/v1/admin/domain_allows_spec.rb b/spec/requests/api/v1/admin/domain_allows_spec.rb index 26c962b347..eb5128e420 100644 --- a/spec/requests/api/v1/admin/domain_allows_spec.rb +++ b/spec/requests/api/v1/admin/domain_allows_spec.rb @@ -24,6 +24,8 @@ RSpec.describe 'Domain Allows' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end context 'when there is no allowed domains' do @@ -79,6 +81,8 @@ RSpec.describe 'Domain Allows' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:domain]).to eq domain_allow.domain end @@ -87,6 +91,8 @@ RSpec.describe 'Domain Allows' do get '/api/v1/admin/domain_allows/-1', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -107,6 +113,8 @@ RSpec.describe 'Domain Allows' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:domain]).to eq 'foo.bar.com' expect(DomainAllow.find_by(domain: 'foo.bar.com')).to be_present end @@ -119,6 +127,8 @@ RSpec.describe 'Domain Allows' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -129,6 +139,8 @@ RSpec.describe 'Domain Allows' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -160,6 +172,8 @@ RSpec.describe 'Domain Allows' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(DomainAllow.find_by(id: domain_allow.id)).to be_nil end @@ -168,6 +182,8 @@ RSpec.describe 'Domain Allows' do delete '/api/v1/admin/domain_allows/-1', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/admin/domain_blocks_spec.rb b/spec/requests/api/v1/admin/domain_blocks_spec.rb index 3f2cbbf113..1a506bd9be 100644 --- a/spec/requests/api/v1/admin/domain_blocks_spec.rb +++ b/spec/requests/api/v1/admin/domain_blocks_spec.rb @@ -24,6 +24,8 @@ RSpec.describe 'Domain Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end context 'when there are no domain blocks' do @@ -94,6 +96,8 @@ RSpec.describe 'Domain Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match( id: domain_block.id.to_s, domain: domain_block.domain, @@ -113,6 +117,8 @@ RSpec.describe 'Domain Blocks' do get '/api/v1/admin/domain_blocks/-1', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -132,6 +138,8 @@ RSpec.describe 'Domain Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match a_hash_including( { domain: 'foo.bar.com', @@ -153,6 +161,8 @@ RSpec.describe 'Domain Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match a_hash_including( { domain: 'foo.bar.com', @@ -173,6 +183,8 @@ RSpec.describe 'Domain Blocks' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:existing_domain_block][:domain]).to eq('foo.bar.com') end end @@ -186,6 +198,8 @@ RSpec.describe 'Domain Blocks' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:existing_domain_block][:domain]).to eq('bar.com') end end @@ -197,6 +211,8 @@ RSpec.describe 'Domain Blocks' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end end @@ -217,6 +233,8 @@ RSpec.describe 'Domain Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match a_hash_including( { id: domain_block.id.to_s, @@ -236,6 +254,8 @@ RSpec.describe 'Domain Blocks' do put '/api/v1/admin/domain_blocks/-1', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -255,6 +275,8 @@ RSpec.describe 'Domain Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(DomainBlock.find_by(id: domain_block.id)).to be_nil end @@ -263,6 +285,8 @@ RSpec.describe 'Domain Blocks' do delete '/api/v1/admin/domain_blocks/-1', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/admin/email_domain_blocks_spec.rb b/spec/requests/api/v1/admin/email_domain_blocks_spec.rb index aa3073341a..3f51a3ea2d 100644 --- a/spec/requests/api/v1/admin/email_domain_blocks_spec.rb +++ b/spec/requests/api/v1/admin/email_domain_blocks_spec.rb @@ -25,6 +25,8 @@ RSpec.describe 'Email Domain Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end context 'when there is no email domain block' do @@ -97,6 +99,8 @@ RSpec.describe 'Email Domain Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:domain]).to eq(email_domain_block.domain) end end @@ -106,6 +110,8 @@ RSpec.describe 'Email Domain Blocks' do get '/api/v1/admin/email_domain_blocks/-1', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -125,6 +131,8 @@ RSpec.describe 'Email Domain Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:domain]).to eq(params[:domain]) end @@ -135,6 +143,8 @@ RSpec.describe 'Email Domain Blocks' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -145,6 +155,8 @@ RSpec.describe 'Email Domain Blocks' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -157,6 +169,8 @@ RSpec.describe 'Email Domain Blocks' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end end @@ -176,6 +190,8 @@ RSpec.describe 'Email Domain Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to be_empty expect(EmailDomainBlock.find_by(id: email_domain_block.id)).to be_nil end @@ -185,6 +201,8 @@ RSpec.describe 'Email Domain Blocks' do delete '/api/v1/admin/email_domain_blocks/-1', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/admin/ip_blocks_spec.rb b/spec/requests/api/v1/admin/ip_blocks_spec.rb index b18f8f885c..c096aa3328 100644 --- a/spec/requests/api/v1/admin/ip_blocks_spec.rb +++ b/spec/requests/api/v1/admin/ip_blocks_spec.rb @@ -24,6 +24,8 @@ RSpec.describe 'IP Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end context 'when there is no ip block' do @@ -88,6 +90,8 @@ RSpec.describe 'IP Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to include( @@ -101,6 +105,8 @@ RSpec.describe 'IP Blocks' do get '/api/v1/admin/ip_blocks/-1', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -120,6 +126,8 @@ RSpec.describe 'IP Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to include( ip: eq("#{params[:ip]}/32"), @@ -135,6 +143,8 @@ RSpec.describe 'IP Blocks' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -145,6 +155,8 @@ RSpec.describe 'IP Blocks' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -157,6 +169,8 @@ RSpec.describe 'IP Blocks' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -167,6 +181,8 @@ RSpec.describe 'IP Blocks' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end end @@ -185,6 +201,8 @@ RSpec.describe 'IP Blocks' do .and change_comment_value expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match(hash_including({ ip: "#{ip_block.ip}/#{ip_block.ip.prefix}", severity: 'sign_up_requires_approval', @@ -205,6 +223,8 @@ RSpec.describe 'IP Blocks' do put '/api/v1/admin/ip_blocks/-1', headers: headers, params: params expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -220,6 +240,8 @@ RSpec.describe 'IP Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to be_empty expect(IpBlock.find_by(id: ip_block.id)).to be_nil end @@ -229,6 +251,8 @@ RSpec.describe 'IP Blocks' do delete '/api/v1/admin/ip_blocks/-1', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/admin/measures_spec.rb b/spec/requests/api/v1/admin/measures_spec.rb index de359a5ccd..519b5cc7b3 100644 --- a/spec/requests/api/v1/admin/measures_spec.rb +++ b/spec/requests/api/v1/admin/measures_spec.rb @@ -32,6 +32,8 @@ RSpec.describe 'Admin Measures' do expect(response) .to have_http_status(403) + expect(response.content_type) + .to start_with('application/json') end end @@ -43,6 +45,8 @@ RSpec.describe 'Admin Measures' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_an(Array) diff --git a/spec/requests/api/v1/admin/reports_spec.rb b/spec/requests/api/v1/admin/reports_spec.rb index 2c40f56dc8..a0e7b0a369 100644 --- a/spec/requests/api/v1/admin/reports_spec.rb +++ b/spec/requests/api/v1/admin/reports_spec.rb @@ -23,6 +23,8 @@ RSpec.describe 'Reports' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end context 'when there are no reports' do @@ -126,6 +128,8 @@ RSpec.describe 'Reports' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to include( { id: report.id.to_s, @@ -156,6 +160,8 @@ RSpec.describe 'Reports' do .and create_an_action_log expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') report.reload @@ -190,6 +196,8 @@ RSpec.describe 'Reports' do .to change { report.reload.unresolved? }.from(true).to(false) .and create_an_action_log expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -208,6 +216,8 @@ RSpec.describe 'Reports' do .to change { report.reload.unresolved? }.from(false).to(true) .and create_an_action_log expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -226,6 +236,8 @@ RSpec.describe 'Reports' do .to change { report.reload.assigned_account_id }.from(nil).to(user.account.id) .and create_an_action_log expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -244,6 +256,8 @@ RSpec.describe 'Reports' do .to change { report.reload.assigned_account_id }.from(user.account.id).to(nil) .and create_an_action_log expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end diff --git a/spec/requests/api/v1/admin/retention_spec.rb b/spec/requests/api/v1/admin/retention_spec.rb index c28fa6de81..e28bc2a94f 100644 --- a/spec/requests/api/v1/admin/retention_spec.rb +++ b/spec/requests/api/v1/admin/retention_spec.rb @@ -15,6 +15,8 @@ RSpec.describe 'Admin Retention' do expect(response) .to have_http_status(403) + expect(response.content_type) + .to start_with('application/json') end end @@ -26,6 +28,8 @@ RSpec.describe 'Admin Retention' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_an(Array) diff --git a/spec/requests/api/v1/admin/tags_spec.rb b/spec/requests/api/v1/admin/tags_spec.rb index fda9227acf..3623c09ac7 100644 --- a/spec/requests/api/v1/admin/tags_spec.rb +++ b/spec/requests/api/v1/admin/tags_spec.rb @@ -24,6 +24,8 @@ RSpec.describe 'Tags' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end context 'when there are no tags' do @@ -77,6 +79,8 @@ RSpec.describe 'Tags' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:id].to_i).to eq(tag.id) expect(response.parsed_body[:name]).to eq(tag.name) @@ -87,6 +91,8 @@ RSpec.describe 'Tags' do get '/api/v1/admin/tags/-1', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -107,6 +113,8 @@ RSpec.describe 'Tags' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:id].to_i).to eq(tag.id) expect(response.parsed_body[:name]).to eq(tag.name.upcase) @@ -119,6 +127,8 @@ RSpec.describe 'Tags' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -127,6 +137,8 @@ RSpec.describe 'Tags' do get '/api/v1/admin/tags/-1', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/admin/trends/links/links_spec.rb b/spec/requests/api/v1/admin/trends/links/links_spec.rb index c436b7081e..51e800734a 100644 --- a/spec/requests/api/v1/admin/trends/links/links_spec.rb +++ b/spec/requests/api/v1/admin/trends/links/links_spec.rb @@ -18,6 +18,8 @@ RSpec.describe 'Links' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -36,6 +38,8 @@ RSpec.describe 'Links' do .to change_link_trendable_to_true expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expects_correct_link_data end @@ -60,6 +64,8 @@ RSpec.describe 'Links' do post '/api/v1/admin/trends/links/-1/approve', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -70,6 +76,8 @@ RSpec.describe 'Links' do subject expect(response).to have_http_status(403) + expect(response.content_type) + .to start_with('application/json') end end end @@ -89,6 +97,8 @@ RSpec.describe 'Links' do .to_not change_link_trendable expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end def change_link_trendable @@ -114,6 +124,8 @@ RSpec.describe 'Links' do post '/api/v1/admin/trends/links/-1/reject', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -124,6 +136,8 @@ RSpec.describe 'Links' do subject expect(response).to have_http_status(403) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/admin/trends/links/preview_card_providers_spec.rb b/spec/requests/api/v1/admin/trends/links/preview_card_providers_spec.rb index 193906ab05..d46d0ff555 100644 --- a/spec/requests/api/v1/admin/trends/links/preview_card_providers_spec.rb +++ b/spec/requests/api/v1/admin/trends/links/preview_card_providers_spec.rb @@ -16,6 +16,8 @@ RSpec.describe 'API V1 Admin Trends Links Preview Card Providers' do get '/api/v1/admin/trends/links/publishers', params: { account_id: account.id, limit: 2 }, headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -29,6 +31,8 @@ RSpec.describe 'API V1 Admin Trends Links Preview Card Providers' do it 'returns http success' do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -42,6 +46,8 @@ RSpec.describe 'API V1 Admin Trends Links Preview Card Providers' do it 'returns http success' do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/admin/trends/statuses_spec.rb b/spec/requests/api/v1/admin/trends/statuses_spec.rb index e33a9658a9..c63d8d925c 100644 --- a/spec/requests/api/v1/admin/trends/statuses_spec.rb +++ b/spec/requests/api/v1/admin/trends/statuses_spec.rb @@ -16,6 +16,8 @@ RSpec.describe 'API V1 Admin Trends Statuses' do get '/api/v1/admin/trends/statuses', params: { account_id: account.id, limit: 2 }, headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -29,6 +31,8 @@ RSpec.describe 'API V1 Admin Trends Statuses' do it 'returns http success' do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -42,6 +46,8 @@ RSpec.describe 'API V1 Admin Trends Statuses' do it 'returns http success' do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/admin/trends/tags_spec.rb b/spec/requests/api/v1/admin/trends/tags_spec.rb index 748a27283c..433cc6c5a6 100644 --- a/spec/requests/api/v1/admin/trends/tags_spec.rb +++ b/spec/requests/api/v1/admin/trends/tags_spec.rb @@ -16,6 +16,8 @@ RSpec.describe 'API V1 Admin Trends Tags' do get '/api/v1/admin/trends/tags', params: { account_id: account.id, limit: 2 }, headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -29,6 +31,8 @@ RSpec.describe 'API V1 Admin Trends Tags' do it 'returns http success' do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -42,6 +46,8 @@ RSpec.describe 'API V1 Admin Trends Tags' do it 'returns http success' do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/announcements/reactions_spec.rb b/spec/requests/api/v1/announcements/reactions_spec.rb index ffacb2b0af..82154b4a26 100644 --- a/spec/requests/api/v1/announcements/reactions_spec.rb +++ b/spec/requests/api/v1/announcements/reactions_spec.rb @@ -15,7 +15,9 @@ RSpec.describe 'API V1 Announcements Reactions' do it 'returns http unauthorized' do put "/api/v1/announcements/#{announcement.id}/reactions/#{escaped_emoji}" - expect(response).to have_http_status 401 + expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end @@ -26,6 +28,8 @@ RSpec.describe 'API V1 Announcements Reactions' do it 'creates reaction', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(announcement.announcement_reactions.find_by(name: '😂', account: user.account)).to_not be_nil end end @@ -39,7 +43,9 @@ RSpec.describe 'API V1 Announcements Reactions' do context 'without token' do it 'returns http unauthorized' do delete "/api/v1/announcements/#{announcement.id}/reactions/#{escaped_emoji}" - expect(response).to have_http_status 401 + expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end @@ -50,6 +56,8 @@ RSpec.describe 'API V1 Announcements Reactions' do it 'creates reaction', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(announcement.announcement_reactions.find_by(name: '😂', account: user.account)).to be_nil end end diff --git a/spec/requests/api/v1/announcements_spec.rb b/spec/requests/api/v1/announcements_spec.rb index 1624b76012..97a4442aa9 100644 --- a/spec/requests/api/v1/announcements_spec.rb +++ b/spec/requests/api/v1/announcements_spec.rb @@ -15,7 +15,9 @@ RSpec.describe 'API V1 Announcements' do it 'returns http unprocessable entity' do get '/api/v1/announcements' - expect(response).to have_http_status 422 + expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -26,6 +28,8 @@ RSpec.describe 'API V1 Announcements' do it 'returns http success' do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end @@ -35,7 +39,9 @@ RSpec.describe 'API V1 Announcements' do it 'returns http unauthorized' do post "/api/v1/announcements/#{announcement.id}/dismiss" - expect(response).to have_http_status 401 + expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end @@ -48,6 +54,8 @@ RSpec.describe 'API V1 Announcements' do it 'dismisses announcement', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(announcement.announcement_mutes.find_by(account: user.account)).to_not be_nil end end diff --git a/spec/requests/api/v1/annual_reports_spec.rb b/spec/requests/api/v1/annual_reports_spec.rb index 8051a65484..b9831d17e2 100644 --- a/spec/requests/api/v1/annual_reports_spec.rb +++ b/spec/requests/api/v1/annual_reports_spec.rb @@ -14,6 +14,8 @@ RSpec.describe 'API V1 Annual Reports' do expect(response) .to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end @@ -33,6 +35,8 @@ RSpec.describe 'API V1 Annual Reports' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_present @@ -51,6 +55,8 @@ RSpec.describe 'API V1 Annual Reports' do .to change { annual_report.reload.viewed? }.to(true) expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/apps/credentials_spec.rb b/spec/requests/api/v1/apps/credentials_spec.rb index 6fd885eb86..30200ed60c 100644 --- a/spec/requests/api/v1/apps/credentials_spec.rb +++ b/spec/requests/api/v1/apps/credentials_spec.rb @@ -17,6 +17,8 @@ RSpec.describe 'Credentials' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match( a_hash_including( @@ -36,6 +38,8 @@ RSpec.describe 'Credentials' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:client_id]).to_not be_present expect(response.parsed_body[:client_secret]).to_not be_present @@ -51,6 +55,8 @@ RSpec.describe 'Credentials' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match( a_hash_including( @@ -74,6 +80,8 @@ RSpec.describe 'Credentials' do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end @@ -86,6 +94,8 @@ RSpec.describe 'Credentials' do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end it 'returns the error in the json response' do @@ -108,6 +118,8 @@ RSpec.describe 'Credentials' do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match( a_hash_including( diff --git a/spec/requests/api/v1/apps_spec.rb b/spec/requests/api/v1/apps_spec.rb index 51a0c3fd0c..cf43e14d62 100644 --- a/spec/requests/api/v1/apps_spec.rb +++ b/spec/requests/api/v1/apps_spec.rb @@ -28,6 +28,8 @@ RSpec.describe 'Apps' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') app = Doorkeeper::Application.find_by(name: client_name) @@ -59,6 +61,8 @@ RSpec.describe 'Apps' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(Doorkeeper::Application.find_by(name: client_name)).to be_present expect(response.parsed_body) @@ -76,6 +80,8 @@ RSpec.describe 'Apps' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') app = Doorkeeper::Application.find_by(name: client_name) @@ -96,6 +102,8 @@ RSpec.describe 'Apps' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -106,6 +114,8 @@ RSpec.describe 'Apps' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(Doorkeeper::Application.find_by(name: client_name).scopes.to_s).to eq 'read' end end @@ -117,6 +127,8 @@ RSpec.describe 'Apps' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -127,6 +139,8 @@ RSpec.describe 'Apps' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -137,6 +151,8 @@ RSpec.describe 'Apps' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -148,6 +164,8 @@ RSpec.describe 'Apps' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -158,6 +176,8 @@ RSpec.describe 'Apps' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') app = Doorkeeper::Application.find_by(name: client_name) @@ -180,6 +200,8 @@ RSpec.describe 'Apps' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') app = Doorkeeper::Application.find_by(name: client_name) @@ -202,6 +224,8 @@ RSpec.describe 'Apps' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -212,6 +236,8 @@ RSpec.describe 'Apps' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -222,6 +248,8 @@ RSpec.describe 'Apps' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -232,6 +260,8 @@ RSpec.describe 'Apps' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -242,6 +272,8 @@ RSpec.describe 'Apps' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') app = Doorkeeper::Application.find_by(name: client_name) diff --git a/spec/requests/api/v1/blocks_spec.rb b/spec/requests/api/v1/blocks_spec.rb index fc028f9bac..498cf93275 100644 --- a/spec/requests/api/v1/blocks_spec.rb +++ b/spec/requests/api/v1/blocks_spec.rb @@ -26,6 +26,8 @@ RSpec.describe 'Blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match_array(expected_response) end @@ -36,6 +38,8 @@ RSpec.describe 'Blocks' do subject expect(response.parsed_body.size).to eq(params[:limit]) + expect(response.content_type) + .to start_with('application/json') expect(response) .to include_pagination_headers( prev: api_v1_blocks_url(limit: params[:limit], since_id: blocks.last.id), diff --git a/spec/requests/api/v1/bookmarks_spec.rb b/spec/requests/api/v1/bookmarks_spec.rb index 5955de6652..c78e691236 100644 --- a/spec/requests/api/v1/bookmarks_spec.rb +++ b/spec/requests/api/v1/bookmarks_spec.rb @@ -28,6 +28,8 @@ RSpec.describe 'Bookmarks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match_array(expected_response) end @@ -40,6 +42,8 @@ RSpec.describe 'Bookmarks' do expect(response.parsed_body.size) .to eq(params[:limit]) + expect(response.content_type) + .to start_with('application/json') expect(response) .to include_pagination_headers( prev: api_v1_bookmarks_url(limit: params[:limit], min_id: bookmarks.last.id), @@ -55,6 +59,8 @@ RSpec.describe 'Bookmarks' do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/conversations_spec.rb b/spec/requests/api/v1/conversations_spec.rb index bd3cbfd0e7..6e2ac1df53 100644 --- a/spec/requests/api/v1/conversations_spec.rb +++ b/spec/requests/api/v1/conversations_spec.rb @@ -26,6 +26,8 @@ RSpec.describe 'API V1 Conversations' do prev: api_v1_conversations_url(limit: 1, min_id: Status.first.id), next: api_v1_conversations_url(limit: 1, max_id: Status.first.id) ) + expect(response.content_type) + .to start_with('application/json') end it 'returns conversations', :aggregate_failures do diff --git a/spec/requests/api/v1/custom_emojis_spec.rb b/spec/requests/api/v1/custom_emojis_spec.rb index 0942734ff3..e860fbeb17 100644 --- a/spec/requests/api/v1/custom_emojis_spec.rb +++ b/spec/requests/api/v1/custom_emojis_spec.rb @@ -18,6 +18,8 @@ RSpec.describe 'Custom Emojis' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_present @@ -33,6 +35,8 @@ RSpec.describe 'Custom Emojis' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_present diff --git a/spec/requests/api/v1/directories_spec.rb b/spec/requests/api/v1/directories_spec.rb index aa602a71cd..282be9a582 100644 --- a/spec/requests/api/v1/directories_spec.rb +++ b/spec/requests/api/v1/directories_spec.rb @@ -82,6 +82,8 @@ RSpec.describe 'Directories API' do get '/api/v1/directory', headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to eq(2) expect(response.parsed_body.pluck(:id)).to contain_exactly(eligible_remote_account.id.to_s, local_discoverable_account.id.to_s) end @@ -101,6 +103,8 @@ RSpec.describe 'Directories API' do get '/api/v1/directory', headers: headers, params: { local: '1' } expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to eq(1) expect(response.parsed_body.first[:id]).to include(local_account.id.to_s) expect(response.body).to_not include(remote_account.id.to_s) @@ -115,6 +119,8 @@ RSpec.describe 'Directories API' do get '/api/v1/directory', headers: headers, params: { order: 'active' } expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to eq(2) expect(response.parsed_body.first[:id]).to include(new_stat.account_id.to_s) expect(response.parsed_body.second[:id]).to include(old_stat.account_id.to_s) @@ -130,6 +136,8 @@ RSpec.describe 'Directories API' do get '/api/v1/directory', headers: headers, params: { order: 'new' } expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to eq(2) expect(response.parsed_body.first[:id]).to include(account_new.id.to_s) expect(response.parsed_body.second[:id]).to include(account_old.id.to_s) diff --git a/spec/requests/api/v1/domain_blocks_spec.rb b/spec/requests/api/v1/domain_blocks_spec.rb index 8184c26bed..339f49fe76 100644 --- a/spec/requests/api/v1/domain_blocks_spec.rb +++ b/spec/requests/api/v1/domain_blocks_spec.rb @@ -26,6 +26,8 @@ RSpec.describe 'Domain blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match_array(blocked_domains) end @@ -53,6 +55,8 @@ RSpec.describe 'Domain blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.domain_blocking?(params[:domain])).to be(true) end @@ -63,6 +67,8 @@ RSpec.describe 'Domain blocks' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -73,6 +79,8 @@ RSpec.describe 'Domain blocks' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end end @@ -94,6 +102,8 @@ RSpec.describe 'Domain blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.domain_blocking?('example.com')).to be(false) end @@ -104,6 +114,8 @@ RSpec.describe 'Domain blocks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/emails/confirmations_spec.rb b/spec/requests/api/v1/emails/confirmations_spec.rb index 0a419a10cf..1408ad0506 100644 --- a/spec/requests/api/v1/emails/confirmations_spec.rb +++ b/spec/requests/api/v1/emails/confirmations_spec.rb @@ -26,6 +26,8 @@ RSpec.describe 'Confirmations' do subject expect(response).to have_http_status(403) + expect(response.content_type) + .to start_with('application/json') end end @@ -41,6 +43,8 @@ RSpec.describe 'Confirmations' do subject expect(response).to have_http_status(403) + expect(response.content_type) + .to start_with('application/json') end context 'when user changed e-mail and has not confirmed it' do @@ -52,6 +56,8 @@ RSpec.describe 'Confirmations' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end @@ -61,6 +67,8 @@ RSpec.describe 'Confirmations' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -71,6 +79,8 @@ RSpec.describe 'Confirmations' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.reload.unconfirmed_email).to eq('foo@bar.com') end end @@ -82,6 +92,8 @@ RSpec.describe 'Confirmations' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end end @@ -94,6 +106,8 @@ RSpec.describe 'Confirmations' do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end end @@ -111,6 +125,8 @@ RSpec.describe 'Confirmations' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to be false end end @@ -122,6 +138,8 @@ RSpec.describe 'Confirmations' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to be true end end @@ -139,6 +157,8 @@ RSpec.describe 'Confirmations' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to be false end end @@ -150,6 +170,8 @@ RSpec.describe 'Confirmations' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to be true end end @@ -162,6 +184,8 @@ RSpec.describe 'Confirmations' do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/endorsements_spec.rb b/spec/requests/api/v1/endorsements_spec.rb index 25917f527a..730ba6350c 100644 --- a/spec/requests/api/v1/endorsements_spec.rb +++ b/spec/requests/api/v1/endorsements_spec.rb @@ -14,6 +14,8 @@ RSpec.describe 'Endorsements' do expect(response) .to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end @@ -36,6 +38,8 @@ RSpec.describe 'Endorsements' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_present @@ -51,6 +55,8 @@ RSpec.describe 'Endorsements' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to_not be_present diff --git a/spec/requests/api/v1/favourites_spec.rb b/spec/requests/api/v1/favourites_spec.rb index 2f8bef1190..44d0239556 100644 --- a/spec/requests/api/v1/favourites_spec.rb +++ b/spec/requests/api/v1/favourites_spec.rb @@ -28,6 +28,8 @@ RSpec.describe 'Favourites' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match_array(expected_response) end @@ -43,6 +45,8 @@ RSpec.describe 'Favourites' do prev: api_v1_favourites_url(limit: params[:limit], min_id: favourites.last.id), next: api_v1_favourites_url(limit: params[:limit], max_id: favourites.second.id) ) + expect(response.content_type) + .to start_with('application/json') end end @@ -53,6 +57,8 @@ RSpec.describe 'Favourites' do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/featured_tags/suggestions_spec.rb b/spec/requests/api/v1/featured_tags/suggestions_spec.rb index 8815c65cf1..5fbbec5097 100644 --- a/spec/requests/api/v1/featured_tags/suggestions_spec.rb +++ b/spec/requests/api/v1/featured_tags/suggestions_spec.rb @@ -32,6 +32,8 @@ RSpec.describe 'Featured Tags Suggestions API' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to contain_exactly( include(name: used_tag.name) diff --git a/spec/requests/api/v1/featured_tags_spec.rb b/spec/requests/api/v1/featured_tags_spec.rb index f0e939d42a..b9c78cc11b 100644 --- a/spec/requests/api/v1/featured_tags_spec.rb +++ b/spec/requests/api/v1/featured_tags_spec.rb @@ -22,6 +22,8 @@ RSpec.describe 'FeaturedTags' do get '/api/v1/featured_tags' expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end @@ -29,6 +31,8 @@ RSpec.describe 'FeaturedTags' do get '/api/v1/featured_tags', headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end context 'when the requesting user has no featured tag' do @@ -62,6 +66,8 @@ RSpec.describe 'FeaturedTags' do post '/api/v1/featured_tags', headers: headers, params: params expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to include( name: params[:name] @@ -89,6 +95,8 @@ RSpec.describe 'FeaturedTags' do post '/api/v1/featured_tags', params: params expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end @@ -97,6 +105,8 @@ RSpec.describe 'FeaturedTags' do post '/api/v1/featured_tags', headers: headers expect(response).to have_http_status(400) + expect(response.content_type) + .to start_with('application/json') end end @@ -107,6 +117,8 @@ RSpec.describe 'FeaturedTags' do post '/api/v1/featured_tags', headers: headers, params: params expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -119,6 +131,8 @@ RSpec.describe 'FeaturedTags' do post '/api/v1/featured_tags', headers: headers, params: params expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end end @@ -131,6 +145,8 @@ RSpec.describe 'FeaturedTags' do delete "/api/v1/featured_tags/#{id}", headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to be_empty featured_tag = FeaturedTag.find_by(id: id) @@ -150,6 +166,8 @@ RSpec.describe 'FeaturedTags' do delete "/api/v1/featured_tags/#{id}" expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end @@ -158,6 +176,8 @@ RSpec.describe 'FeaturedTags' do delete '/api/v1/featured_tags/0', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -169,6 +189,8 @@ RSpec.describe 'FeaturedTags' do delete "/api/v1/featured_tags/#{id}", headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/filters_spec.rb b/spec/requests/api/v1/filters_spec.rb index 93ed78b346..51f03cc04d 100644 --- a/spec/requests/api/v1/filters_spec.rb +++ b/spec/requests/api/v1/filters_spec.rb @@ -15,6 +15,8 @@ RSpec.describe 'API V1 Filters' do it 'returns http success' do get '/api/v1/filters', headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to contain_exactly( include(id: custom_filter_keyword.id.to_s) @@ -35,6 +37,8 @@ RSpec.describe 'API V1 Filters' do filter = user.account.custom_filters.first expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(filter).to_not be_nil expect(filter.keywords.pluck(:keyword, :whole_word)).to eq [['magic', whole_word]] expect(filter.context).to eq %w(home) @@ -50,6 +54,8 @@ RSpec.describe 'API V1 Filters' do filter = user.account.custom_filters.first expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(filter).to_not be_nil expect(filter.keywords.pluck(:keyword, :whole_word)).to eq [['magic', whole_word]] expect(filter.context).to eq %w(home) @@ -68,6 +74,8 @@ RSpec.describe 'API V1 Filters' do get "/api/v1/filters/#{keyword.id}", headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -82,6 +90,8 @@ RSpec.describe 'API V1 Filters' do it 'updates the filter', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(keyword.reload.phrase).to eq 'updated' end end @@ -97,6 +107,8 @@ RSpec.describe 'API V1 Filters' do it 'removes the filter', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect { keyword.reload }.to raise_error ActiveRecord::RecordNotFound end end diff --git a/spec/requests/api/v1/follow_requests_spec.rb b/spec/requests/api/v1/follow_requests_spec.rb index c143ccaec1..f0f73d38ad 100644 --- a/spec/requests/api/v1/follow_requests_spec.rb +++ b/spec/requests/api/v1/follow_requests_spec.rb @@ -36,6 +36,8 @@ RSpec.describe 'Follow requests' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match_array(expected_response) end @@ -66,6 +68,8 @@ RSpec.describe 'Follow requests' do it 'allows the requesting follower to follow', :aggregate_failures do expect { subject }.to change { follower.following?(user.account) }.from(false).to(true) expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:followed_by]).to be true end end @@ -87,6 +91,8 @@ RSpec.describe 'Follow requests' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(FollowRequest.where(target_account: user.account, account: follower)).to_not exist expect(response.parsed_body[:followed_by]).to be false end diff --git a/spec/requests/api/v1/followed_tags_spec.rb b/spec/requests/api/v1/followed_tags_spec.rb index f15c0d7f44..b0191b523f 100644 --- a/spec/requests/api/v1/followed_tags_spec.rb +++ b/spec/requests/api/v1/followed_tags_spec.rb @@ -32,20 +32,20 @@ RSpec.describe 'Followed tags' do subject expect(response).to have_http_status(:success) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match_array(expected_response) end context 'with limit param' do let(:params) { { limit: 1 } } - it 'returns only the requested number of follow tags' do + it 'returns only the requested number of follow tags and sets pagination headers' do subject + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to eq(params[:limit]) - end - - it 'sets the correct pagination headers' do - subject expect(response) .to include_pagination_headers( diff --git a/spec/requests/api/v1/instance_spec.rb b/spec/requests/api/v1/instance_spec.rb index 8d6ba572e0..821cbfec61 100644 --- a/spec/requests/api/v1/instance_spec.rb +++ b/spec/requests/api/v1/instance_spec.rb @@ -14,6 +14,8 @@ RSpec.describe 'Instances' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_present @@ -27,6 +29,8 @@ RSpec.describe 'Instances' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_present diff --git a/spec/requests/api/v1/instances/activity_spec.rb b/spec/requests/api/v1/instances/activity_spec.rb index 72e3faeb65..c2f94cc578 100644 --- a/spec/requests/api/v1/instances/activity_spec.rb +++ b/spec/requests/api/v1/instances/activity_spec.rb @@ -13,6 +13,9 @@ RSpec.describe 'Activity' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) .to be_present .and(be_an(Array)) diff --git a/spec/requests/api/v1/instances/domain_blocks_spec.rb b/spec/requests/api/v1/instances/domain_blocks_spec.rb index 460d338607..b214fda73b 100644 --- a/spec/requests/api/v1/instances/domain_blocks_spec.rb +++ b/spec/requests/api/v1/instances/domain_blocks_spec.rb @@ -17,6 +17,9 @@ RSpec.describe 'Domain Blocks' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) .to be_present .and(be_an(Array)) diff --git a/spec/requests/api/v1/instances/extended_descriptions_spec.rb b/spec/requests/api/v1/instances/extended_descriptions_spec.rb index bf6d58216a..62a7fff2ec 100644 --- a/spec/requests/api/v1/instances/extended_descriptions_spec.rb +++ b/spec/requests/api/v1/instances/extended_descriptions_spec.rb @@ -9,6 +9,8 @@ RSpec.describe 'Extended Descriptions' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_present diff --git a/spec/requests/api/v1/instances/languages_spec.rb b/spec/requests/api/v1/instances/languages_spec.rb index 3ab1ba57b8..3d188d23c0 100644 --- a/spec/requests/api/v1/instances/languages_spec.rb +++ b/spec/requests/api/v1/instances/languages_spec.rb @@ -10,6 +10,8 @@ RSpec.describe 'Languages' do it 'returns http success and includes supported languages' do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.pluck(:code)).to match_array LanguagesHelper::SUPPORTED_LOCALES.keys.map(&:to_s) end end diff --git a/spec/requests/api/v1/instances/peers_spec.rb b/spec/requests/api/v1/instances/peers_spec.rb index 1140612f0a..8ebfc93357 100644 --- a/spec/requests/api/v1/instances/peers_spec.rb +++ b/spec/requests/api/v1/instances/peers_spec.rb @@ -12,6 +12,8 @@ RSpec.describe 'Peers' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_an(Array) diff --git a/spec/requests/api/v1/instances/privacy_policies_spec.rb b/spec/requests/api/v1/instances/privacy_policies_spec.rb index 93490542cd..519c2b29d0 100644 --- a/spec/requests/api/v1/instances/privacy_policies_spec.rb +++ b/spec/requests/api/v1/instances/privacy_policies_spec.rb @@ -9,6 +9,8 @@ RSpec.describe 'Privacy Policy' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_present diff --git a/spec/requests/api/v1/instances/rules_spec.rb b/spec/requests/api/v1/instances/rules_spec.rb index 620c991ae2..b730048863 100644 --- a/spec/requests/api/v1/instances/rules_spec.rb +++ b/spec/requests/api/v1/instances/rules_spec.rb @@ -9,6 +9,8 @@ RSpec.describe 'Rules' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_an(Array) diff --git a/spec/requests/api/v1/instances/translation_languages_spec.rb b/spec/requests/api/v1/instances/translation_languages_spec.rb index 0de5ec3bc2..fef8700db9 100644 --- a/spec/requests/api/v1/instances/translation_languages_spec.rb +++ b/spec/requests/api/v1/instances/translation_languages_spec.rb @@ -10,6 +10,8 @@ RSpec.describe 'Translation Languages' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to eq({}) @@ -24,6 +26,8 @@ RSpec.describe 'Translation Languages' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to match({ und: %w(en de), en: ['de'] }) diff --git a/spec/requests/api/v1/lists/accounts_spec.rb b/spec/requests/api/v1/lists/accounts_spec.rb index 6e5f9e4e9d..3911d1f28b 100644 --- a/spec/requests/api/v1/lists/accounts_spec.rb +++ b/spec/requests/api/v1/lists/accounts_spec.rb @@ -34,6 +34,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match_array(expected_response) end @@ -68,6 +70,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(list.accounts).to include(bob) end end @@ -81,6 +85,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(list.accounts).to include(bob) end end @@ -90,6 +96,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') expect(list.accounts).to_not include(bob) end end @@ -105,6 +113,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -118,6 +128,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end end @@ -143,6 +155,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(list.accounts).to_not include(bob) expect(list.accounts).to include(peter) end @@ -154,6 +168,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(list.accounts).to contain_exactly(bob, peter) end end @@ -167,6 +183,8 @@ RSpec.describe 'Accounts' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/lists_spec.rb b/spec/requests/api/v1/lists_spec.rb index 2042a64d5c..20f27a7431 100644 --- a/spec/requests/api/v1/lists_spec.rb +++ b/spec/requests/api/v1/lists_spec.rb @@ -43,6 +43,8 @@ RSpec.describe 'Lists' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match_array(expected_response) end end @@ -60,6 +62,8 @@ RSpec.describe 'Lists' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match({ id: list.id.to_s, title: list.title, @@ -75,6 +79,8 @@ RSpec.describe 'Lists' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -83,6 +89,8 @@ RSpec.describe 'Lists' do get '/api/v1/lists/-1', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -100,6 +108,8 @@ RSpec.describe 'Lists' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match(a_hash_including(title: 'my list', replies_policy: 'none', exclusive: true)) expect(List.where(account: user.account).count).to eq(1) end @@ -111,6 +121,8 @@ RSpec.describe 'Lists' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -121,6 +133,8 @@ RSpec.describe 'Lists' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end end @@ -142,6 +156,8 @@ RSpec.describe 'Lists' do .and change_list_exclusive expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') list.reload expect(response.parsed_body).to match({ @@ -169,6 +185,8 @@ RSpec.describe 'Lists' do put '/api/v1/lists/-1', headers: headers, params: params expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -179,6 +197,8 @@ RSpec.describe 'Lists' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -196,6 +216,8 @@ RSpec.describe 'Lists' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(List.where(id: list.id)).to_not exist end @@ -214,6 +236,8 @@ RSpec.describe 'Lists' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/markers_spec.rb b/spec/requests/api/v1/markers_spec.rb index a10d2dc3e2..d7cd78924b 100644 --- a/spec/requests/api/v1/markers_spec.rb +++ b/spec/requests/api/v1/markers_spec.rb @@ -18,6 +18,8 @@ RSpec.describe 'API Markers' do it 'returns markers', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to include( home: include(last_read_id: '123'), @@ -34,6 +36,8 @@ RSpec.describe 'API Markers' do it 'creates a marker', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.markers.first.timeline).to eq 'home' expect(user.markers.first.last_read_id).to eq 69_420 end @@ -47,6 +51,8 @@ RSpec.describe 'API Markers' do it 'updates a marker', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.markers.first.timeline).to eq 'home' expect(user.markers.first.last_read_id).to eq 70_120 end @@ -61,6 +67,8 @@ RSpec.describe 'API Markers' do it 'returns error json' do expect(response) .to have_http_status(409) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to include(error: /Conflict during update/) end diff --git a/spec/requests/api/v1/media_spec.rb b/spec/requests/api/v1/media_spec.rb index a10bbb31ef..d7d0b92f11 100644 --- a/spec/requests/api/v1/media_spec.rb +++ b/spec/requests/api/v1/media_spec.rb @@ -21,6 +21,8 @@ RSpec.describe 'Media' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match( a_hash_including( id: media.id.to_s, @@ -39,6 +41,8 @@ RSpec.describe 'Media' do subject expect(response).to have_http_status(206) + expect(response.content_type) + .to start_with('application/json') end end @@ -49,6 +53,8 @@ RSpec.describe 'Media' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -59,6 +65,8 @@ RSpec.describe 'Media' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -75,6 +83,8 @@ RSpec.describe 'Media' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(MediaAttachment.first).to be_present expect(MediaAttachment.first).to have_attached_file(:file) @@ -102,6 +112,8 @@ RSpec.describe 'Media' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -112,6 +124,8 @@ RSpec.describe 'Media' do subject expect(response).to have_http_status(500) + expect(response.content_type) + .to start_with('application/json') end end end @@ -153,6 +167,8 @@ RSpec.describe 'Media' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -171,6 +187,8 @@ RSpec.describe 'Media' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/mutes_spec.rb b/spec/requests/api/v1/mutes_spec.rb index 316d455d28..61e32cb9ae 100644 --- a/spec/requests/api/v1/mutes_spec.rb +++ b/spec/requests/api/v1/mutes_spec.rb @@ -22,6 +22,8 @@ RSpec.describe 'Mutes' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') muted_accounts = mutes.map(&:target_account) expect(response.parsed_body.pluck(:id)).to match_array(muted_accounts.map { |account| account.id.to_s }) @@ -34,6 +36,8 @@ RSpec.describe 'Mutes' do subject expect(response.parsed_body.size).to eq(params[:limit]) + expect(response.content_type) + .to start_with('application/json') expect(response) .to include_pagination_headers( prev: api_v1_mutes_url(limit: params[:limit], since_id: mutes.last.id), @@ -71,6 +75,8 @@ RSpec.describe 'Mutes' do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/notifications/policies_spec.rb b/spec/requests/api/v1/notifications/policies_spec.rb index 8bafcad2fe..ac24501526 100644 --- a/spec/requests/api/v1/notifications/policies_spec.rb +++ b/spec/requests/api/v1/notifications/policies_spec.rb @@ -26,6 +26,8 @@ RSpec.describe 'Policies' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to include( filter_not_following: false, filter_not_followers: false, @@ -54,6 +56,8 @@ RSpec.describe 'Policies' do .to change { NotificationPolicy.find_or_initialize_by(account: user.account).for_not_following.to_sym }.from(:accept).to(:filter) expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to include( filter_not_following: true, filter_not_followers: false, diff --git a/spec/requests/api/v1/notifications/requests_spec.rb b/spec/requests/api/v1/notifications/requests_spec.rb index 030b7cfa21..bee9d3a3da 100644 --- a/spec/requests/api/v1/notifications/requests_spec.rb +++ b/spec/requests/api/v1/notifications/requests_spec.rb @@ -26,6 +26,8 @@ RSpec.describe 'Requests' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end @@ -43,6 +45,8 @@ RSpec.describe 'Requests' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(NotificationPermission.find_by(account: notification_request.account, from_account: notification_request.from_account)).to_not be_nil end @@ -53,6 +57,8 @@ RSpec.describe 'Requests' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -70,6 +76,8 @@ RSpec.describe 'Requests' do expect { subject }.to change(NotificationRequest, :count).by(-1) expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end context 'when notification request belongs to someone else' do @@ -79,6 +87,8 @@ RSpec.describe 'Requests' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -97,6 +107,8 @@ RSpec.describe 'Requests' do expect(NotificationPermission.find_by(account: notification_request.account, from_account: notification_request.from_account)).to_not be_nil expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -113,6 +125,8 @@ RSpec.describe 'Requests' do expect { subject }.to change(NotificationRequest, :count).by(-1) expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -128,6 +142,8 @@ RSpec.describe 'Requests' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match({ merged: true }) end end @@ -141,6 +157,8 @@ RSpec.describe 'Requests' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match({ merged: false }) end end diff --git a/spec/requests/api/v1/notifications_spec.rb b/spec/requests/api/v1/notifications_spec.rb index b74adb5dff..0e8eb6ad3b 100644 --- a/spec/requests/api/v1/notifications_spec.rb +++ b/spec/requests/api/v1/notifications_spec.rb @@ -31,6 +31,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:count]).to eq 5 end end @@ -45,6 +47,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:count]).to eq 2 end end @@ -56,6 +60,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:count]).to eq 4 end end @@ -67,6 +73,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:count]).to eq 2 end end @@ -80,6 +88,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:count]).to eq Api::V1::NotificationsController::DEFAULT_NOTIFICATIONS_COUNT_LIMIT end end @@ -111,6 +121,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to eq 5 expect(body_json_types).to include('reblog', 'mention', 'favourite', 'follow') expect(response.parsed_body.any? { |x| x[:filtered] }).to be false @@ -124,6 +136,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to eq 6 expect(body_json_types).to include('reblog', 'mention', 'favourite', 'follow') expect(response.parsed_body.any? { |x| x[:filtered] }).to be true @@ -137,6 +151,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(body_json_account_ids.uniq).to eq [tom.account.id.to_s] end @@ -152,6 +168,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to eq 0 end end @@ -163,6 +181,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to_not eq 0 expect(body_json_types.uniq).to_not include 'mention' end @@ -175,6 +195,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(body_json_types.uniq).to eq ['mention'] end end @@ -216,6 +238,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end context 'when notification belongs to someone else' do @@ -225,6 +249,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -242,6 +268,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect { notification.reload }.to raise_error(ActiveRecord::RecordNotFound) end @@ -252,6 +280,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -272,6 +302,8 @@ RSpec.describe 'Notifications' do expect(user.account.reload.notifications).to be_empty expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/peers/search_spec.rb b/spec/requests/api/v1/peers/search_spec.rb index dc5f550d0e..d00a2437f2 100644 --- a/spec/requests/api/v1/peers/search_spec.rb +++ b/spec/requests/api/v1/peers/search_spec.rb @@ -23,6 +23,8 @@ RSpec.describe 'API Peers Search' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_blank end @@ -34,6 +36,8 @@ RSpec.describe 'API Peers Search' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_blank end @@ -49,6 +53,8 @@ RSpec.describe 'API Peers Search' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size) .to eq(1) expect(response.parsed_body.first) diff --git a/spec/requests/api/v1/polls/votes_spec.rb b/spec/requests/api/v1/polls/votes_spec.rb index 669f64b6e4..d3f7eb431d 100644 --- a/spec/requests/api/v1/polls/votes_spec.rb +++ b/spec/requests/api/v1/polls/votes_spec.rb @@ -18,6 +18,8 @@ RSpec.describe 'API V1 Polls Votes' do it 'creates a vote', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(vote).to_not be_nil expect(vote.choice).to eq 1 @@ -30,6 +32,8 @@ RSpec.describe 'API V1 Polls Votes' do it 'returns http bad request' do expect(response).to have_http_status(400) + expect(response.content_type) + .to start_with('application/json') end end diff --git a/spec/requests/api/v1/polls_spec.rb b/spec/requests/api/v1/polls_spec.rb index 138a37a73c..fd38297931 100644 --- a/spec/requests/api/v1/polls_spec.rb +++ b/spec/requests/api/v1/polls_spec.rb @@ -23,6 +23,8 @@ RSpec.describe 'Polls' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match( a_hash_including( id: poll.id.to_s, @@ -41,6 +43,8 @@ RSpec.describe 'Polls' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/preferences_spec.rb b/spec/requests/api/v1/preferences_spec.rb index d6991ca90c..e03b9cf108 100644 --- a/spec/requests/api/v1/preferences_spec.rb +++ b/spec/requests/api/v1/preferences_spec.rb @@ -14,6 +14,8 @@ RSpec.describe 'Preferences' do expect(response) .to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end @@ -34,6 +36,9 @@ RSpec.describe 'Preferences' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) .to be_present end diff --git a/spec/requests/api/v1/profiles_spec.rb b/spec/requests/api/v1/profiles_spec.rb index 9616f41559..fd3ab4bf58 100644 --- a/spec/requests/api/v1/profiles_spec.rb +++ b/spec/requests/api/v1/profiles_spec.rb @@ -32,6 +32,8 @@ RSpec.describe 'Deleting profile images' do delete '/api/v1/profile/avatar', headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') account.reload expect(account.avatar).to_not exist @@ -53,6 +55,8 @@ RSpec.describe 'Deleting profile images' do delete '/api/v1/profile/header', headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') account.reload expect(account.avatar).to exist diff --git a/spec/requests/api/v1/push/subscriptions_spec.rb b/spec/requests/api/v1/push/subscriptions_spec.rb index a9587f8d58..8ad672c95e 100644 --- a/spec/requests/api/v1/push/subscriptions_spec.rb +++ b/spec/requests/api/v1/push/subscriptions_spec.rb @@ -45,6 +45,8 @@ RSpec.describe 'API V1 Push Subscriptions' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') expect(endpoint_push_subscriptions.count).to eq(0) expect(endpoint_push_subscription).to be_nil end diff --git a/spec/requests/api/v1/reports_spec.rb b/spec/requests/api/v1/reports_spec.rb index a176bd78a6..18b894bf63 100644 --- a/spec/requests/api/v1/reports_spec.rb +++ b/spec/requests/api/v1/reports_spec.rb @@ -37,6 +37,8 @@ RSpec.describe 'Reports' do emails = capture_emails { subject } expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match( a_hash_including( status_ids: [status.id.to_s], @@ -65,6 +67,8 @@ RSpec.describe 'Reports' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end diff --git a/spec/requests/api/v1/scheduled_status_spec.rb b/spec/requests/api/v1/scheduled_status_spec.rb index eb03827c9a..3a1b81ce65 100644 --- a/spec/requests/api/v1/scheduled_status_spec.rb +++ b/spec/requests/api/v1/scheduled_status_spec.rb @@ -14,6 +14,8 @@ RSpec.describe 'Scheduled Statuses' do expect(response) .to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end @@ -33,6 +35,8 @@ RSpec.describe 'Scheduled Statuses' do expect(response) .to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -45,6 +49,8 @@ RSpec.describe 'Scheduled Statuses' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to_not be_present @@ -59,6 +65,8 @@ RSpec.describe 'Scheduled Statuses' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to be_present diff --git a/spec/requests/api/v1/statuses/bookmarks_spec.rb b/spec/requests/api/v1/statuses/bookmarks_spec.rb index 6401a4370d..583f5b6a0e 100644 --- a/spec/requests/api/v1/statuses/bookmarks_spec.rb +++ b/spec/requests/api/v1/statuses/bookmarks_spec.rb @@ -22,6 +22,8 @@ RSpec.describe 'Bookmarks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.bookmarked?(status)).to be true expect(response.parsed_body).to match( @@ -37,6 +39,8 @@ RSpec.describe 'Bookmarks' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -51,6 +55,8 @@ RSpec.describe 'Bookmarks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.bookmarked?(status)).to be true end end @@ -60,6 +66,8 @@ RSpec.describe 'Bookmarks' do post '/api/v1/statuses/-1/bookmark', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -70,6 +78,8 @@ RSpec.describe 'Bookmarks' do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end end @@ -93,6 +103,8 @@ RSpec.describe 'Bookmarks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.bookmarked?(status)).to be false expect(response.parsed_body).to match( @@ -113,6 +125,8 @@ RSpec.describe 'Bookmarks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.bookmarked?(status)).to be false expect(response.parsed_body).to match( @@ -126,6 +140,8 @@ RSpec.describe 'Bookmarks' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end @@ -137,6 +153,8 @@ RSpec.describe 'Bookmarks' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end 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 24bd03d343..6471697154 100644 --- a/spec/requests/api/v1/statuses/favourited_by_accounts_spec.rb +++ b/spec/requests/api/v1/statuses/favourited_by_accounts_spec.rb @@ -33,6 +33,8 @@ RSpec.describe 'API V1 Statuses Favourited by Accounts' do 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(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size) .to eq(2) @@ -72,6 +74,8 @@ RSpec.describe 'API V1 Statuses Favourited by Accounts' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -88,6 +92,8 @@ RSpec.describe 'API V1 Statuses Favourited by Accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/statuses/favourites_spec.rb b/spec/requests/api/v1/statuses/favourites_spec.rb index c3acf0413e..3d1021e29d 100644 --- a/spec/requests/api/v1/statuses/favourites_spec.rb +++ b/spec/requests/api/v1/statuses/favourites_spec.rb @@ -22,6 +22,8 @@ RSpec.describe 'Favourites', :inline_jobs do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.favourited?(status)).to be true expect(response.parsed_body).to match( @@ -37,6 +39,8 @@ RSpec.describe 'Favourites', :inline_jobs do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -51,6 +55,8 @@ RSpec.describe 'Favourites', :inline_jobs do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.favourited?(status)).to be true end end @@ -62,6 +68,8 @@ RSpec.describe 'Favourites', :inline_jobs do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end end @@ -84,6 +92,8 @@ RSpec.describe 'Favourites', :inline_jobs do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.favourited?(status)).to be false @@ -103,6 +113,8 @@ RSpec.describe 'Favourites', :inline_jobs do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.favourited?(status)).to be false @@ -117,6 +129,8 @@ RSpec.describe 'Favourites', :inline_jobs do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -127,6 +141,8 @@ RSpec.describe 'Favourites', :inline_jobs do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/statuses/histories_spec.rb b/spec/requests/api/v1/statuses/histories_spec.rb index 4115a52fa8..9c7f93d343 100644 --- a/spec/requests/api/v1/statuses/histories_spec.rb +++ b/spec/requests/api/v1/statuses/histories_spec.rb @@ -18,6 +18,8 @@ RSpec.describe 'API V1 Statuses Histories' do it 'returns http success' do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to_not be 0 end end diff --git a/spec/requests/api/v1/statuses/mutes_spec.rb b/spec/requests/api/v1/statuses/mutes_spec.rb index 69ae948852..55313482d6 100644 --- a/spec/requests/api/v1/statuses/mutes_spec.rb +++ b/spec/requests/api/v1/statuses/mutes_spec.rb @@ -18,6 +18,8 @@ RSpec.describe 'API V1 Statuses Mutes' do it 'creates a conversation mute', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(ConversationMute.find_by(account: user.account, conversation_id: status.conversation_id)).to_not be_nil end end @@ -32,6 +34,8 @@ RSpec.describe 'API V1 Statuses Mutes' do it 'destroys the conversation mute', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(ConversationMute.find_by(account: user.account, conversation_id: status.conversation_id)).to be_nil end end diff --git a/spec/requests/api/v1/statuses/pins_spec.rb b/spec/requests/api/v1/statuses/pins_spec.rb index 409c50e7c2..05d8f570cc 100644 --- a/spec/requests/api/v1/statuses/pins_spec.rb +++ b/spec/requests/api/v1/statuses/pins_spec.rb @@ -22,6 +22,8 @@ RSpec.describe 'Pins' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.pinned?(status)).to be true expect(response.parsed_body).to match( @@ -37,6 +39,8 @@ RSpec.describe 'Pins' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.pinned?(status)).to be true end end @@ -48,6 +52,8 @@ RSpec.describe 'Pins' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -56,6 +62,8 @@ RSpec.describe 'Pins' do post '/api/v1/statuses/-1/pin', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -66,6 +74,8 @@ RSpec.describe 'Pins' do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end end @@ -86,6 +96,8 @@ RSpec.describe 'Pins' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(user.account.pinned?(status)).to be false expect(response.parsed_body).to match( @@ -99,6 +111,8 @@ RSpec.describe 'Pins' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -107,6 +121,8 @@ RSpec.describe 'Pins' do post '/api/v1/statuses/-1/unpin', headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -117,6 +133,8 @@ RSpec.describe 'Pins' do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end end 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 bd26c22f08..40457f6e89 100644 --- a/spec/requests/api/v1/statuses/reblogged_by_accounts_spec.rb +++ b/spec/requests/api/v1/statuses/reblogged_by_accounts_spec.rb @@ -32,6 +32,8 @@ RSpec.describe 'API V1 Statuses Reblogged by Accounts' do 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(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size) .to eq(2) @@ -71,6 +73,8 @@ RSpec.describe 'API V1 Statuses Reblogged by Accounts' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -87,6 +91,8 @@ RSpec.describe 'API V1 Statuses Reblogged by Accounts' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/statuses/reblogs_spec.rb b/spec/requests/api/v1/statuses/reblogs_spec.rb index 8c7894d875..5e88690074 100644 --- a/spec/requests/api/v1/statuses/reblogs_spec.rb +++ b/spec/requests/api/v1/statuses/reblogs_spec.rb @@ -19,6 +19,8 @@ RSpec.describe 'API V1 Statuses Reblogs' do context 'with public status' do it 'reblogs the status', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(status.reblogs.count).to eq 1 @@ -40,6 +42,8 @@ RSpec.describe 'API V1 Statuses Reblogs' do it 'returns http not found' do expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -55,6 +59,8 @@ RSpec.describe 'API V1 Statuses Reblogs' do it 'destroys the reblog', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(status.reblogs.count).to eq 0 @@ -80,6 +86,8 @@ RSpec.describe 'API V1 Statuses Reblogs' do it 'destroys the reblog', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(status.reblogs.count).to eq 0 @@ -103,6 +111,8 @@ RSpec.describe 'API V1 Statuses Reblogs' do it 'returns http not found' do expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/statuses/sources_spec.rb b/spec/requests/api/v1/statuses/sources_spec.rb index eab19d64da..6b769f35e1 100644 --- a/spec/requests/api/v1/statuses/sources_spec.rb +++ b/spec/requests/api/v1/statuses/sources_spec.rb @@ -22,6 +22,8 @@ RSpec.describe 'Sources' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match({ id: status.id.to_s, text: status.text, @@ -37,6 +39,8 @@ RSpec.describe 'Sources' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -51,6 +55,8 @@ RSpec.describe 'Sources' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match({ id: status.id.to_s, text: status.text, @@ -66,6 +72,8 @@ RSpec.describe 'Sources' do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/statuses/translations_spec.rb b/spec/requests/api/v1/statuses/translations_spec.rb index 047b2f0485..e316bd451b 100644 --- a/spec/requests/api/v1/statuses/translations_spec.rb +++ b/spec/requests/api/v1/statuses/translations_spec.rb @@ -20,6 +20,8 @@ RSpec.describe 'API V1 Statuses Translations' do it 'returns http unprocessable entity' do expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end end @@ -38,6 +40,8 @@ RSpec.describe 'API V1 Statuses Translations' do it 'returns http success' do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/statuses_spec.rb b/spec/requests/api/v1/statuses_spec.rb index 057800a266..ddf5945d25 100644 --- a/spec/requests/api/v1/statuses_spec.rb +++ b/spec/requests/api/v1/statuses_spec.rb @@ -18,6 +18,8 @@ RSpec.describe '/api/v1/statuses' do get '/api/v1/statuses', headers: headers, params: { id: [status.id, other_status.id, 123_123] } expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to contain_exactly( hash_including(id: status.id.to_s), hash_including(id: other_status.id.to_s) @@ -39,6 +41,8 @@ RSpec.describe '/api/v1/statuses' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end context 'when post includes filtered terms' do @@ -52,6 +56,8 @@ RSpec.describe '/api/v1/statuses' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:filtered][0]).to include({ filter: a_hash_including({ id: user.account.custom_filters.first.id.to_s, @@ -75,6 +81,8 @@ RSpec.describe '/api/v1/statuses' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:filtered][0]).to include({ filter: a_hash_including({ id: user.account.custom_filters.first.id.to_s, @@ -97,6 +105,8 @@ RSpec.describe '/api/v1/statuses' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:reblog][:filtered][0]).to include({ filter: a_hash_including({ id: user.account.custom_filters.first.id.to_s, @@ -121,6 +131,8 @@ RSpec.describe '/api/v1/statuses' do get "/api/v1/statuses/#{status.id}/context", headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -139,6 +151,8 @@ RSpec.describe '/api/v1/statuses' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s expect(response.headers['X-RateLimit-Remaining']).to eq (RateLimiter::FAMILIES[:statuses][:limit] - 1).to_s end @@ -154,6 +168,8 @@ RSpec.describe '/api/v1/statuses' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:unexpected_accounts].map { |a| a.slice(:id, :acct) }).to match [{ id: bob.id.to_s, acct: bob.acct }] end end @@ -165,6 +181,8 @@ RSpec.describe '/api/v1/statuses' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s end end @@ -179,6 +197,8 @@ RSpec.describe '/api/v1/statuses' do subject expect(response).to have_http_status(429) + expect(response.content_type) + .to start_with('application/json') expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s expect(response.headers['X-RateLimit-Remaining']).to eq '0' end @@ -191,6 +211,8 @@ RSpec.describe '/api/v1/statuses' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -202,6 +224,8 @@ RSpec.describe '/api/v1/statuses' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end it 'creates a scheduled status' do @@ -215,6 +239,8 @@ RSpec.describe '/api/v1/statuses' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') expect(account.scheduled_statuses).to be_empty end end @@ -235,6 +261,8 @@ RSpec.describe '/api/v1/statuses' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(Status.find_by(id: status.id)).to be_nil end end @@ -253,6 +281,8 @@ RSpec.describe '/api/v1/statuses' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(status.reload.text).to eq 'I am updated' end end @@ -267,6 +297,8 @@ RSpec.describe '/api/v1/statuses' do get "/api/v1/statuses/#{status.id}" expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -279,6 +311,8 @@ RSpec.describe '/api/v1/statuses' do get "/api/v1/statuses/#{status.id}/context" expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -291,6 +325,8 @@ RSpec.describe '/api/v1/statuses' do get "/api/v1/statuses/#{status.id}" expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -303,6 +339,8 @@ RSpec.describe '/api/v1/statuses' do get "/api/v1/statuses/#{status.id}/context" expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/streaming_spec.rb b/spec/requests/api/v1/streaming_spec.rb index a1f64846cf..62b43d657a 100644 --- a/spec/requests/api/v1/streaming_spec.rb +++ b/spec/requests/api/v1/streaming_spec.rb @@ -17,6 +17,8 @@ RSpec.describe 'API V1 Streaming' do get '/api/v1/streaming' expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/suggestions_spec.rb b/spec/requests/api/v1/suggestions_spec.rb index b971f88129..0a32d8899b 100644 --- a/spec/requests/api/v1/suggestions_spec.rb +++ b/spec/requests/api/v1/suggestions_spec.rb @@ -27,6 +27,8 @@ RSpec.describe 'Suggestions' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to contain_exactly(include(id: bob.id.to_s), include(id: jeff.id.to_s)) end @@ -48,6 +50,8 @@ RSpec.describe 'Suggestions' do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end end @@ -71,6 +75,8 @@ RSpec.describe 'Suggestions' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(FollowRecommendationMute.exists?(account: user.account, target_account: jeff)).to be true end diff --git a/spec/requests/api/v1/tags_spec.rb b/spec/requests/api/v1/tags_spec.rb index 9637823d45..f6ff7c614f 100644 --- a/spec/requests/api/v1/tags_spec.rb +++ b/spec/requests/api/v1/tags_spec.rb @@ -21,6 +21,8 @@ RSpec.describe 'Tags' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:name]).to eq(name) end end @@ -32,6 +34,8 @@ RSpec.describe 'Tags' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -42,6 +46,8 @@ RSpec.describe 'Tags' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -61,6 +67,8 @@ RSpec.describe 'Tags' do subject expect(response).to have_http_status(:success) + expect(response.content_type) + .to start_with('application/json') expect(TagFollow.where(tag: tag, account: user.account)).to exist end end @@ -72,6 +80,8 @@ RSpec.describe 'Tags' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(Tag.where(name: name)).to exist expect(TagFollow.where(tag: Tag.find_by(name: name), account: user.account)).to exist end @@ -84,6 +94,8 @@ RSpec.describe 'Tags' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -95,6 +107,8 @@ RSpec.describe 'Tags' do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end end @@ -117,6 +131,8 @@ RSpec.describe 'Tags' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(TagFollow.where(tag: tag, account: user.account)).to_not exist end @@ -127,6 +143,8 @@ RSpec.describe 'Tags' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -138,6 +156,8 @@ RSpec.describe 'Tags' do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/timelines/home_spec.rb b/spec/requests/api/v1/timelines/home_spec.rb index 19a6f3adbc..2023b189ec 100644 --- a/spec/requests/api/v1/timelines/home_spec.rb +++ b/spec/requests/api/v1/timelines/home_spec.rb @@ -35,6 +35,8 @@ RSpec.describe 'Home', :inline_jobs do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.pluck(:id)).to match_array(home_statuses.map { |status| status.id.to_s }) end @@ -52,6 +54,8 @@ RSpec.describe 'Home', :inline_jobs do prev: api_v1_timelines_home_url(limit: params[:limit], min_id: ana.statuses.first.id), next: api_v1_timelines_home_url(limit: params[:limit], max_id: ana.statuses.first.id) ) + expect(response.content_type) + .to start_with('application/json') end end end @@ -67,6 +71,8 @@ RSpec.describe 'Home', :inline_jobs do subject expect(response).to have_http_status(206) + expect(response.content_type) + .to start_with('application/json') end end @@ -77,6 +83,8 @@ RSpec.describe 'Home', :inline_jobs do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end @@ -89,6 +97,8 @@ RSpec.describe 'Home', :inline_jobs do expect(response) .to have_http_status(422) .and not_have_http_link_header + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/timelines/link_spec.rb b/spec/requests/api/v1/timelines/link_spec.rb index e1d421fb7a..37a3b36872 100644 --- a/spec/requests/api/v1/timelines/link_spec.rb +++ b/spec/requests/api/v1/timelines/link_spec.rb @@ -13,6 +13,8 @@ RSpec.describe 'Link' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.pluck(:id)).to match_array(expected_statuses.map { |status| status.id.to_s }) end end @@ -50,6 +52,8 @@ RSpec.describe 'Link' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -62,6 +66,8 @@ RSpec.describe 'Link' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -74,6 +80,8 @@ RSpec.describe 'Link' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -91,6 +99,8 @@ RSpec.describe 'Link' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -101,6 +111,8 @@ RSpec.describe 'Link' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -127,6 +139,8 @@ RSpec.describe 'Link' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to eq(params[:limit]) expect(response) diff --git a/spec/requests/api/v1/timelines/list_spec.rb b/spec/requests/api/v1/timelines/list_spec.rb index eb4395d1f9..1be754f264 100644 --- a/spec/requests/api/v1/timelines/list_spec.rb +++ b/spec/requests/api/v1/timelines/list_spec.rb @@ -23,6 +23,8 @@ RSpec.describe 'API V1 Timelines List' do get "/api/v1/timelines/list/#{list.id}", headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end @@ -36,6 +38,8 @@ RSpec.describe 'API V1 Timelines List' do get "/api/v1/timelines/list/#{list.id}", headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v1/timelines/public_spec.rb b/spec/requests/api/v1/timelines/public_spec.rb index 759e236d05..1e459bf3ec 100644 --- a/spec/requests/api/v1/timelines/public_spec.rb +++ b/spec/requests/api/v1/timelines/public_spec.rb @@ -13,6 +13,8 @@ RSpec.describe 'Public' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.pluck(:id)).to match_array(expected_statuses.map { |status| status.id.to_s }) end end @@ -77,15 +79,13 @@ RSpec.describe 'Public' do context 'with limit param' do let(:params) { { limit: 1 } } - it 'returns only the requested number of statuses', :aggregate_failures do + it 'returns only the requested number of statuses and sets pagination headers', :aggregate_failures do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to eq(params[:limit]) - end - - it 'sets the correct pagination headers', :aggregate_failures do - subject expect(response) .to include_pagination_headers( @@ -110,6 +110,8 @@ RSpec.describe 'Public' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -120,6 +122,8 @@ RSpec.describe 'Public' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end diff --git a/spec/requests/api/v1/timelines/tag_spec.rb b/spec/requests/api/v1/timelines/tag_spec.rb index 03d34e59f1..75348fe1b4 100644 --- a/spec/requests/api/v1/timelines/tag_spec.rb +++ b/spec/requests/api/v1/timelines/tag_spec.rb @@ -19,6 +19,8 @@ RSpec.describe 'Tag' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.pluck(:id)) .to match_array(expected_statuses.map { |status| status.id.to_s }) .and not_include(private_status.id) @@ -81,6 +83,8 @@ RSpec.describe 'Tag' do prev: api_v1_timelines_tag_url(limit: params[:limit], min_id: love_status.id), next: api_v1_timelines_tag_url(limit: params[:limit], max_id: love_status.id) ) + expect(response.content_type) + .to start_with('application/json') end end @@ -107,6 +111,8 @@ RSpec.describe 'Tag' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end diff --git a/spec/requests/api/v1/trends/links_spec.rb b/spec/requests/api/v1/trends/links_spec.rb index 04d36da0a2..b1b77e7fd8 100644 --- a/spec/requests/api/v1/trends/links_spec.rb +++ b/spec/requests/api/v1/trends/links_spec.rb @@ -13,6 +13,8 @@ RSpec.describe 'API V1 Trends Links' do expect(response) .to have_http_status(200) .and not_have_http_link_header + expect(response.content_type) + .to start_with('application/json') end end @@ -27,6 +29,8 @@ RSpec.describe 'API V1 Trends Links' do expect(response) .to have_http_status(200) .and have_http_link_header(api_v1_trends_links_url(offset: 2)).for(rel: 'next') + expect(response.content_type) + .to start_with('application/json') end def prepare_trends diff --git a/spec/requests/api/v1/trends/statuses_spec.rb b/spec/requests/api/v1/trends/statuses_spec.rb index f04addfe0a..fe00c9c645 100644 --- a/spec/requests/api/v1/trends/statuses_spec.rb +++ b/spec/requests/api/v1/trends/statuses_spec.rb @@ -13,6 +13,8 @@ RSpec.describe 'API V1 Trends Statuses' do expect(response) .to have_http_status(200) .and not_have_http_link_header + expect(response.content_type) + .to start_with('application/json') end end @@ -27,6 +29,8 @@ RSpec.describe 'API V1 Trends Statuses' do expect(response) .to have_http_status(200) .and have_http_link_header(api_v1_trends_statuses_url(offset: 2)).for(rel: 'next') + expect(response.content_type) + .to start_with('application/json') end def prepare_trends diff --git a/spec/requests/api/v1/trends/tags_spec.rb b/spec/requests/api/v1/trends/tags_spec.rb index 2ff51eed63..14ab73fc96 100644 --- a/spec/requests/api/v1/trends/tags_spec.rb +++ b/spec/requests/api/v1/trends/tags_spec.rb @@ -13,6 +13,8 @@ RSpec.describe 'API V1 Trends Tags' do expect(response) .to have_http_status(200) .and not_have_http_link_header + expect(response.content_type) + .to start_with('application/json') end end @@ -27,6 +29,8 @@ RSpec.describe 'API V1 Trends Tags' do expect(response) .to have_http_status(200) .and have_http_link_header(api_v1_trends_tags_url(offset: 2)).for(rel: 'next') + expect(response.content_type) + .to start_with('application/json') end def prepare_trends diff --git a/spec/requests/api/web/settings_spec.rb b/spec/requests/api/web/settings_spec.rb index 81b8b44953..3873bc179b 100644 --- a/spec/requests/api/web/settings_spec.rb +++ b/spec/requests/api/web/settings_spec.rb @@ -17,6 +17,8 @@ RSpec.describe '/api/web/settings' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -29,6 +31,8 @@ RSpec.describe '/api/web/settings' do expect(response) .to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end From 171394e914b4f3cc15b6659a45ecc210e42ee004 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 20 Sep 2024 09:13:47 -0400 Subject: [PATCH 41/93] Add coverage for CSV responses for severed relationships (#31962) --- spec/requests/severed_relationships_spec.rb | 43 +++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 spec/requests/severed_relationships_spec.rb diff --git a/spec/requests/severed_relationships_spec.rb b/spec/requests/severed_relationships_spec.rb new file mode 100644 index 0000000000..ac98ab8f94 --- /dev/null +++ b/spec/requests/severed_relationships_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Severed Relationships' do + let(:account_rs_event) { Fabricate :account_relationship_severance_event } + + before { sign_in Fabricate(:user) } + + describe 'GET /severed_relationships/:id/following' do + it 'returns a CSV file with correct data' do + get following_severed_relationship_path(account_rs_event, format: :csv) + + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('text/csv') + expect(response.headers['Content-Disposition']) + .to match(<<~FILENAME.squish) + attachment; filename="following-example.com-#{Date.current}.csv" + FILENAME + expect(response.body) + .to include('Account address') + end + end + + describe 'GET /severed_relationships/:id/followers' do + it 'returns a CSV file with correct data' do + get followers_severed_relationship_path(account_rs_event, format: :csv) + + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('text/csv') + expect(response.headers['Content-Disposition']) + .to match(<<~FILENAME.squish) + attachment; filename="followers-example.com-#{Date.current}.csv" + FILENAME + expect(response.body) + .to include('Account address') + end + end +end From d55f4fbda1967ab0889e6553dfad9f91bb2805f0 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 20 Sep 2024 09:19:53 -0400 Subject: [PATCH 42/93] Add content type checks to api/v2 request specs (#31983) --- spec/requests/api/v2/admin/accounts_spec.rb | 12 ++++++ spec/requests/api/v2/filters/keywords_spec.rb | 20 ++++++++++ spec/requests/api/v2/filters/statuses_spec.rb | 14 +++++++ spec/requests/api/v2/filters_spec.rb | 28 ++++++++++++++ spec/requests/api/v2/instance_spec.rb | 6 +++ spec/requests/api/v2/media_spec.rb | 12 ++++++ .../api/v2/notifications/accounts_spec.rb | 4 ++ .../api/v2/notifications/policies_spec.rb | 4 ++ spec/requests/api/v2/notifications_spec.rb | 38 +++++++++++++++++++ spec/requests/api/v2/search_spec.rb | 24 ++++++++++++ spec/requests/api/v2/suggestions_spec.rb | 2 + spec/requests/api/web/embeds_spec.rb | 24 ++++++++++++ 12 files changed, 188 insertions(+) diff --git a/spec/requests/api/v2/admin/accounts_spec.rb b/spec/requests/api/v2/admin/accounts_spec.rb index 17c38e2e55..bc3db4f886 100644 --- a/spec/requests/api/v2/admin/accounts_spec.rb +++ b/spec/requests/api/v2/admin/accounts_spec.rb @@ -34,6 +34,8 @@ RSpec.describe 'API V2 Admin Accounts' do it 'returns the correct accounts' do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(body_json_ids).to eq([admin_account.id]) end end @@ -43,6 +45,8 @@ RSpec.describe 'API V2 Admin Accounts' do it 'returns the correct accounts' do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(body_json_ids).to include(remote_account.id) expect(body_json_ids).to_not include(other_remote_account.id) end @@ -53,6 +57,8 @@ RSpec.describe 'API V2 Admin Accounts' do it 'returns the correct accounts' do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(body_json_ids).to include(suspended_remote.id, suspended_account.id) end end @@ -62,6 +68,8 @@ RSpec.describe 'API V2 Admin Accounts' do it 'returns the correct accounts' do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(body_json_ids).to include(disabled_account.id) end end @@ -71,6 +79,8 @@ RSpec.describe 'API V2 Admin Accounts' do it 'returns the correct accounts' do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(body_json_ids).to include(pending_account.id) end end @@ -85,6 +95,8 @@ RSpec.describe 'API V2 Admin Accounts' do it 'sets the correct pagination headers' do expect(response) .to include_pagination_headers(next: api_v2_admin_accounts_url(limit: 1, max_id: admin_account.id)) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v2/filters/keywords_spec.rb b/spec/requests/api/v2/filters/keywords_spec.rb index a31accaa5c..f0d62e9ecc 100644 --- a/spec/requests/api/v2/filters/keywords_spec.rb +++ b/spec/requests/api/v2/filters/keywords_spec.rb @@ -17,6 +17,8 @@ RSpec.describe 'API V2 Filters Keywords' do it 'returns http success' do get "/api/v2/filters/#{filter.id}/keywords", headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to contain_exactly( include(id: keyword.id.to_s) @@ -27,6 +29,8 @@ RSpec.describe 'API V2 Filters Keywords' do it 'returns http not found' do get "/api/v2/filters/#{other_filter.id}/keywords", headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -41,6 +45,8 @@ RSpec.describe 'API V2 Filters Keywords' do it 'creates a filter', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to include( @@ -58,6 +64,8 @@ RSpec.describe 'API V2 Filters Keywords' do it 'returns http not found' do expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -72,6 +80,8 @@ RSpec.describe 'API V2 Filters Keywords' do it 'responds with the keyword', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to include( @@ -85,6 +95,8 @@ RSpec.describe 'API V2 Filters Keywords' do it 'returns http not found' do expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -99,6 +111,8 @@ RSpec.describe 'API V2 Filters Keywords' do it 'updates the keyword', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(keyword.reload.keyword).to eq 'updated' end @@ -108,6 +122,8 @@ RSpec.describe 'API V2 Filters Keywords' do it 'returns http not found' do expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -122,6 +138,8 @@ RSpec.describe 'API V2 Filters Keywords' do it 'destroys the keyword', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect { keyword.reload }.to raise_error ActiveRecord::RecordNotFound end @@ -131,6 +149,8 @@ RSpec.describe 'API V2 Filters Keywords' do it 'returns http not found' do expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v2/filters/statuses_spec.rb b/spec/requests/api/v2/filters/statuses_spec.rb index aed8934a5e..f20a6ae5e6 100644 --- a/spec/requests/api/v2/filters/statuses_spec.rb +++ b/spec/requests/api/v2/filters/statuses_spec.rb @@ -17,6 +17,8 @@ RSpec.describe 'API V2 Filters Statuses' do it 'returns http success' do get "/api/v2/filters/#{filter.id}/statuses", headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to contain_exactly( include(id: status_filter.id.to_s) @@ -27,6 +29,8 @@ RSpec.describe 'API V2 Filters Statuses' do it 'returns http not found' do get "/api/v2/filters/#{other_filter.id}/statuses", headers: headers expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -42,6 +46,8 @@ RSpec.describe 'API V2 Filters Statuses' do it 'creates a filter', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to include( @@ -58,6 +64,8 @@ RSpec.describe 'API V2 Filters Statuses' do it 'returns http not found' do expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -72,6 +80,8 @@ RSpec.describe 'API V2 Filters Statuses' do it 'responds with the filter', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to include( @@ -98,6 +108,8 @@ RSpec.describe 'API V2 Filters Statuses' do it 'destroys the filter', :aggregate_failures do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect { status_filter.reload }.to raise_error ActiveRecord::RecordNotFound end @@ -107,6 +119,8 @@ RSpec.describe 'API V2 Filters Statuses' do it 'returns http not found' do expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v2/filters_spec.rb b/spec/requests/api/v2/filters_spec.rb index ad8b55973e..3b5c44cefa 100644 --- a/spec/requests/api/v2/filters_spec.rb +++ b/spec/requests/api/v2/filters_spec.rb @@ -15,6 +15,8 @@ RSpec.describe 'Filters' do subject expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') end end @@ -32,6 +34,8 @@ RSpec.describe 'Filters' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.pluck(:id)).to match_array(filters.map { |filter| filter.id.to_s }) end end @@ -53,6 +57,8 @@ RSpec.describe 'Filters' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to include( @@ -81,6 +87,8 @@ RSpec.describe 'Filters' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -91,6 +99,8 @@ RSpec.describe 'Filters' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -101,6 +111,8 @@ RSpec.describe 'Filters' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end end @@ -119,6 +131,8 @@ RSpec.describe 'Filters' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body) .to include( id: filter.id.to_s @@ -132,6 +146,8 @@ RSpec.describe 'Filters' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -158,6 +174,8 @@ RSpec.describe 'Filters' do filter.reload expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(filter.title).to eq 'updated' expect(filter.reload.context).to eq %w(home public) end @@ -170,6 +188,8 @@ RSpec.describe 'Filters' do subject expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end end @@ -185,6 +205,8 @@ RSpec.describe 'Filters' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(keyword.reload.keyword).to eq 'updated' @@ -199,6 +221,8 @@ RSpec.describe 'Filters' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -217,6 +241,8 @@ RSpec.describe 'Filters' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect { filter.reload }.to raise_error ActiveRecord::RecordNotFound end @@ -228,6 +254,8 @@ RSpec.describe 'Filters' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v2/instance_spec.rb b/spec/requests/api/v2/instance_spec.rb index d484dc7c46..fae92b7391 100644 --- a/spec/requests/api/v2/instance_spec.rb +++ b/spec/requests/api/v2/instance_spec.rb @@ -15,6 +15,9 @@ RSpec.describe 'Instances' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) .to be_present .and include(title: 'Mastodon') @@ -30,6 +33,9 @@ RSpec.describe 'Instances' do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) .to be_present .and include(title: 'Mastodon') diff --git a/spec/requests/api/v2/media_spec.rb b/spec/requests/api/v2/media_spec.rb index 06ce0053e8..70e0679f57 100644 --- a/spec/requests/api/v2/media_spec.rb +++ b/spec/requests/api/v2/media_spec.rb @@ -21,6 +21,9 @@ RSpec.describe 'Media API', :attachment_processing do expect(response) .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) .to be_a(Hash) end @@ -38,6 +41,9 @@ RSpec.describe 'Media API', :attachment_processing do expect(response) .to have_http_status(202) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) .to be_a(Hash) end @@ -63,6 +69,9 @@ RSpec.describe 'Media API', :attachment_processing do expect(response) .to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) .to be_a(Hash) .and include(error: /File type/) @@ -80,6 +89,9 @@ RSpec.describe 'Media API', :attachment_processing do expect(response) .to have_http_status(500) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) .to be_a(Hash) .and include(error: /processing/) diff --git a/spec/requests/api/v2/notifications/accounts_spec.rb b/spec/requests/api/v2/notifications/accounts_spec.rb index 102b009c0b..29880a4c0a 100644 --- a/spec/requests/api/v2/notifications/accounts_spec.rb +++ b/spec/requests/api/v2/notifications/accounts_spec.rb @@ -30,6 +30,8 @@ RSpec.describe 'Accounts in grouped notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') # The group we are interested in is only favorites notifications = user.account.notifications.where(type: 'favourite').reorder(id: :desc) @@ -55,6 +57,8 @@ RSpec.describe 'Accounts in grouped notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') # The group we are interested in is only favorites notifications = user.account.notifications.where(type: 'favourite').reorder(id: :desc) diff --git a/spec/requests/api/v2/notifications/policies_spec.rb b/spec/requests/api/v2/notifications/policies_spec.rb index dc205b6ebb..f080bc730f 100644 --- a/spec/requests/api/v2/notifications/policies_spec.rb +++ b/spec/requests/api/v2/notifications/policies_spec.rb @@ -26,6 +26,8 @@ RSpec.describe 'Policies' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to include( for_not_following: 'accept', for_not_followers: 'accept', @@ -56,6 +58,8 @@ RSpec.describe 'Policies' do .and change { NotificationPolicy.find_or_initialize_by(account: user.account).for_limited_accounts.to_sym }.from(:filter).to(:drop) expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to include( for_not_following: 'filter', for_not_followers: 'accept', diff --git a/spec/requests/api/v2/notifications_spec.rb b/spec/requests/api/v2/notifications_spec.rb index 9522a39e0f..ffa0a71c77 100644 --- a/spec/requests/api/v2/notifications_spec.rb +++ b/spec/requests/api/v2/notifications_spec.rb @@ -31,6 +31,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:count]).to eq 4 end end @@ -42,6 +44,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:count]).to eq 5 end end @@ -56,6 +60,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:count]).to eq 2 end end @@ -67,6 +73,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:count]).to eq 3 end end @@ -78,6 +86,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:count]).to eq 2 end end @@ -91,6 +101,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:count]).to eq Api::V2::NotificationsController::DEFAULT_NOTIFICATIONS_COUNT_LIMIT end end @@ -125,6 +137,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:notification_groups]).to eq [] end end @@ -134,6 +148,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(body_json_types).to include('reblog', 'mention', 'favourite', 'follow') end end @@ -145,6 +161,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:notification_groups]).to contain_exactly( a_hash_including( type: 'reblog', @@ -177,6 +195,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body.size).to_not eq 0 expect(body_json_types.uniq).to_not include 'mention' end @@ -189,6 +209,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(body_json_types.uniq).to eq ['mention'] expect(response.parsed_body.dig(:notification_groups, 0, :page_min_id)).to_not be_nil end @@ -211,6 +233,8 @@ RSpec.describe 'Notifications' do # not the last that has been skipped, so pagination is very likely to give overlap next: api_v2_notifications_url(limit: params[:limit], max_id: notifications[3].id) ) + expect(response.content_type) + .to start_with('application/json') end end @@ -224,6 +248,8 @@ RSpec.describe 'Notifications' do expect(response.parsed_body[:notification_groups].size) .to eq(2) + expect(response.content_type) + .to start_with('application/json') expect(response) .to include_pagination_headers( prev: api_v2_notifications_url(limit: params[:limit], min_id: notifications.first.id), @@ -247,6 +273,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:partial_accounts].size).to be > 0 expect(response.parsed_body[:partial_accounts][0].keys.map(&:to_sym)).to contain_exactly(:acct, :avatar, :avatar_static, :bot, :id, :locked, :url) expect(response.parsed_body[:partial_accounts].pluck(:id)).to_not include(recent_account.id.to_s) @@ -261,6 +289,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(400) + expect(response.content_type) + .to start_with('application/json') end end @@ -282,6 +312,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end context 'when notification belongs to someone else' do @@ -291,6 +323,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -308,6 +342,8 @@ RSpec.describe 'Notifications' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect { notification.reload }.to raise_error(ActiveRecord::RecordNotFound) end @@ -341,6 +377,8 @@ RSpec.describe 'Notifications' do expect(user.account.reload.notifications).to be_empty expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end end diff --git a/spec/requests/api/v2/search_spec.rb b/spec/requests/api/v2/search_spec.rb index a59ec7ca6b..5a2346dc39 100644 --- a/spec/requests/api/v2/search_spec.rb +++ b/spec/requests/api/v2/search_spec.rb @@ -19,6 +19,8 @@ RSpec.describe 'Search API' do get '/api/v2/search', headers: headers, params: params expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end context 'when searching accounts' do @@ -37,6 +39,8 @@ RSpec.describe 'Search API' do get '/api/v2/search', headers: headers, params: params expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -47,6 +51,8 @@ RSpec.describe 'Search API' do get '/api/v2/search', headers: headers, params: params expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -57,6 +63,8 @@ RSpec.describe 'Search API' do get '/api/v2/search', headers: headers, params: params expect(response).to have_http_status(400) + expect(response.content_type) + .to start_with('application/json') end end @@ -67,6 +75,8 @@ RSpec.describe 'Search API' do get '/api/v2/search', headers: headers, params: params expect(response).to have_http_status(400) + expect(response.content_type) + .to start_with('application/json') end end @@ -92,6 +102,8 @@ RSpec.describe 'Search API' do get '/api/v2/search', headers: headers, params: params expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') end end @@ -102,6 +114,8 @@ RSpec.describe 'Search API' do get '/api/v2/search', headers: headers, params: params expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -118,6 +132,8 @@ RSpec.describe 'Search API' do context 'without a `q` param' do it 'returns http bad_request' do expect(response).to have_http_status(400) + expect(response.content_type) + .to start_with('application/json') end end @@ -126,6 +142,8 @@ RSpec.describe 'Search API' do it 'returns http success' do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end end @@ -134,6 +152,8 @@ RSpec.describe 'Search API' do it 'returns http success' do expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') end context 'with truthy `resolve`' do @@ -141,6 +161,8 @@ RSpec.describe 'Search API' do it 'returns http unauthorized' do expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') expect(response.body).to match('resolve remote resources') end end @@ -150,6 +172,8 @@ RSpec.describe 'Search API' do it 'returns http unauthorized' do expect(response).to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') expect(response.body).to match('pagination is not supported') end end diff --git a/spec/requests/api/v2/suggestions_spec.rb b/spec/requests/api/v2/suggestions_spec.rb index e92507ed66..099d9bc3b2 100644 --- a/spec/requests/api/v2/suggestions_spec.rb +++ b/spec/requests/api/v2/suggestions_spec.rb @@ -21,6 +21,8 @@ RSpec.describe 'Suggestions API' do get '/api/v2/suggestions', headers: headers expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to match_array( [bob, jeff].map do |account| diff --git a/spec/requests/api/web/embeds_spec.rb b/spec/requests/api/web/embeds_spec.rb index 2b28502835..3cc2f977f8 100644 --- a/spec/requests/api/web/embeds_spec.rb +++ b/spec/requests/api/web/embeds_spec.rb @@ -18,6 +18,8 @@ RSpec.describe '/api/web/embed' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:html]).to be_present end end @@ -29,6 +31,8 @@ RSpec.describe '/api/web/embed' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -42,6 +46,8 @@ RSpec.describe '/api/web/embed' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -52,6 +58,8 @@ RSpec.describe '/api/web/embed' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -71,6 +79,8 @@ RSpec.describe '/api/web/embed' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:html]).to be_present end @@ -83,6 +93,8 @@ RSpec.describe '/api/web/embed' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -98,6 +110,8 @@ RSpec.describe '/api/web/embed' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -123,6 +137,8 @@ RSpec.describe '/api/web/embed' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -133,6 +149,8 @@ RSpec.describe '/api/web/embed' do subject expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body[:html]).to be_present end end @@ -146,6 +164,8 @@ RSpec.describe '/api/web/embed' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end @@ -156,6 +176,8 @@ RSpec.describe '/api/web/embed' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end @@ -167,6 +189,8 @@ RSpec.describe '/api/web/embed' do subject expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') end end end From ed8b0e4b1ea9df593aff6d831bfb9ad8fe0ed32d Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 20 Sep 2024 15:33:26 +0200 Subject: [PATCH 43/93] Fix links for reblogs in moderation interface (#31979) --- app/views/admin/reports/_status.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/reports/_status.html.haml b/app/views/admin/reports/_status.html.haml index e0870503d6..f4630ed25a 100644 --- a/app/views/admin/reports/_status.html.haml +++ b/app/views/admin/reports/_status.html.haml @@ -18,7 +18,7 @@ - if status.application = status.application.name · - = link_to ActivityPub::TagManager.instance.url_for(status), class: 'detailed-status__datetime', target: stream_link_target, rel: 'noopener noreferrer' do + = link_to ActivityPub::TagManager.instance.url_for(status.proper), class: 'detailed-status__datetime', target: stream_link_target, rel: 'noopener noreferrer' do %time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at) - if status.edited? · From 7ed9c590b98610f8d68deab9ef8df260eec6d8f0 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 20 Sep 2024 16:58:06 +0200 Subject: [PATCH 44/93] Fix issue when encountering reblog of deleted post in feed rebuild (#32001) --- app/lib/feed_manager.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index 1fb224a133..97cb25d58f 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -558,7 +558,7 @@ class FeedManager arr = crutches[:active_mentions][s.id] || [] arr.push(s.account_id) - if s.reblog? + if s.reblog? && s.reblog.present? arr.push(s.reblog.account_id) arr.concat(crutches[:active_mentions][s.reblog_of_id] || []) end From efc0d4d526e3f05b089b7eb1685dbe415f12bb03 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:35:11 +0200 Subject: [PATCH 45/93] Update dependency react-intl to v6.7.0 (#32028) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index c90ada78f5..c17ccb3988 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2330,9 +2330,9 @@ __metadata: languageName: node linkType: hard -"@formatjs/intl@npm:2.10.4": - version: 2.10.4 - resolution: "@formatjs/intl@npm:2.10.4" +"@formatjs/intl@npm:2.10.5": + version: 2.10.5 + resolution: "@formatjs/intl@npm:2.10.5" dependencies: "@formatjs/ecma402-abstract": "npm:2.0.0" "@formatjs/fast-memoize": "npm:2.2.0" @@ -2346,7 +2346,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/ca7877e962f73f1fe0e358f12d73bdc3ec4006c14ee801e06d9f7aef06bcdcc12355a8f53f32b0e890f829949ded35e825c914ca5f4709eb1e08c2a18c1368c2 + checksum: 10c0/d00ef00e41200947ed22895b73a0863283de4762ec238b4a81e2252e642e30a309cd9c73174e4917b6c675ab6f148eda5a4e3345c3caeef64e090fc8374d27c4 languageName: node linkType: hard @@ -14509,12 +14509,12 @@ __metadata: linkType: hard "react-intl@npm:^6.4.2": - version: 6.6.8 - resolution: "react-intl@npm:6.6.8" + version: 6.7.0 + resolution: "react-intl@npm:6.7.0" dependencies: "@formatjs/ecma402-abstract": "npm:2.0.0" "@formatjs/icu-messageformat-parser": "npm:2.7.8" - "@formatjs/intl": "npm:2.10.4" + "@formatjs/intl": "npm:2.10.5" "@formatjs/intl-displaynames": "npm:6.6.8" "@formatjs/intl-listformat": "npm:7.5.7" "@types/hoist-non-react-statics": "npm:^3.3.1" @@ -14528,7 +14528,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/7673507eb73ad4edd1593da7173cec68f316cf77037e0959900babd32d5984a39ba7fa10aaa0a23bcddb7b98daf7dd007cb73ddfc39127ede87c18ec780a519c + checksum: 10c0/210088bf0e934ad5f09d8e7c6d7d72682bb806583645fb333d4efd8ae55585b675ea8e47bb240140d5143ca15ecc0457c3ddc3e8ca45e9b576bce1fa2f9886b3 languageName: node linkType: hard From f294c4a594ebf6da8ea5a18bdfd62b754941575e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:35:23 +0200 Subject: [PATCH 46/93] Update libretranslate/libretranslate Docker tag to v1.6.1 (#32027) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .devcontainer/compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/compose.yaml b/.devcontainer/compose.yaml index f87082013c..5c7263c874 100644 --- a/.devcontainer/compose.yaml +++ b/.devcontainer/compose.yaml @@ -69,7 +69,7 @@ services: hard: -1 libretranslate: - image: libretranslate/libretranslate:v1.6.0 + image: libretranslate/libretranslate:v1.6.1 restart: unless-stopped volumes: - lt-data:/home/libretranslate/.local From 84d04386dd2b16e86c520349efd898c3d4bf8b44 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:35:37 +0200 Subject: [PATCH 47/93] Update DefinitelyTyped types (non-major) (#32026) 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 c17ccb3988..944262bb4f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3786,9 +3786,9 @@ __metadata: linkType: hard "@types/prop-types@npm:*, @types/prop-types@npm:^15.7.5": - version: 15.7.12 - resolution: "@types/prop-types@npm:15.7.12" - checksum: 10c0/1babcc7db6a1177779f8fde0ccc78d64d459906e6ef69a4ed4dd6339c920c2e05b074ee5a92120fe4e9d9f1a01c952f843ebd550bee2332fc2ef81d1706878f8 + version: 15.7.13 + resolution: "@types/prop-types@npm:15.7.13" + checksum: 10c0/1b20fc67281902c6743379960247bc161f3f0406ffc0df8e7058745a85ea1538612109db0406290512947f9632fe9e10e7337bf0ce6338a91d6c948df16a7c61 languageName: node linkType: hard @@ -3931,12 +3931,12 @@ __metadata: linkType: hard "@types/react@npm:*, @types/react@npm:16 || 17 || 18, @types/react@npm:>=16.9.11, @types/react@npm:^18.2.7": - version: 18.3.5 - resolution: "@types/react@npm:18.3.5" + version: 18.3.8 + resolution: "@types/react@npm:18.3.8" dependencies: "@types/prop-types": "npm:*" csstype: "npm:^3.0.2" - checksum: 10c0/548b1d3d7c2f0242fbfdbbd658731b4ce69a134be072fa83e6ab516f2840402a3f20e3e7f72e95133b23d4880ef24a6d864050dc8e1f7c68f39fa87ca8445917 + checksum: 10c0/367312c9fe276639ecb142265e090a4dd04bb39f8d718cbab546de3f1ddcfddeff415e1147d0fc40f734badaa7420b7b109d511bd4304b2c4c9c36164612fdf8 languageName: node linkType: hard From c37f9c0d44fb78e5023942ce9948168b5e637b74 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:36:57 +0200 Subject: [PATCH 48/93] Update dependency jsdom to v25.0.1 (#32017) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 56 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/yarn.lock b/yarn.lock index 944262bb4f..4aa0d4b62f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6826,12 +6826,12 @@ __metadata: languageName: node linkType: hard -"cssstyle@npm:^4.0.1": - version: 4.0.1 - resolution: "cssstyle@npm:4.0.1" +"cssstyle@npm:^4.1.0": + version: 4.1.0 + resolution: "cssstyle@npm:4.1.0" dependencies: - rrweb-cssom: "npm:^0.6.0" - checksum: 10c0/cadf9a8b23e11f4c6d63f21291096a0b0be868bd4ab9c799daa2c5b18330e39e5281605f01da906e901b42f742df0f3b3645af6465e83377ff7d15a88ee432a0 + rrweb-cssom: "npm:^0.7.1" + checksum: 10c0/05c6597e5d3e0ec6b15221f2c0ce9a0443a46cc50a6089a3ba9ee1ac27f83ff86a445a8f95435137dadd859f091fc61b6d342abaf396d3c910471b5b33cfcbfa languageName: node linkType: hard @@ -11105,10 +11105,10 @@ __metadata: linkType: hard "jsdom@npm:^25.0.0": - version: 25.0.0 - resolution: "jsdom@npm:25.0.0" + version: 25.0.1 + resolution: "jsdom@npm:25.0.1" dependencies: - cssstyle: "npm:^4.0.1" + cssstyle: "npm:^4.1.0" data-urls: "npm:^5.0.0" decimal.js: "npm:^10.4.3" form-data: "npm:^4.0.0" @@ -11121,7 +11121,7 @@ __metadata: rrweb-cssom: "npm:^0.7.1" saxes: "npm:^6.0.0" symbol-tree: "npm:^3.2.4" - tough-cookie: "npm:^4.1.4" + tough-cookie: "npm:^5.0.0" w3c-xmlserializer: "npm:^5.0.0" webidl-conversions: "npm:^7.0.0" whatwg-encoding: "npm:^3.1.1" @@ -11134,7 +11134,7 @@ __metadata: peerDependenciesMeta: canvas: optional: true - checksum: 10c0/1552bcfb816b2c69ae159ba0cd79e8964030c106cc0cb2deb20a64c1ca54e1ea41352b9802d89b7cf823e43e6d74ed7289abff4aacc95b1b2bc936570aab3594 + checksum: 10c0/6bda32a6dfe4e37a30568bf51136bdb3ba9c0b72aadd6356280404275a34c9e097c8c25b5eb3c742e602623741e172da977ff456684befd77c9042ed9bf8c2b4 languageName: node linkType: hard @@ -15366,13 +15366,6 @@ __metadata: languageName: node linkType: hard -"rrweb-cssom@npm:^0.6.0": - version: 0.6.0 - resolution: "rrweb-cssom@npm:0.6.0" - checksum: 10c0/3d9d90d53c2349ea9c8509c2690df5a4ef930c9cf8242aeb9425d4046f09d712bb01047e00da0e1c1dab5db35740b3d78fd45c3e7272f75d3724a563f27c30a3 - languageName: node - linkType: hard - "rrweb-cssom@npm:^0.7.1": version: 0.7.1 resolution: "rrweb-cssom@npm:0.7.1" @@ -17036,6 +17029,24 @@ __metadata: languageName: node linkType: hard +"tldts-core@npm:^6.1.47": + version: 6.1.47 + resolution: "tldts-core@npm:6.1.47" + checksum: 10c0/538372072aea7153e842646a9e22d0d9335acf7fd877d10ee374cf78dceff79a2ccebadf7d25e0dbddd7b7b60bafe1c885aac3e3b1d5bec7806963c89b163ee7 + languageName: node + linkType: hard + +"tldts@npm:^6.1.32": + version: 6.1.47 + resolution: "tldts@npm:6.1.47" + dependencies: + tldts-core: "npm:^6.1.47" + bin: + tldts: bin/cli.js + checksum: 10c0/42c999ab24ce3ab221cfefe77488d145d16d9523524913badaa4af4f1f0d65e0a92a678659b22b7d26d1c62796540c95158049220c9ff243090b93470f236302 + languageName: node + linkType: hard + "tmpl@npm:1.0.5": version: 1.0.5 resolution: "tmpl@npm:1.0.5" @@ -17111,7 +17122,7 @@ __metadata: languageName: node linkType: hard -"tough-cookie@npm:^4.1.2, tough-cookie@npm:^4.1.4": +"tough-cookie@npm:^4.1.2": version: 4.1.4 resolution: "tough-cookie@npm:4.1.4" dependencies: @@ -17123,6 +17134,15 @@ __metadata: languageName: node linkType: hard +"tough-cookie@npm:^5.0.0": + version: 5.0.0 + resolution: "tough-cookie@npm:5.0.0" + dependencies: + tldts: "npm:^6.1.32" + checksum: 10c0/4a69c885bf6f45c5a64e60262af99e8c0d58a33bd3d0ce5da62121eeb9c00996d0128a72df8fc4614cbde59cc8b70aa3e21e4c3c98c2bbde137d7aba7fa00124 + languageName: node + linkType: hard + "tr46@npm:^1.0.1": version: 1.0.1 resolution: "tr46@npm:1.0.1" From f4632d941ad1f89a52a2cd1dfd09b54797e935d0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:40:46 +0200 Subject: [PATCH 49/93] Update dependency aws-sdk-s3 to v1.164.0 (#32010) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 79e542014c..85c3b4abd4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -101,16 +101,16 @@ GEM awrence (1.2.1) aws-eventstream (1.3.0) aws-partitions (1.977.0) - aws-sdk-core (3.206.0) + aws-sdk-core (3.207.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.91.0) - aws-sdk-core (~> 3, >= 3.205.0) + aws-sdk-kms (1.92.0) + aws-sdk-core (~> 3, >= 3.207.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.163.0) - aws-sdk-core (~> 3, >= 3.205.0) + aws-sdk-s3 (1.164.0) + aws-sdk-core (~> 3, >= 3.207.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) aws-sigv4 (1.10.0) From 958f01e7225daf371307848d6fe2729a2580e0a8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 08:41:05 +0000 Subject: [PATCH 50/93] Update dependency sass to v1.79.3 (#32009) 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 4aa0d4b62f..eff88f772e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15468,15 +15468,15 @@ __metadata: linkType: hard "sass@npm:^1.62.1": - version: 1.79.2 - resolution: "sass@npm:1.79.2" + version: 1.79.3 + resolution: "sass@npm:1.79.3" dependencies: chokidar: "npm:^4.0.0" immutable: "npm:^4.0.0" source-map-js: "npm:>=0.6.2 <2.0.0" bin: sass: sass.js - checksum: 10c0/b637daf133da4fbafbb7e6ae07b01ff7c73e406f3134e66749bf6f712dcc0056c6971d8629d8cc2b186df5ffb2282baa8f1818f35e326b3558ab284e31fdd87d + checksum: 10c0/ad171bbbb2d7a789cc47803a59dcf2d0ac92ede34b538bb3fd683b6391a9ac3dc3eabaac264fc9582c770c4e435b85840e011785b7adfc0ac002b51ba91179c9 languageName: node linkType: hard From 10d2f83025b3161fb2a149b997d06cbb5f1b8e48 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 08:41:26 +0000 Subject: [PATCH 51/93] Update dependency selenium-webdriver to v4.25.0 (#32008) 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 85c3b4abd4..8fece04a6d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -406,7 +406,7 @@ GEM llhttp-ffi (0.5.0) ffi-compiler (~> 1.0) rake (~> 13.0) - logger (1.6.0) + logger (1.6.1) lograge (0.14.0) actionpack (>= 4) activesupport (>= 4) @@ -781,7 +781,7 @@ GEM scenic (1.8.0) activerecord (>= 4.0.0) railties (>= 4.0.0) - selenium-webdriver (4.24.0) + selenium-webdriver (4.25.0) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) From 5fae1d55e5e15bb3e44cae2677b0904799e4ef62 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 23 Sep 2024 10:42:03 +0200 Subject: [PATCH 52/93] Fix OAuth authorization prompt referring to third-party apps (#32005) --- config/locales/doorkeeper.en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/doorkeeper.en.yml b/config/locales/doorkeeper.en.yml index e28f6a7966..3b3b141afa 100644 --- a/config/locales/doorkeeper.en.yml +++ b/config/locales/doorkeeper.en.yml @@ -60,7 +60,7 @@ en: error: title: An error has occurred new: - prompt_html: "%{client_name} would like permission to access your account. It is a third-party application. If you do not trust it, then you should not authorize it." + prompt_html: "%{client_name} would like permission to access your account. Only approve this request if you recognize and trust this source." review_permissions: Review permissions title: Authorization required show: From e0b45b35c9415c912f92803ab7c797b1ae516a7e Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 23 Sep 2024 04:42:52 -0400 Subject: [PATCH 53/93] Combine repeated parsed_body assertions into single (#32002) --- .../activitypub/outboxes_controller_spec.rb | 15 +++++++++------ spec/requests/api/v1/admin/tags_spec.rb | 14 ++++++++++---- spec/requests/api/v1/apps/credentials_spec.rb | 5 +++-- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/spec/controllers/activitypub/outboxes_controller_spec.rb b/spec/controllers/activitypub/outboxes_controller_spec.rb index 7ae28e8e09..ca986dcabb 100644 --- a/spec/controllers/activitypub/outboxes_controller_spec.rb +++ b/spec/controllers/activitypub/outboxes_controller_spec.rb @@ -69,9 +69,10 @@ RSpec.describe ActivityPub::OutboxesController do expect(response.parsed_body) .to include( - orderedItems: be_an(Array).and(have_attributes(size: 2)) + orderedItems: be_an(Array) + .and(have_attributes(size: 2)) + .and(all(satisfy { |item| targets_public_collection?(item) })) ) - expect(response.parsed_body[:orderedItems].all? { |item| targets_public_collection?(item) }).to be true end context 'when account is permanently suspended' do @@ -113,9 +114,10 @@ RSpec.describe ActivityPub::OutboxesController do expect(response.parsed_body) .to include( - orderedItems: be_an(Array).and(have_attributes(size: 2)) + orderedItems: be_an(Array) + .and(have_attributes(size: 2)) + .and(all(satisfy { |item| targets_public_collection?(item) })) ) - expect(response.parsed_body[:orderedItems].all? { |item| targets_public_collection?(item) }).to be true end end @@ -132,9 +134,10 @@ RSpec.describe ActivityPub::OutboxesController do expect(response.parsed_body) .to include( - orderedItems: be_an(Array).and(have_attributes(size: 3)) + orderedItems: be_an(Array) + .and(have_attributes(size: 3)) + .and(all(satisfy { |item| targets_public_collection?(item) || targets_followers_collection?(item, account) })) ) - expect(response.parsed_body[:orderedItems].all? { |item| targets_public_collection?(item) || targets_followers_collection?(item, account) }).to be true end end diff --git a/spec/requests/api/v1/admin/tags_spec.rb b/spec/requests/api/v1/admin/tags_spec.rb index 3623c09ac7..696a11da67 100644 --- a/spec/requests/api/v1/admin/tags_spec.rb +++ b/spec/requests/api/v1/admin/tags_spec.rb @@ -82,8 +82,11 @@ RSpec.describe 'Tags' do expect(response.content_type) .to start_with('application/json') - expect(response.parsed_body[:id].to_i).to eq(tag.id) - expect(response.parsed_body[:name]).to eq(tag.name) + expect(response.parsed_body) + .to include( + id: tag.id.to_s, + name: tag.name + ) end context 'when the requested tag does not exist' do @@ -116,8 +119,11 @@ RSpec.describe 'Tags' do expect(response.content_type) .to start_with('application/json') - expect(response.parsed_body[:id].to_i).to eq(tag.id) - expect(response.parsed_body[:name]).to eq(tag.name.upcase) + expect(response.parsed_body) + .to include( + id: tag.id.to_s, + name: tag.name.upcase + ) end context 'when the updated display name is invalid' do diff --git a/spec/requests/api/v1/apps/credentials_spec.rb b/spec/requests/api/v1/apps/credentials_spec.rb index 30200ed60c..8e5fa14b7e 100644 --- a/spec/requests/api/v1/apps/credentials_spec.rb +++ b/spec/requests/api/v1/apps/credentials_spec.rb @@ -41,8 +41,9 @@ RSpec.describe 'Credentials' do expect(response.content_type) .to start_with('application/json') - expect(response.parsed_body[:client_id]).to_not be_present - expect(response.parsed_body[:client_secret]).to_not be_present + expect(response.parsed_body) + .to not_include(client_id: be_present) + .and not_include(client_secret: be_present) end end From 0ba3ad4a354df6b7cdcc9fbad4dc50d86f429171 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 23 Sep 2024 04:45:05 -0400 Subject: [PATCH 54/93] Remove `body_json_ids` from api/v2/admin/accounts spec (#32003) --- spec/requests/api/v2/admin/accounts_spec.rb | 32 ++++++++++++++------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/spec/requests/api/v2/admin/accounts_spec.rb b/spec/requests/api/v2/admin/accounts_spec.rb index bc3db4f886..1d1fda3e67 100644 --- a/spec/requests/api/v2/admin/accounts_spec.rb +++ b/spec/requests/api/v2/admin/accounts_spec.rb @@ -36,7 +36,10 @@ RSpec.describe 'API V2 Admin Accounts' do expect(response).to have_http_status(200) expect(response.content_type) .to start_with('application/json') - expect(body_json_ids).to eq([admin_account.id]) + expect(response.parsed_body) + .to contain_exactly( + hash_including(id: admin_account.id.to_s) + ) end end @@ -47,8 +50,11 @@ RSpec.describe 'API V2 Admin Accounts' do expect(response).to have_http_status(200) expect(response.content_type) .to start_with('application/json') - expect(body_json_ids).to include(remote_account.id) - expect(body_json_ids).to_not include(other_remote_account.id) + expect(response.parsed_body) + .to contain_exactly( + hash_including(id: remote_account.id.to_s) + ) + .and not_include(hash_including(id: other_remote_account.id.to_s)) end end @@ -59,7 +65,11 @@ RSpec.describe 'API V2 Admin Accounts' do expect(response).to have_http_status(200) expect(response.content_type) .to start_with('application/json') - expect(body_json_ids).to include(suspended_remote.id, suspended_account.id) + expect(response.parsed_body) + .to contain_exactly( + hash_including(id: suspended_remote.id.to_s), + hash_including(id: suspended_account.id.to_s) + ) end end @@ -70,7 +80,10 @@ RSpec.describe 'API V2 Admin Accounts' do expect(response).to have_http_status(200) expect(response.content_type) .to start_with('application/json') - expect(body_json_ids).to include(disabled_account.id) + expect(response.parsed_body) + .to contain_exactly( + hash_including(id: disabled_account.id.to_s) + ) end end @@ -81,14 +94,13 @@ RSpec.describe 'API V2 Admin Accounts' do expect(response).to have_http_status(200) expect(response.content_type) .to start_with('application/json') - expect(body_json_ids).to include(pending_account.id) + expect(response.parsed_body) + .to contain_exactly( + hash_including(id: pending_account.id.to_s) + ) end end - def body_json_ids - response.parsed_body.map { |a| a[:id].to_i } - end - context 'with limit param' do let(:params) { { limit: 1 } } From ed90d9342ee61efa110996dc88f7352c3c9efe8d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 08:50:19 +0000 Subject: [PATCH 55/93] New Crowdin Translations (automated) (#32011) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/an.json | 2 - app/javascript/mastodon/locales/ar.json | 2 - app/javascript/mastodon/locales/be.json | 2 - app/javascript/mastodon/locales/bg.json | 2 - app/javascript/mastodon/locales/br.json | 2 - app/javascript/mastodon/locales/ca.json | 4 +- app/javascript/mastodon/locales/ckb.json | 2 - app/javascript/mastodon/locales/co.json | 2 - app/javascript/mastodon/locales/cs.json | 2 - app/javascript/mastodon/locales/cy.json | 2 - app/javascript/mastodon/locales/da.json | 4 +- app/javascript/mastodon/locales/de.json | 6 +- app/javascript/mastodon/locales/el.json | 2 - app/javascript/mastodon/locales/en-GB.json | 4 +- app/javascript/mastodon/locales/eo.json | 97 +++++++++++++++++++- app/javascript/mastodon/locales/es-AR.json | 4 +- app/javascript/mastodon/locales/es-MX.json | 4 +- app/javascript/mastodon/locales/es.json | 4 +- app/javascript/mastodon/locales/et.json | 2 - app/javascript/mastodon/locales/eu.json | 2 - app/javascript/mastodon/locales/fa.json | 2 - app/javascript/mastodon/locales/fi.json | 4 +- app/javascript/mastodon/locales/fo.json | 4 +- app/javascript/mastodon/locales/fr-CA.json | 2 - app/javascript/mastodon/locales/fr.json | 2 - app/javascript/mastodon/locales/fy.json | 2 - app/javascript/mastodon/locales/ga.json | 4 +- app/javascript/mastodon/locales/gd.json | 2 - app/javascript/mastodon/locales/gl.json | 4 +- app/javascript/mastodon/locales/he.json | 4 +- app/javascript/mastodon/locales/hi.json | 2 - app/javascript/mastodon/locales/hu.json | 4 +- app/javascript/mastodon/locales/hy.json | 2 - app/javascript/mastodon/locales/ia.json | 2 - app/javascript/mastodon/locales/id.json | 2 - app/javascript/mastodon/locales/ie.json | 2 - app/javascript/mastodon/locales/io.json | 2 - app/javascript/mastodon/locales/is.json | 2 - app/javascript/mastodon/locales/it.json | 4 +- app/javascript/mastodon/locales/ja.json | 6 +- app/javascript/mastodon/locales/kab.json | 8 +- app/javascript/mastodon/locales/ko.json | 4 +- app/javascript/mastodon/locales/ku.json | 2 - app/javascript/mastodon/locales/kw.json | 2 - app/javascript/mastodon/locales/lad.json | 6 +- app/javascript/mastodon/locales/lt.json | 4 +- app/javascript/mastodon/locales/lv.json | 2 - app/javascript/mastodon/locales/mr.json | 2 - app/javascript/mastodon/locales/ms.json | 2 - app/javascript/mastodon/locales/my.json | 2 - app/javascript/mastodon/locales/nl.json | 4 +- app/javascript/mastodon/locales/nn.json | 4 +- app/javascript/mastodon/locales/no.json | 2 - app/javascript/mastodon/locales/oc.json | 2 - app/javascript/mastodon/locales/pl.json | 4 +- app/javascript/mastodon/locales/pt-BR.json | 3 +- app/javascript/mastodon/locales/pt-PT.json | 2 - app/javascript/mastodon/locales/ro.json | 2 - app/javascript/mastodon/locales/ru.json | 2 - app/javascript/mastodon/locales/sa.json | 2 - app/javascript/mastodon/locales/sc.json | 2 - app/javascript/mastodon/locales/sco.json | 2 - app/javascript/mastodon/locales/sk.json | 2 - app/javascript/mastodon/locales/sl.json | 2 - app/javascript/mastodon/locales/sq.json | 2 - app/javascript/mastodon/locales/sr-Latn.json | 2 - app/javascript/mastodon/locales/sr.json | 2 - app/javascript/mastodon/locales/sv.json | 2 - app/javascript/mastodon/locales/th.json | 2 - app/javascript/mastodon/locales/tok.json | 2 - app/javascript/mastodon/locales/tr.json | 4 +- app/javascript/mastodon/locales/tt.json | 2 - app/javascript/mastodon/locales/uk.json | 4 +- app/javascript/mastodon/locales/vi.json | 80 ++++++++-------- app/javascript/mastodon/locales/zh-CN.json | 4 +- app/javascript/mastodon/locales/zh-HK.json | 2 - app/javascript/mastodon/locales/zh-TW.json | 4 +- config/locales/devise.kab.yml | 4 +- config/locales/doorkeeper.kab.yml | 12 +-- config/locales/es-MX.yml | 2 +- config/locales/ja.yml | 3 + config/locales/kab.yml | 8 +- config/locales/lad.yml | 19 ++++ config/locales/simple_form.kab.yml | 2 +- config/locales/simple_form.lad.yml | 1 + config/locales/simple_form.vi.yml | 12 +-- config/locales/vi.yml | 18 ++-- 87 files changed, 247 insertions(+), 224 deletions(-) diff --git a/app/javascript/mastodon/locales/an.json b/app/javascript/mastodon/locales/an.json index fcd3080421..be303985ee 100644 --- a/app/javascript/mastodon/locales/an.json +++ b/app/javascript/mastodon/locales/an.json @@ -288,8 +288,6 @@ "keyboard_shortcuts.unfocus": "Retirar lo foco d'a caixa de redacción/busqueda", "keyboard_shortcuts.up": "Ir enta alto en a lista", "lightbox.close": "Zarrar", - "lightbox.compress": "Comprimir quadro de visualización d'imachen", - "lightbox.expand": "Expandir quadro de visualización d'imachen", "lightbox.next": "Siguient", "lightbox.previous": "Anterior", "limited_account_hint.action": "Amostrar perfil de totz modos", diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index d50ca8dbb5..17b288723b 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -420,8 +420,6 @@ "keyboard_shortcuts.unfocus": "لإلغاء التركيز على حقل النص أو نافذة البحث", "keyboard_shortcuts.up": "للانتقال إلى أعلى القائمة", "lightbox.close": "إغلاق", - "lightbox.compress": "ضغط مربع عرض الصورة", - "lightbox.expand": "توسيع مربع عرض الصور", "lightbox.next": "التالي", "lightbox.previous": "العودة", "limited_account_hint.action": "إظهار الملف التعريفي على أي حال", diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json index 8c28e71d2c..098c7a7e9d 100644 --- a/app/javascript/mastodon/locales/be.json +++ b/app/javascript/mastodon/locales/be.json @@ -412,8 +412,6 @@ "keyboard_shortcuts.unfocus": "Расфакусаваць тэкставую вобласць/пошукавы радок", "keyboard_shortcuts.up": "Перамясціцца ўверх па спісе", "lightbox.close": "Закрыць", - "lightbox.compress": "Сціснуць бачную вобласць выявы", - "lightbox.expand": "Павялічыць бачную вобласць выявы", "lightbox.next": "Далей", "lightbox.previous": "Назад", "limited_account_hint.action": "Усе роўна паказваць профіль", diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index 507b8d2937..c8f8474e91 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -419,8 +419,6 @@ "keyboard_shortcuts.unfocus": "Разфокусиране на текстовото поле за съставяне/търсене", "keyboard_shortcuts.up": "Преместване нагоре в списъка", "lightbox.close": "Затваряне", - "lightbox.compress": "Свиване на полето за преглед на образи", - "lightbox.expand": "Разгъване на полето за преглед на образи", "lightbox.next": "Напред", "lightbox.previous": "Назад", "limited_account_hint.action": "Показване на профила въпреки това", diff --git a/app/javascript/mastodon/locales/br.json b/app/javascript/mastodon/locales/br.json index 18616fcef2..79949ed91f 100644 --- a/app/javascript/mastodon/locales/br.json +++ b/app/javascript/mastodon/locales/br.json @@ -338,8 +338,6 @@ "keyboard_shortcuts.unfocus": "Difokus an dachenn testenn/klask", "keyboard_shortcuts.up": "Pignat er roll", "lightbox.close": "Serriñ", - "lightbox.compress": "Bihanaat boest hewel ar skeudenn", - "lightbox.expand": "Ledanaat boest hewel ar skeudenn", "lightbox.next": "Da-heul", "lightbox.previous": "A-raok", "limited_account_hint.action": "Diskouez an aelad memes tra", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 3cdeab3ae4..710ac594f5 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "Descentra l'àrea de composició de text/cerca", "keyboard_shortcuts.up": "Apuja a la llista", "lightbox.close": "Tanca", - "lightbox.compress": "Comprimeix el quadre de visualització d’imatge", - "lightbox.expand": "Amplia el quadre de visualització d’imatge", "lightbox.next": "Següent", "lightbox.previous": "Anterior", + "lightbox.zoom_in": "Amplia fins a la mida real", + "lightbox.zoom_out": "Amplia fins a encabir", "limited_account_hint.action": "Mostra el perfil de totes maneres", "limited_account_hint.title": "Aquest perfil l'han amagat els moderadors de {domain}.", "link_preview.author": "Per {name}", diff --git a/app/javascript/mastodon/locales/ckb.json b/app/javascript/mastodon/locales/ckb.json index 2a3a391eab..bea6e5ceec 100644 --- a/app/javascript/mastodon/locales/ckb.json +++ b/app/javascript/mastodon/locales/ckb.json @@ -335,8 +335,6 @@ "keyboard_shortcuts.unfocus": "بۆ دروستکردنی ناوچەی دەق/گەڕان", "keyboard_shortcuts.up": "بۆ ئەوەی لە لیستەکەدا بڕۆیت", "lightbox.close": "دابخە", - "lightbox.compress": "سندوقی نیشاندانی وێنە بپەستێنە", - "lightbox.expand": "فراوانکردنی سندوقی بینینی وێنە", "lightbox.next": "داهاتوو", "lightbox.previous": "پێشوو", "limited_account_hint.action": "بەهەر حاڵ پڕۆفایلی پیشان بدە", diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json index 37eb945615..043061769b 100644 --- a/app/javascript/mastodon/locales/co.json +++ b/app/javascript/mastodon/locales/co.json @@ -196,8 +196,6 @@ "keyboard_shortcuts.unfocus": "ùn fucalizà più l'area di testu", "keyboard_shortcuts.up": "cullà indè a lista", "lightbox.close": "Chjudà", - "lightbox.compress": "Cumprime a finestra d'affissera di i ritratti", - "lightbox.expand": "Ingrandà a finestra d'affissera di i ritratti", "lightbox.next": "Siguente", "lightbox.previous": "Pricidente", "lists.account.add": "Aghjunghje à a lista", diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json index af7a012813..b70ff3ee2b 100644 --- a/app/javascript/mastodon/locales/cs.json +++ b/app/javascript/mastodon/locales/cs.json @@ -410,8 +410,6 @@ "keyboard_shortcuts.unfocus": "Zrušit zaměření na nový příspěvek/hledání", "keyboard_shortcuts.up": "Posunout v seznamu nahoru", "lightbox.close": "Zavřít", - "lightbox.compress": "Sbalit pole zobrazení obrázku", - "lightbox.expand": "Rozbalit pole zobrazení obrázku", "lightbox.next": "Další", "lightbox.previous": "Předchozí", "limited_account_hint.action": "Přesto profil zobrazit", diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json index 51ea1bdf3a..ec93a5b11b 100644 --- a/app/javascript/mastodon/locales/cy.json +++ b/app/javascript/mastodon/locales/cy.json @@ -432,8 +432,6 @@ "keyboard_shortcuts.unfocus": "Dad-ffocysu ardal cyfansoddi testun/chwilio", "keyboard_shortcuts.up": "Symud yn uwch yn y rhestr", "lightbox.close": "Cau", - "lightbox.compress": "Cywasgu blwch gweld delwedd", - "lightbox.expand": "Ehangu blwch gweld delwedd", "lightbox.next": "Nesaf", "lightbox.previous": "Blaenorol", "limited_account_hint.action": "Dangos y proffil beth bynnag", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index 2c36869622..76752802b0 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "Fjern fokus fra tekstskrivningsområde/søgning", "keyboard_shortcuts.up": "Flyt opad på listen", "lightbox.close": "Luk", - "lightbox.compress": "Komprimér billedvisningsfelt", - "lightbox.expand": "Udvid billedvisningsfelt", "lightbox.next": "Næste", "lightbox.previous": "Forrige", + "lightbox.zoom_in": "Zoom til faktisk størrelse", + "lightbox.zoom_out": "Zoom for at tilpasse", "limited_account_hint.action": "Vis profil alligevel", "limited_account_hint.title": "Denne profil er blevet skjult af {domain}-moderatorerne.", "link_preview.author": "Af {name}", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index e6c086bfa2..1931ce23f1 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -155,7 +155,7 @@ "compose_form.lock_disclaimer.lock": "geschützt", "compose_form.placeholder": "Was gibt’s Neues?", "compose_form.poll.duration": "Umfragedauer", - "compose_form.poll.multiple": "Mul­ti­ple-Choice", + "compose_form.poll.multiple": "Mehrfachauswahl", "compose_form.poll.option_placeholder": "Option {number}", "compose_form.poll.single": "Einfachauswahl", "compose_form.poll.switch_to_multiple": "Mehrfachauswahl erlauben", @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "Eingabefeld/Suche nicht mehr fokussieren", "keyboard_shortcuts.up": "Ansicht nach oben bewegen", "lightbox.close": "Schließen", - "lightbox.compress": "Bildansicht verkleinern", - "lightbox.expand": "Bildansicht vergrößern", "lightbox.next": "Vor", "lightbox.previous": "Zurück", + "lightbox.zoom_in": "In Originalgröße anzeigen", + "lightbox.zoom_out": "In angepasster Größe anzeigen", "limited_account_hint.action": "Profil trotzdem anzeigen", "limited_account_hint.title": "Dieses Profil wurde von den Moderator*innen von {domain} ausgeblendet.", "link_preview.author": "Von {name}", diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index 367efe1cd9..ac6a2cf4e8 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -427,8 +427,6 @@ "keyboard_shortcuts.unfocus": "Αποεστίαση του πεδίου σύνθεσης/αναζήτησης", "keyboard_shortcuts.up": "Μετακίνηση προς τα πάνω στη λίστα", "lightbox.close": "Κλείσιμο", - "lightbox.compress": "Συμπίεση πλαισίου προβολής εικόνας", - "lightbox.expand": "Ανάπτυξη πλαισίου εμφάνισης εικόνας", "lightbox.next": "Επόμενο", "lightbox.previous": "Προηγούμενο", "limited_account_hint.action": "Εμφάνιση προφίλ ούτως ή άλλως", diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json index c81f389b9d..c0335dd6b2 100644 --- a/app/javascript/mastodon/locales/en-GB.json +++ b/app/javascript/mastodon/locales/en-GB.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "Move up in the list", "lightbox.close": "Close", - "lightbox.compress": "Compress image view box", - "lightbox.expand": "Expand image view box", "lightbox.next": "Next", "lightbox.previous": "Previous", + "lightbox.zoom_in": "Zoom to actual size", + "lightbox.zoom_out": "Zoom to fit", "limited_account_hint.action": "Show profile anyway", "limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.", "link_preview.author": "By {name}", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index e162e732d0..05f53fb731 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -215,8 +215,25 @@ "dismissable_banner.explore_statuses": "Ĉi tioj estas afiŝoj de socia reto kiu populariĝas hodiau.", "dismissable_banner.explore_tags": "Ĉi tiuj kradvostoj populariĝas en ĉi tiu kaj aliaj serviloj en la malcentraliza reto nun.", "dismissable_banner.public_timeline": "Ĉi tioj estas plej lastaj publikaj afiŝoj de personoj ĉe socia reto kiu personoj ĉe {domain} sekvas.", + "domain_block_modal.block": "Bloki servilon", + "domain_block_modal.block_account_instead": "Bloki @{name} anstataŭe", + "domain_block_modal.they_can_interact_with_old_posts": "Homoj de ĉi tiu servilo povas interagi kun viaj malnovaj afiŝoj.", "domain_block_modal.they_cant_follow": "Neniu el ĉi tiu servilo povas sekvi vin.", + "domain_block_modal.they_wont_know": "Ili ne scios, ke ili estas blokitaj.", + "domain_block_modal.title": "Ĉu bloki la domajnon?", + "domain_block_modal.you_will_lose_followers": "Ĉiuj viaj sekvantoj de ĉi tiu servilo estos forigitaj.", + "domain_block_modal.you_wont_see_posts": "Vi ne vidos afiŝojn aŭ sciigojn de uzantoj sur ĉi tiu servilo.", + "domain_pill.activitypub_lets_connect": "Ĝi ebligas vin konekti kaj interagi kun homoj ne nur sur Mastodon, sed ankaŭ tra diversaj sociaj apoj.", + "domain_pill.activitypub_like_language": "ActivityPub estas kiel la lingvo kiun Mastodon parolas kun aliaj sociaj retoj.", + "domain_pill.server": "Servilo", + "domain_pill.their_handle": "Ilia identigo:", + "domain_pill.their_server": "Ilia cifereca hejmo, kie ĉiuj iliaj afiŝoj loĝas.", + "domain_pill.their_username": "Ilia unika identigilo sur ilia servilo. Eblas trovi uzantojn kun la sama uzantnomo sur malsamaj serviloj.", "domain_pill.username": "Uzantnomo", + "domain_pill.whats_in_a_handle": "Kio estas en identigo?", + "domain_pill.your_handle": "Via identigo:", + "domain_pill.your_server": "Via cifereca hejmo, kie loĝas ĉiuj viaj afiŝoj. Ĉu vi ne ŝatas ĉi tiun? Transloku servilojn iam ajn kaj alportu ankaŭ viajn sekvantojn.", + "domain_pill.your_username": "Via unika identigilo sur ĉi tiu servilo. Eblas trovi uzantojn kun la sama uzantnomo sur malsamaj serviloj.", "embed.instructions": "Enkorpigu ĉi tiun afiŝon en vian retejon per kopio de la suba kodo.", "embed.preview": "Ĝi aperos tiel:", "emoji_button.activity": "Agadoj", @@ -253,6 +270,7 @@ "empty_column.list": "Ankoraŭ estas nenio en ĉi tiu listo. Kiam membroj de ĉi tiu listo afiŝos novajn afiŝojn, ili aperos ĉi tie.", "empty_column.lists": "Vi ankoraŭ ne havas liston. Kiam vi kreos iun, ĝi aperos ĉi tie.", "empty_column.mutes": "Vi ne ankoraŭ silentigis iun uzanton.", + "empty_column.notification_requests": "Ĉio klara! Estas nenio tie ĉi. Kiam vi ricevas novajn sciigojn, ili aperos ĉi tie laŭ viaj agordoj.", "empty_column.notifications": "Vi ankoraŭ ne havas sciigojn. Interagu kun aliaj por komenci konversacion.", "empty_column.public": "Estas nenio ĉi tie! Publike skribu ion, aŭ mane sekvu uzantojn de aliaj serviloj por plenigi la publikan tempolinion", "error.unexpected_crash.explanation": "Pro eraro en nia kodo, aŭ problemo de kongruo en via retumilo, ĉi tiu paĝo ne povis esti montrata ĝuste.", @@ -283,6 +301,8 @@ "filter_modal.select_filter.subtitle": "Uzu ekzistantan kategorion aŭ kreu novan", "filter_modal.select_filter.title": "Filtri ĉi tiun afiŝon", "filter_modal.title.status": "Filtri mesaĝon", + "filtered_notifications_banner.pending_requests": "El {count, plural, =0 {neniu} one {unu persono} other {# homoj}} vi eble konas", + "filtered_notifications_banner.title": "Filtritaj sciigoj", "firehose.all": "Ĉiuj", "firehose.local": "Ĉi tiu servilo", "firehose.remote": "Aliaj serviloj", @@ -290,9 +310,13 @@ "follow_request.reject": "Rifuzi", "follow_requests.unlocked_explanation": "Kvankam via konto ne estas ŝlosita, la dungitaro de {domain} opinias, ke vi eble volas revizii petojn pri sekvado de ĉi tiuj kontoj permane.", "follow_suggestions.dismiss": "Ne montri denove", + "follow_suggestions.friends_of_friends_longer": "Populara inter homoj, kiujn vi sekvas", "follow_suggestions.hints.friends_of_friends": "Ĉi tiu profilo estas populara inter la homoj, kiujn vi sekvas.", "follow_suggestions.hints.most_followed": "Ĉi tiu profilo estas unu el la plej sekvataj en {domain}.", + "follow_suggestions.hints.similar_to_recently_followed": "Ĉi tiu profilo similas al la profiloj kiujn vi plej lastatempe sekvis.", + "follow_suggestions.popular_suggestion": "Popularaj proponoj", "follow_suggestions.popular_suggestion_longer": "Populara en {domain}", + "follow_suggestions.similar_to_recently_followed_longer": "Simile al profiloj, kiujn vi lastatempe sekvis", "follow_suggestions.view_all": "Vidi ĉiujn", "follow_suggestions.who_to_follow": "Kiun sekvi", "followed_tags": "Sekvataj kradvortoj", @@ -321,6 +345,11 @@ "hashtag.follow": "Sekvi la kradvorton", "hashtag.unfollow": "Ne plu sekvi la kradvorton", "hashtags.and_other": "…kaj {count, plural,other {# pli}}", + "hints.profiles.followers_may_be_missing": "Sekvantoj por ĉi tiu profilo eble mankas.", + "hints.profiles.posts_may_be_missing": "Iuj afiŝoj de ĉi tiu profilo eble mankas.", + "hints.profiles.see_more_followers": "Vidi pli da sekvantoj sur {domain}", + "hints.profiles.see_more_posts": "Vidi pli da afiŝoj sur {domain}", + "hints.threads.see_more": "Vidi pli da respondoj sur {domain}", "home.column_settings.show_reblogs": "Montri diskonigojn", "home.column_settings.show_replies": "Montri respondojn", "home.hide_announcements": "Kaŝi la anoncojn", @@ -328,6 +357,13 @@ "home.pending_critical_update.link": "Vidi ĝisdatigojn", "home.pending_critical_update.title": "Kritika sekurĝisdatigo estas disponebla!", "home.show_announcements": "Montri anoncojn", + "ignore_notifications_modal.disclaimer": "Mastodon ne povas informi uzantojn, ke vi ignoris iliajn sciigojn. Ignori sciigojn ne malhelpos la mesaĝojn mem esti senditaj.", + "ignore_notifications_modal.ignore": "Ignori sciigojn", + "ignore_notifications_modal.limited_accounts_title": "Ĉu ignori sciigojn de moderigitaj kontoj?", + "ignore_notifications_modal.new_accounts_title": "Ĉu ignori sciigojn de novaj kontoj?", + "ignore_notifications_modal.not_followers_title": "Ĉu ignori sciigojn de homoj, kiuj ne sekvas vin?", + "ignore_notifications_modal.not_following_title": "Ĉu ignori sciigojn de homoj, kiujn vi ne sekvas?", + "ignore_notifications_modal.private_mentions_title": "Ĉu ignori sciigojn de nepetitaj privataj mencioj?", "interaction_modal.description.favourite": "Per konto ĉe Mastodon, vi povas stelumiti ĉi tiun afiŝon por sciigi la afiŝanton ke vi aprezigas ŝin kaj konservas por la estonteco.", "interaction_modal.description.follow": "Kun konto ĉe Mastodon, vi povos sekvi {name} por vidi ties mesaĝojn en via hejmo.", "interaction_modal.description.reblog": "Kun konto ĉe Mastodon, vi povas diskonigi ĉi tiun afiŝon, por ke viaj propraj sekvantoj vidu ĝin.", @@ -381,13 +417,14 @@ "keyboard_shortcuts.unfocus": "malenfokusigi la tekstujon aŭ la serĉilon", "keyboard_shortcuts.up": "iri supren en la listo", "lightbox.close": "Fermi", - "lightbox.compress": "Kunpremi bildan vidkeston", - "lightbox.expand": "Pligrandigi bildan vidkeston", "lightbox.next": "Antaŭen", "lightbox.previous": "Malantaŭen", + "lightbox.zoom_in": "Zomi al reala grandeco", + "lightbox.zoom_out": "Zomi por konveni", "limited_account_hint.action": "Montru profilon ĉiukaze", "limited_account_hint.title": "La profilo estas kaŝita de la moderigantoj de {domain}.", "link_preview.author": "De {name}", + "link_preview.more_from_author": "Pli de {name}", "link_preview.shares": "{count, plural, one {{counter} afiŝo} other {{counter} afiŝoj}}", "lists.account.add": "Aldoni al la listo", "lists.account.remove": "Forigi de la listo", @@ -407,8 +444,15 @@ "loading_indicator.label": "Ŝargado…", "media_gallery.hide": "Kaŝi", "moved_to_account_banner.text": "Via konto {disabledAccount} estas malvalidigita ĉar vi movis ĝin al {movedToAccount}.", + "mute_modal.hide_from_notifications": "Kaŝi de sciigoj", + "mute_modal.hide_options": "Kaŝi agordojn", + "mute_modal.indefinite": "Ĝis mi malsilentas ilin", "mute_modal.show_options": "Montri agordojn", "mute_modal.they_can_mention_and_follow": "Ili povas mencii kaj sekvi vin, sed vi ne vidos ilin.", + "mute_modal.they_wont_know": "Ili ne scios, ke ili estas silentigitaj.", + "mute_modal.title": "Ĉu silentigi uzanton?", + "mute_modal.you_wont_see_mentions": "Vi ne vidos afiŝojn, kiuj mencias ilin.", + "mute_modal.you_wont_see_posts": "Ili ankoraŭ povas vidi viajn afiŝojn, sed vi ne vidos iliajn.", "navigation_bar.about": "Pri", "navigation_bar.administration": "Administrado", "navigation_bar.advanced_interface": "Malfermi altnivelan retpaĝan interfacon", @@ -437,28 +481,58 @@ "navigation_bar.security": "Sekureco", "not_signed_in_indicator.not_signed_in": "Necesas saluti por aliri tiun rimedon.", "notification.admin.report": "{name} raportis {target}", + "notification.admin.report_account": "{name} raportis {count, plural, one {afiŝon} other {# afiŝojn}} de {target} por {category}", + "notification.admin.report_account_other": "{name} raportis {count, plural, one {afiŝon} other {# afiŝojn}} de {target}", + "notification.admin.report_statuses": "{name} raportis {target} por {category}", + "notification.admin.report_statuses_other": "{name} raportis {target}", "notification.admin.sign_up": "{name} kreis konton", "notification.favourite": "{name} stelumis vian afiŝon", "notification.follow": "{name} eksekvis vin", + "notification.follow.name_and_others": "{name} kaj {count, plural, one {# alia} other {# aliaj}} sekvis vin", "notification.follow_request": "{name} petis sekvi vin", + "notification.follow_request.name_and_others": "{name} kaj {count, plural, one {# alia} other {# aliaj}} petis sekvi vin", "notification.label.mention": "Mencii", "notification.label.private_mention": "Privata mencio", "notification.label.private_reply": "Privata respondo", "notification.label.reply": "Respondi", "notification.mention": "Mencii", "notification.moderation-warning.learn_more": "Lerni pli", + "notification.moderation_warning": "Vi ricevis moderigan averton", + "notification.moderation_warning.action_delete_statuses": "Kelkaj el viaj afiŝoj estis forigitaj.", + "notification.moderation_warning.action_disable": "Via konto estas malŝaltita.", + "notification.moderation_warning.action_mark_statuses_as_sensitive": "Kelkaj el viaj afiŝoj estis markitaj kiel sentemaj.", + "notification.moderation_warning.action_none": "Via konto ricevis moderigan averton.", + "notification.moderation_warning.action_sensitive": "Viaj afiŝoj estos markitaj kiel sentemaj ekde nun.", + "notification.moderation_warning.action_silence": "Via konto estis limigita.", + "notification.moderation_warning.action_suspend": "Via konto estas malakceptita.", "notification.own_poll": "Via enketo finiĝis", "notification.reblog": "{name} diskonigis vian afiŝon", + "notification.reblog.name_and_others_with_link": "{name} kaj {count, plural, one {# alia} other {# aliaj}} diskonigis vian afiŝon", "notification.relationships_severance_event.learn_more": "Lerni pli", "notification.status": "{name} ĵus afiŝis", "notification.update": "{name} redaktis afiŝon", "notification_requests.accept": "Akcepti", + "notification_requests.accept_multiple": "{count, plural, one {Akcepti # peton…} other {Akcepti # petojn…}}", + "notification_requests.confirm_accept_multiple.button": "{count, plural, one {Akcepti peton} other {Akcepti petojn}}", + "notification_requests.confirm_accept_multiple.title": "Ĉu akcepti sciigajn petojn?", + "notification_requests.confirm_dismiss_multiple.button": "{count, plural, one {Malakcepti peton} other {Malakcepti petojn}}", + "notification_requests.confirm_dismiss_multiple.title": "Ĉu malakcepti sciigajn petojn?", + "notification_requests.dismiss": "Forĵeti", + "notification_requests.edit_selection": "Redakti", + "notification_requests.exit_selection": "Farita", + "notification_requests.explainer_for_limited_account": "Sciigoj de ĉi tiu konto estis filtritaj ĉar la konto estis limigita de moderanto.", + "notification_requests.explainer_for_limited_remote_account": "Sciigoj de ĉi tiu konto estis filtritaj ĉar la konto aŭ ĝia servilo estis limigitaj de moderanto.", + "notification_requests.notifications_from": "Sciigoj de {name}", + "notification_requests.title": "Filtritaj sciigoj", + "notification_requests.view": "Vidi sciigojn", "notifications.clear": "Forviŝi sciigojn", "notifications.clear_confirmation": "Ĉu vi certas, ke vi volas porĉiame forviŝi ĉiujn viajn sciigojn?", + "notifications.clear_title": "Ĉu forigi sciigojn?", "notifications.column_settings.admin.report": "Novaj raportoj:", "notifications.column_settings.admin.sign_up": "Novaj registriĝoj:", "notifications.column_settings.alert": "Sciigoj de la retumilo", "notifications.column_settings.favourite": "Stelumoj:", + "notifications.column_settings.filter_bar.advanced": "Montri ĉiujn kategoriojn", "notifications.column_settings.follow": "Novaj sekvantoj:", "notifications.column_settings.follow_request": "Novaj petoj de sekvado:", "notifications.column_settings.mention": "Mencioj:", @@ -485,7 +559,18 @@ "notifications.permission_denied_alert": "Labortablaj sciigoj ne povas esti ebligitaj, ĉar retumilpermeso antaŭe estis rifuzita", "notifications.permission_required": "Labortablaj sciigoj ne disponeblas ĉar la bezonata permeso ne estis donita.", "notifications.policy.accept": "Akcepti", + "notifications.policy.accept_hint": "Montri en sciigoj", + "notifications.policy.drop": "Ignori", + "notifications.policy.drop_hint": "Sendi al la malpleno, por neniam esti vidita denove", + "notifications.policy.filter": "Filtri", + "notifications.policy.filter_limited_accounts_title": "Moderigitaj kontoj", + "notifications.policy.filter_new_accounts.hint": "Kreite en la {days, plural, one {lasta tago} other {# lastaj tagoj}}", "notifications.policy.filter_new_accounts_title": "Novaj kontoj", + "notifications.policy.filter_not_followers_title": "Homoj, kiuj ne sekvas vin", + "notifications.policy.filter_not_following_hint": "Ĝis vi permane aprobas ilin", + "notifications.policy.filter_not_following_title": "Homoj, kiujn vi ne sekvas", + "notifications.policy.filter_private_mentions_title": "Nepetitaj privataj mencioj", + "notifications.policy.title": "Administri sciigojn de…", "notifications_permission_banner.enable": "Ŝalti retumilajn sciigojn", "notifications_permission_banner.how_to_control": "Por ricevi sciigojn kiam Mastodon ne estas malfermita, ebligu labortablajn sciigojn. Vi povas regi precize kiuj specoj de interagoj generas labortablajn sciigojn per la supra butono {icon} post kiam ili estas ebligitaj.", "notifications_permission_banner.title": "Neniam preterlasas iun ajn", @@ -609,10 +694,13 @@ "report.unfollow_explanation": "Vi sekvas ĉi tiun konton. Por ne plu vidi ĝiajn afiŝojn en via hejma templinio, ĉesu sekvi ĝin.", "report_notification.attached_statuses": "{count, plural, one {{count} afiŝo almetita} other {{count} afiŝoj almetitaj}}", "report_notification.categories.legal": "Laŭleĝa", + "report_notification.categories.legal_sentence": "kontraŭleĝa enhavo", "report_notification.categories.other": "Alia", "report_notification.categories.other_sentence": "alia", "report_notification.categories.spam": "Trudmesaĝo", + "report_notification.categories.spam_sentence": "trudmesaĝo", "report_notification.categories.violation": "Malobservo de la regulo", + "report_notification.categories.violation_sentence": "malobservo de la regulo", "report_notification.open": "Malfermi la raporton", "search.no_recent_searches": "Neniuj lastaj serĉoj", "search.placeholder": "Serĉi", @@ -640,8 +728,10 @@ "server_banner.about_active_users": "Personoj uzantaj ĉi tiun servilon dum la lastaj 30 tagoj (Aktivaj Uzantoj Monate)", "server_banner.active_users": "aktivaj uzantoj", "server_banner.administered_by": "Administrata de:", + "server_banner.is_one_of_many": "{domain} estas unu el la multaj sendependaj Mastodon-serviloj, kiujn vi povas uzi por partopreni en la fediverso.", "server_banner.server_stats": "Statistikoj de la servilo:", "sign_in_banner.create_account": "Krei konton", + "sign_in_banner.follow_anyone": "Sekvi iun ajn tra la fediverso kaj vidi ĉion en kronologia ordo. Neniuj algoritmoj, reklamoj aŭ klakbetoj videblas.", "sign_in_banner.sign_in": "Saluti", "sign_in_banner.sso_redirect": "Ensalutu aŭ Registriĝi", "status.admin_account": "Malfermi fasadon de moderigado por @{name}", @@ -657,8 +747,10 @@ "status.direct": "Private mencii @{name}", "status.direct_indicator": "Privata mencio", "status.edit": "Redakti", + "status.edited": "Laste redaktita {date}", "status.edited_x_times": "Redactita {count, plural, one {{count} fojon} other {{count} fojojn}}", "status.favourite": "Ŝatata", + "status.favourites": "{count, plural, one {plej ŝatata} other {plej ŝatataj}}", "status.filter": "Filtri ĉi tiun afiŝon", "status.history.created": "{name} kreis {date}", "status.history.edited": "{name} redaktis {date}", @@ -677,6 +769,7 @@ "status.reblog": "Diskonigi", "status.reblog_private": "Diskonigi kun la sama videbleco", "status.reblogged_by": "{name} diskonigis", + "status.reblogs": "{count, plural, one {diskonigo} other {diskonigoj}}", "status.reblogs.empty": "Ankoraŭ neniu diskonigis tiun afiŝon. Kiam iu faras tion, ri aperos ĉi tie.", "status.redraft": "Forigi kaj reskribi", "status.remove_bookmark": "Forigi legosignon", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index 968b01babc..6ab3f0fdb9 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "Quitar el foco del área de texto de redacción o de búsqueda", "keyboard_shortcuts.up": "Subir en la lista", "lightbox.close": "Cerrar", - "lightbox.compress": "Comprimir cuadro de vista de imagen", - "lightbox.expand": "Expandir cuadro de vista de imagen", "lightbox.next": "Siguiente", "lightbox.previous": "Anterior", + "lightbox.zoom_in": "Ampliar al tamaño real", + "lightbox.zoom_out": "Ampliar para ajustar", "limited_account_hint.action": "Mostrar perfil de todos modos", "limited_account_hint.title": "Este perfil fue ocultado por los moderadores de {domain}.", "link_preview.author": "Por {name}", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index 5c8f44d4a7..52ebf0ae18 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "para retirar el foco de la caja de redacción/búsqueda", "keyboard_shortcuts.up": "para ir hacia arriba en la lista", "lightbox.close": "Cerrar", - "lightbox.compress": "Comprimir cuadro de visualización de imagen", - "lightbox.expand": "Expandir cuadro de visualización de imagen", "lightbox.next": "Siguiente", "lightbox.previous": "Anterior", + "lightbox.zoom_in": "Ampliar al tamaño real", + "lightbox.zoom_out": "Ampliar para ajustar", "limited_account_hint.action": "Mostrar perfil de todos modos", "limited_account_hint.title": "Este perfil ha sido ocultado por los moderadores de {domain}.", "link_preview.author": "Por {name}", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 6a428e353b..df05e82b0b 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "para retirar el foco de la caja de redacción/búsqueda", "keyboard_shortcuts.up": "para ir hacia arriba en la lista", "lightbox.close": "Cerrar", - "lightbox.compress": "Comprimir cuadro de visualización de imagen", - "lightbox.expand": "Expandir cuadro de visualización de imagen", "lightbox.next": "Siguiente", "lightbox.previous": "Anterior", + "lightbox.zoom_in": "Ampliar al tamaño real", + "lightbox.zoom_out": "Ampliar para ajustar", "limited_account_hint.action": "Mostrar perfil de todos modos", "limited_account_hint.title": "Este perfil ha sido ocultado por los moderadores de {domain}.", "link_preview.author": "Por {name}", diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json index 5b8e8369f6..287ffb9852 100644 --- a/app/javascript/mastodon/locales/et.json +++ b/app/javascript/mastodon/locales/et.json @@ -432,8 +432,6 @@ "keyboard_shortcuts.unfocus": "Fookus tekstialalt/otsingult ära", "keyboard_shortcuts.up": "Liigu loetelus üles", "lightbox.close": "Sulge", - "lightbox.compress": "Suru kokku pildi vaatamise kast", - "lightbox.expand": "Laienda pildi vaatamise kast", "lightbox.next": "Järgmine", "lightbox.previous": "Eelmine", "limited_account_hint.action": "Näita profilli sellegipoolest", diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json index acdd27ce2b..b3137bc21a 100644 --- a/app/javascript/mastodon/locales/eu.json +++ b/app/javascript/mastodon/locales/eu.json @@ -432,8 +432,6 @@ "keyboard_shortcuts.unfocus": "testua konposatzeko area / bilaketatik fokua kentzea", "keyboard_shortcuts.up": "zerrendan gora mugitzea", "lightbox.close": "Itxi", - "lightbox.compress": "Konprimatu irudia ikusteko kaxa", - "lightbox.expand": "Zabaldu irudia ikusteko kaxa", "lightbox.next": "Hurrengoa", "lightbox.previous": "Aurrekoa", "limited_account_hint.action": "Erakutsi profila hala ere", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index fba8d90235..fbc65ec207 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -422,8 +422,6 @@ "keyboard_shortcuts.unfocus": "برداشتن تمرکز از ناحیهٔ نوشتن یا جست‌وجو", "keyboard_shortcuts.up": "بالا بردن در سیاهه", "lightbox.close": "بستن", - "lightbox.compress": "فشرده‌سازی جعبهٔ نمایش تصویر", - "lightbox.expand": "گسترش جعبهٔ نمایش تصویر", "lightbox.next": "بعدی", "lightbox.previous": "قبلی", "limited_account_hint.action": "به هر روی نمایه نشان داده شود", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 681f5d5bb2..d4adef40f2 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "Poistu kirjoitus- tai hakukentästä", "keyboard_shortcuts.up": "Siirry luettelossa taaksepäin", "lightbox.close": "Sulje", - "lightbox.compress": "Tiivis kuvankatselunäkymä", - "lightbox.expand": "Laajennettu kuvankatselunäkymä", "lightbox.next": "Seuraava", "lightbox.previous": "Edellinen", + "lightbox.zoom_in": "Zoomaa todelliseen kokoon", + "lightbox.zoom_out": "Zoomaa mahtumaan", "limited_account_hint.action": "Näytä profiili joka tapauksessa", "limited_account_hint.title": "Palvelimen {domain} moderaattorit ovat piilottaneet tämän profiilin.", "link_preview.author": "Tehnyt {name}", diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json index 793a75bb05..9b87f5c8d9 100644 --- a/app/javascript/mastodon/locales/fo.json +++ b/app/javascript/mastodon/locales/fo.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "Tak skrivi-/leiti-økið úr miðdeplinum", "keyboard_shortcuts.up": "Flyt upp á listanum", "lightbox.close": "Lat aftur", - "lightbox.compress": "Kroyst myndavísikassa saman", - "lightbox.expand": "Víðka myndavísikassa", "lightbox.next": "Fram", "lightbox.previous": "Aftur", + "lightbox.zoom_in": "Suma til veruliga stødd", + "lightbox.zoom_out": "Suma, so tað passar", "limited_account_hint.action": "Vís vangamynd kortini", "limited_account_hint.title": "Hesin vangin er fjaldur av kjakleiðarunum á {domain}.", "link_preview.author": "Av {name}", diff --git a/app/javascript/mastodon/locales/fr-CA.json b/app/javascript/mastodon/locales/fr-CA.json index 8e33c68442..27e776024f 100644 --- a/app/javascript/mastodon/locales/fr-CA.json +++ b/app/javascript/mastodon/locales/fr-CA.json @@ -432,8 +432,6 @@ "keyboard_shortcuts.unfocus": "Ne plus se concentrer sur la zone de rédaction/barre de recherche", "keyboard_shortcuts.up": "Monter dans la liste", "lightbox.close": "Fermer", - "lightbox.compress": "Compresser la fenêtre de visualisation d'images", - "lightbox.expand": "Agrandir la fenêtre de visualisation d'images", "lightbox.next": "Suivant", "lightbox.previous": "Précédent", "limited_account_hint.action": "Afficher le profil quand même", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index e73ddf734f..f787216c46 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -432,8 +432,6 @@ "keyboard_shortcuts.unfocus": "Quitter la zone de rédaction/barre de recherche", "keyboard_shortcuts.up": "Monter dans la liste", "lightbox.close": "Fermer", - "lightbox.compress": "Compresser la fenêtre de visualisation des images", - "lightbox.expand": "Agrandir la fenêtre de visualisation des images", "lightbox.next": "Suivant", "lightbox.previous": "Précédent", "limited_account_hint.action": "Afficher le profil quand même", diff --git a/app/javascript/mastodon/locales/fy.json b/app/javascript/mastodon/locales/fy.json index 2443692133..e7723c9533 100644 --- a/app/javascript/mastodon/locales/fy.json +++ b/app/javascript/mastodon/locales/fy.json @@ -432,8 +432,6 @@ "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "Nei boppe yn list ferpleatse", "lightbox.close": "Slute", - "lightbox.compress": "Ofbylding passend werjaan", - "lightbox.expand": "Ofbylding grut werjaan", "lightbox.next": "Folgjende", "lightbox.previous": "Foarige", "limited_account_hint.action": "Profyl dochs besjen", diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json index 11da6b64e9..e05adc2732 100644 --- a/app/javascript/mastodon/locales/ga.json +++ b/app/javascript/mastodon/locales/ga.json @@ -432,10 +432,10 @@ "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á", - "lightbox.expand": "Leathnaigh an bosca amhairc íomhá", "lightbox.next": "An céad eile", "lightbox.previous": "Roimhe seo", + "lightbox.zoom_in": "Súmáil chuig an méid iarbhír", + "lightbox.zoom_out": "Súmáil a d'oirfeadh", "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}", diff --git a/app/javascript/mastodon/locales/gd.json b/app/javascript/mastodon/locales/gd.json index 2b93662694..6cd3cbcc56 100644 --- a/app/javascript/mastodon/locales/gd.json +++ b/app/javascript/mastodon/locales/gd.json @@ -432,8 +432,6 @@ "keyboard_shortcuts.unfocus": "Thoir am fòcas far raon teacsa an sgrìobhaidh/an luirg", "keyboard_shortcuts.up": "Gluais suas air an liosta", "lightbox.close": "Dùin", - "lightbox.compress": "Co-theannaich bogsa sealladh an deilbh", - "lightbox.expand": "Leudaich bogsa sealladh an deilbh", "lightbox.next": "Air adhart", "lightbox.previous": "Air ais", "limited_account_hint.action": "Seall a’ phròifil co-dhiù", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index d35bcd8cf5..5e67cbce25 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "Para deixar de destacar a área de escritura/procura", "keyboard_shortcuts.up": "Para mover cara arriba na listaxe", "lightbox.close": "Fechar", - "lightbox.compress": "Comprimir a caixa de vista da imaxe", - "lightbox.expand": "Estender a caixa de vista da imaxe", "lightbox.next": "Seguinte", "lightbox.previous": "Anterior", + "lightbox.zoom_in": "Ver tamaño real", + "lightbox.zoom_out": "Ver tamaño axustado", "limited_account_hint.action": "Mostrar perfil igualmente", "limited_account_hint.title": "Este perfil foi agochado pola moderación de {domain}.", "link_preview.author": "Por {name}", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index 5e6ab56c23..6503d870e5 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "לצאת מתיבת חיבור/חיפוש", "keyboard_shortcuts.up": "לנוע במעלה הרשימה", "lightbox.close": "סגירה", - "lightbox.compress": "דחיסת קופסת צפייה בתמונה", - "lightbox.expand": "הרחבת קופסת צפייה בתמונה", "lightbox.next": "הבא", "lightbox.previous": "הקודם", + "lightbox.zoom_in": "הגדלה לגודל מלא", + "lightbox.zoom_out": "התאמה לגודל המסך", "limited_account_hint.action": "הצג חשבון בכל זאת", "limited_account_hint.title": "פרופיל המשתמש הזה הוסתר על ידי המנחים של {domain}.", "link_preview.author": "מאת {name}", diff --git a/app/javascript/mastodon/locales/hi.json b/app/javascript/mastodon/locales/hi.json index 58b04a20cd..4a513c1c07 100644 --- a/app/javascript/mastodon/locales/hi.json +++ b/app/javascript/mastodon/locales/hi.json @@ -349,8 +349,6 @@ "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", "lightbox.close": "बंद करें", - "lightbox.compress": "कंप्रेस इमेज व्यू बॉक्स", - "lightbox.expand": "एक्सपैंड इमेज व्यू बॉक्स", "lightbox.next": "अगला", "lightbox.previous": "पिछला", "limited_account_hint.action": "फिर भी प्रोफाइल दिखाओ", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index d4ced22463..cb3517e3a3 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "Szerkesztés/keresés fókuszból való kivétele", "keyboard_shortcuts.up": "Mozgás felfelé a listában", "lightbox.close": "Bezárás", - "lightbox.compress": "Képnéző doboz összezárása", - "lightbox.expand": "Képnéző doboz kinyitása", "lightbox.next": "Következő", "lightbox.previous": "Előző", + "lightbox.zoom_in": "Nagyítás a tényleges méretre", + "lightbox.zoom_out": "Méretre igazítás", "limited_account_hint.action": "Profil megjelenítése mindenképpen", "limited_account_hint.title": "Ezt a profilt {domain} moderátorai elrejtették.", "link_preview.author": "{name} szerint", diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json index a81abb82f0..9e5ae79045 100644 --- a/app/javascript/mastodon/locales/hy.json +++ b/app/javascript/mastodon/locales/hy.json @@ -271,8 +271,6 @@ "keyboard_shortcuts.unfocus": "տեքստի/որոնման տիրոյթից ապասեւեռուելու համար", "keyboard_shortcuts.up": "ցանկով վերեւ շարժուելու համար", "lightbox.close": "Փակել", - "lightbox.compress": "Փակել պատկերի դիտման պատուհանը", - "lightbox.expand": "Բացել պատկերի դիտման պատուհանը", "lightbox.next": "Յաջորդ", "lightbox.previous": "Նախորդ", "lists.account.add": "Աւելացնել ցանկին", diff --git a/app/javascript/mastodon/locales/ia.json b/app/javascript/mastodon/locales/ia.json index 9e7b583375..93bee9ec79 100644 --- a/app/javascript/mastodon/locales/ia.json +++ b/app/javascript/mastodon/locales/ia.json @@ -423,8 +423,6 @@ "keyboard_shortcuts.unfocus": "Disfocalisar le area de composition de texto/de recerca", "keyboard_shortcuts.up": "Displaciar in alto in le lista", "lightbox.close": "Clauder", - "lightbox.compress": "Comprimer le quadro de visualisation de imagine", - "lightbox.expand": "Expander le quadro de visualisation de imagine", "lightbox.next": "Sequente", "lightbox.previous": "Precedente", "limited_account_hint.action": "Monstrar profilo in omne caso", diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json index 687c32c1b1..b6d01ec996 100644 --- a/app/javascript/mastodon/locales/id.json +++ b/app/javascript/mastodon/locales/id.json @@ -381,8 +381,6 @@ "keyboard_shortcuts.unfocus": "untuk tidak fokus pada area teks/pencarian", "keyboard_shortcuts.up": "untuk memindah ke atas pada daftar", "lightbox.close": "Tutup", - "lightbox.compress": "Kompres kotak tampilan gambar", - "lightbox.expand": "Besarkan kotak tampilan gambar", "lightbox.next": "Selanjutnya", "lightbox.previous": "Sebelumnya", "limited_account_hint.action": "Tetap tampilkan profil", diff --git a/app/javascript/mastodon/locales/ie.json b/app/javascript/mastodon/locales/ie.json index 3226801019..1439b851d3 100644 --- a/app/javascript/mastodon/locales/ie.json +++ b/app/javascript/mastodon/locales/ie.json @@ -396,8 +396,6 @@ "keyboard_shortcuts.unfocus": "Desinfocar text-area de composition/serchar", "keyboard_shortcuts.up": "Mover ad-supra in li liste", "lightbox.close": "Cluder", - "lightbox.compress": "Compresser vise-buxe de image", - "lightbox.expand": "Expander vise-buxe de image", "lightbox.next": "Sequent", "lightbox.previous": "Precedent", "limited_account_hint.action": "Monstrar profil totvez", diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json index bb0a86ac2b..aa284685e7 100644 --- a/app/javascript/mastodon/locales/io.json +++ b/app/javascript/mastodon/locales/io.json @@ -339,8 +339,6 @@ "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", "lightbox.close": "Klozar", - "lightbox.compress": "Kompresez imajvidbuxo", - "lightbox.expand": "Expansez imajvidbuxo", "lightbox.next": "Nexta", "lightbox.previous": "Antea", "limited_account_hint.action": "Jus montrez profilo", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index fb807429f7..5f32b438dd 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -432,8 +432,6 @@ "keyboard_shortcuts.unfocus": "Taka virkni úr textainnsetningarreit eða leit", "keyboard_shortcuts.up": "Fara ofar í listanum", "lightbox.close": "Loka", - "lightbox.compress": "Þjappa myndskoðunarreit", - "lightbox.expand": "Fletta út myndskoðunarreit", "lightbox.next": "Næsta", "lightbox.previous": "Fyrra", "limited_account_hint.action": "Birta notandasniðið samt", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 97518c5862..ec26d80be6 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "Rimuove il focus sull'area di composizione testuale/ricerca", "keyboard_shortcuts.up": "Scorre in su nell'elenco", "lightbox.close": "Chiudi", - "lightbox.compress": "Comprimi casella di visualizzazione immagine", - "lightbox.expand": "Espandi casella di visualizzazione immagine", "lightbox.next": "Successivo", "lightbox.previous": "Precedente", + "lightbox.zoom_in": "Ingrandisci alla dimensione attuale", + "lightbox.zoom_out": "Ingrandisci per adattarsi", "limited_account_hint.action": "Mostra comunque il profilo", "limited_account_hint.title": "Questo profilo è stato nascosto dai moderatori di {domain}.", "link_preview.author": "Di {name}", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 38492a7361..c3ddeed880 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "投稿の入力欄・検索欄から離れる", "keyboard_shortcuts.up": "カラム内一つ上に移動", "lightbox.close": "閉じる", - "lightbox.compress": "画像ビューボックスを閉じる", - "lightbox.expand": "画像ビューボックスを開く", "lightbox.next": "次", "lightbox.previous": "前", + "lightbox.zoom_in": "実際のサイズにする", + "lightbox.zoom_out": "表示範囲に合わせる", "limited_account_hint.action": "構わず表示する", "limited_account_hint.title": "このプロフィールは{domain}のモデレーターによって非表示にされています。", "link_preview.author": "{name}", @@ -457,6 +457,7 @@ "lists.subheading": "あなたのリスト", "load_pending": "{count}件の新着", "loading_indicator.label": "読み込み中…", + "media_gallery.hide": "隠す", "moved_to_account_banner.text": "あなたのアカウント『{disabledAccount}』は『{movedToAccount}』に移動したため現在無効になっています。", "mute_modal.hide_from_notifications": "通知をオフにする", "mute_modal.hide_options": "オプションを閉じる", @@ -785,6 +786,7 @@ "status.edit": "編集", "status.edited": "最終更新日 {date}", "status.edited_x_times": "{count}回編集", + "status.embed": "埋め込みコードを取得", "status.favourite": "お気に入り", "status.favourites": "{count, plural, one {お気に入り} other {お気に入り}}", "status.filter": "この投稿をフィルターする", diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json index 822fb59e6f..89f68ab95f 100644 --- a/app/javascript/mastodon/locales/kab.json +++ b/app/javascript/mastodon/locales/kab.json @@ -82,6 +82,7 @@ "block_modal.title": "Sewḥel aseqdac ?", "block_modal.you_wont_see_mentions": "Ur tezmireḍ ara ad twaliḍ tisuffaɣ anda d-yettwabdar.", "boost_modal.combo": "Tzemreḍ ad tsiteḍ ɣef {combo} akken ad tzegleḍ aya tikelt i d-iteddun", + "boost_modal.reblog": "Zuzer tasuffeɣt?", "bundle_column_error.copy_stacktrace": "Nɣel tuccḍa n uneqqis", "bundle_column_error.error.title": "Uh, ala !", "bundle_column_error.network.title": "Tuccḍa deg uẓeṭṭa", @@ -190,7 +191,7 @@ "domain_pill.server": "Aqeddac", "domain_pill.username": "Isem n useqdac", "domain_pill.your_server": "D axxam-inek·inem umḍin, anda i zedɣent akk tsuffaɣ-ik·im. Ur k·m-yeεǧib ara wa? Ssenfel-d iqeddacen melmi i ak·m-yehwa, awi-d daɣen ineḍfaren-ik·im yid-k·m.", - "embed.instructions": "Ẓẓu addad-agi deg usmel-inek s wenγal n tangalt yellan sdaw-agi.", + "embed.instructions": "Ẓẓu addad-agi deg usmel-inek·inem s wenɣal n tangalt yellan sdaw-agi.", "embed.preview": "Akka ara d-iban:", "emoji_button.activity": "Aqeddic", "emoji_button.clear": "Sfeḍ", @@ -332,8 +333,6 @@ "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "i tulin ɣer d asawen n tebdart", "lightbox.close": "Mdel", - "lightbox.compress": "Ḥemmeẓ tamnaḍt n uskan n tugna", - "lightbox.expand": "Simeɣer tamnaḍt n uskan n tugna", "lightbox.next": "Ɣer zdat", "lightbox.previous": "Ɣer deffir", "limited_account_hint.action": "Wali amaɣnu akken yebɣu yili", @@ -356,7 +355,7 @@ "lists.subheading": "Tibdarin-ik·im", "load_pending": "{count, plural, one {# n uferdis amaynut} other {# n yiferdisen imaynuten}}", "loading_indicator.label": "Yessalay-d …", - "media_gallery.hide": "Ffer-it", + "media_gallery.hide": "Seggelmes", "mute_modal.hide_from_notifications": "Ffer-it deg ulɣuten", "mute_modal.hide_options": "Ffer tinefrunin", "mute_modal.indefinite": "Alamma ssnesreɣ asgugem fell-as", @@ -590,6 +589,7 @@ "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": "Awi-d tangalt n weslaɣ", "status.favourite": "Amenyaf", "status.favourites": "{count, plural, one {n usmenyaf} other {n ismenyafen}}", "status.filter": "Sizdeg tassufeɣt-a", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index cf2082e105..af1599572b 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "작성창에서 포커스 해제", "keyboard_shortcuts.up": "리스트에서 위로 이동", "lightbox.close": "닫기", - "lightbox.compress": "이미지 박스 압축", - "lightbox.expand": "이미지 박스 확장", "lightbox.next": "다음", "lightbox.previous": "이전", + "lightbox.zoom_in": "실제 크기에 맞춰 보기", + "lightbox.zoom_out": "화면 크기에 맞춰 보기", "limited_account_hint.action": "그래도 프로필 보기", "limited_account_hint.title": "이 프로필은 {domain}의 중재자에 의해 숨겨진 상태입니다.", "link_preview.author": "{name}", diff --git a/app/javascript/mastodon/locales/ku.json b/app/javascript/mastodon/locales/ku.json index dd10000d18..86ecf98446 100644 --- a/app/javascript/mastodon/locales/ku.json +++ b/app/javascript/mastodon/locales/ku.json @@ -293,8 +293,6 @@ "keyboard_shortcuts.unfocus": "Bal nede cîhê nivîsê /lêgerînê", "keyboard_shortcuts.up": "Di lîsteyê de rake jor", "lightbox.close": "Bigire", - "lightbox.compress": "Qutîya wêneya nîşan dike bitepisîne", - "lightbox.expand": "Qutîya wêneya nîşan dike fireh bike", "lightbox.next": "Pêş", "lightbox.previous": "Paş", "limited_account_hint.action": "Bi heman awayî profîlê nîşan bide", diff --git a/app/javascript/mastodon/locales/kw.json b/app/javascript/mastodon/locales/kw.json index 60b8321d53..cef24aa3b7 100644 --- a/app/javascript/mastodon/locales/kw.json +++ b/app/javascript/mastodon/locales/kw.json @@ -195,8 +195,6 @@ "keyboard_shortcuts.unfocus": "Anfogella tekstva gomposya/hwilas", "keyboard_shortcuts.up": "Movya war-vann y'n rol", "lightbox.close": "Degea", - "lightbox.compress": "Kula kist a weles aven", - "lightbox.expand": "Efani kist a weles aven", "lightbox.next": "Nessa", "lightbox.previous": "Kynsa", "lists.account.add": "Keworra dhe rol", diff --git a/app/javascript/mastodon/locales/lad.json b/app/javascript/mastodon/locales/lad.json index 72bf260501..43da2ea16f 100644 --- a/app/javascript/mastodon/locales/lad.json +++ b/app/javascript/mastodon/locales/lad.json @@ -34,7 +34,9 @@ "account.follow_back": "Sige tamyen", "account.followers": "Suivantes", "account.followers.empty": "Por agora dingun no sige a este utilizador.", + "account.followers_counter": "{count, plural, one {{counter} suivante} other {{counter} suivantes}}", "account.following": "Sigiendo", + "account.following_counter": "{count, plural, other {Sigiendo a {counter}}}", "account.follows.empty": "Este utilizador ainda no sige a dingun.", "account.go_to_profile": "Va al profil", "account.hide_reblogs": "Eskonde repartajasyones de @{name}", @@ -60,6 +62,7 @@ "account.requested_follow": "{name} tiene solisitado segirte", "account.share": "Partaja el profil de @{name}", "account.show_reblogs": "Amostra repartajasyones de @{name}", + "account.statuses_counter": "{count, plural, one {{counter} publikasyon} other {{counter} publikasyones}}", "account.unblock": "Dezbloka a @{name}", "account.unblock_domain": "Dezbloka domeno {domain}", "account.unblock_short": "Dezbloka", @@ -393,8 +396,6 @@ "keyboard_shortcuts.unfocus": "No enfoka en el area de eskrivir/bushkeda", "keyboard_shortcuts.up": "Move verso arriva en la lista", "lightbox.close": "Serra", - "lightbox.compress": "Kompresa kuadro de imaje", - "lightbox.expand": "Espande kuadro de imaje", "lightbox.next": "Sigiente", "lightbox.previous": "Anterior", "limited_account_hint.action": "Amostra el profil entanto", @@ -418,6 +419,7 @@ "lists.subheading": "Tus listas", "load_pending": "{count, plural, one {# muevo elemento} other {# muevos elementos}}", "loading_indicator.label": "Eskargando…", + "media_gallery.hide": "Eskonde", "moved_to_account_banner.text": "Tu kuento {disabledAccount} esta aktualmente inkapasitado porke transferates a {movedToAccount}.", "mute_modal.hide_from_notifications": "Eskonde de avizos", "mute_modal.hide_options": "Eskonde opsyones", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index 5a510f5b96..632c33129a 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "Nebefokusuoti rengykles teksto sritį / paiešką", "keyboard_shortcuts.up": "Perkelti į viršų sąraše", "lightbox.close": "Uždaryti", - "lightbox.compress": "Suspausti vaizdo peržiūros langelį", - "lightbox.expand": "Išplėsti vaizdo peržiūros langelį", "lightbox.next": "Kitas", "lightbox.previous": "Ankstesnis", + "lightbox.zoom_in": "Padidink iki tikrojo dydžio", + "lightbox.zoom_out": "Padidink, kad tilptų", "limited_account_hint.action": "Vis tiek rodyti profilį", "limited_account_hint.title": "Šį profilį paslėpė {domain} prižiūrėtojai.", "link_preview.author": "Sukūrė {name}", diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json index 53b3c0fcaa..43fa8369f4 100644 --- a/app/javascript/mastodon/locales/lv.json +++ b/app/javascript/mastodon/locales/lv.json @@ -385,8 +385,6 @@ "keyboard_shortcuts.unfocus": "Atfokusēt veidojamā teksta/meklēšanas lauku", "keyboard_shortcuts.up": "Pārvietoties augšup sarakstā", "lightbox.close": "Aizvērt", - "lightbox.compress": "Saspiest attēla skata lodziņu", - "lightbox.expand": "Izvērst attēla skata lodziņu", "lightbox.next": "Tālāk", "lightbox.previous": "Iepriekšējais", "limited_account_hint.action": "Tik un tā rādīt profilu", diff --git a/app/javascript/mastodon/locales/mr.json b/app/javascript/mastodon/locales/mr.json index 47c3a55e55..aa8169616e 100644 --- a/app/javascript/mastodon/locales/mr.json +++ b/app/javascript/mastodon/locales/mr.json @@ -169,8 +169,6 @@ "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", "lightbox.close": "बंद करा", - "lightbox.compress": "प्रतिमा दृश्य बॉक्स कॉम्प्रेस करा", - "lightbox.expand": "प्रतिमा दृश्य बॉक्स विस्तृत करा", "lightbox.next": "पुढे", "lightbox.previous": "मागील", "limited_account_hint.action": "तरीही प्रोफाइल दाखवा", diff --git a/app/javascript/mastodon/locales/ms.json b/app/javascript/mastodon/locales/ms.json index 44403ec959..cbd57ab356 100644 --- a/app/javascript/mastodon/locales/ms.json +++ b/app/javascript/mastodon/locales/ms.json @@ -360,8 +360,6 @@ "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "Pindah ke atas dalam senarai", "lightbox.close": "Tutup", - "lightbox.compress": "Kecilkan kotak paparan imej", - "lightbox.expand": "Besarkan kotak paparan imej", "lightbox.next": "Seterusnya", "lightbox.previous": "Sebelumnya", "limited_account_hint.action": "Paparkan profil", diff --git a/app/javascript/mastodon/locales/my.json b/app/javascript/mastodon/locales/my.json index e93b47fd54..c97de73335 100644 --- a/app/javascript/mastodon/locales/my.json +++ b/app/javascript/mastodon/locales/my.json @@ -339,8 +339,6 @@ "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", "lightbox.close": "ပိတ်ပါ", - "lightbox.compress": "ရုပ်ပုံမြင်ကွင်းအကွက်ကို ချုံ့ပါ", - "lightbox.expand": "ပုံကိုဖွင့်ပါ", "lightbox.next": "ရှေ့သို့", "lightbox.previous": "ရှေ့သို့", "limited_account_hint.action": "ဘာပဲဖြစ်ဖြစ် ပရိုဖိုင်ကို ပြပါ", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index 1043ca8376..f5f4a0a137 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "Tekst- en zoekveld ontfocussen", "keyboard_shortcuts.up": "Naar boven in de lijst bewegen", "lightbox.close": "Sluiten", - "lightbox.compress": "Afbeelding passend weergeven", - "lightbox.expand": "Afbeelding groot weergeven", "lightbox.next": "Volgende", "lightbox.previous": "Vorige", + "lightbox.zoom_in": "Oorspronkelijke grootte weergeven", + "lightbox.zoom_out": "Passend weergeven", "limited_account_hint.action": "Alsnog het profiel tonen", "limited_account_hint.title": "Dit profiel is door de moderatoren van {domain} verborgen.", "link_preview.author": "Door {name}", diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json index c4b5f0291d..702154927d 100644 --- a/app/javascript/mastodon/locales/nn.json +++ b/app/javascript/mastodon/locales/nn.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "for å fokusere vekk skrive-/søkefeltet", "keyboard_shortcuts.up": "Flytt opp på lista", "lightbox.close": "Lukk", - "lightbox.compress": "Komprimer biletvisningsboksen", - "lightbox.expand": "Utvid biletvisningsboksen", "lightbox.next": "Neste", "lightbox.previous": "Førre", + "lightbox.zoom_in": "Zoom til faktisk storleik", + "lightbox.zoom_out": "Vis heile", "limited_account_hint.action": "Vis profilen likevel", "limited_account_hint.title": "Denne profilen er skjult av moderatorane på {domain}.", "link_preview.author": "Av {name}", diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index 1222f40424..1d0294cd7c 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -432,8 +432,6 @@ "keyboard_shortcuts.unfocus": "Fjern fokus fra komponerings-/søkefeltet", "keyboard_shortcuts.up": "Flytt oppover i listen", "lightbox.close": "Lukk", - "lightbox.compress": "Komprimer bildevisningsboks", - "lightbox.expand": "Ekspander bildevisning boks", "lightbox.next": "Neste", "lightbox.previous": "Forrige", "limited_account_hint.action": "Vis profil likevel", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index 22193f82aa..9dbd123c9b 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -297,8 +297,6 @@ "keyboard_shortcuts.unfocus": "quitar lo camp tèxte/de recèrca", "keyboard_shortcuts.up": "far montar dins la lista", "lightbox.close": "Tampar", - "lightbox.compress": "Fenèstra de visualizacion dels imatges compressats", - "lightbox.expand": "Espandir la fenèstra de visualizacion d’imatge", "lightbox.next": "Seguent", "lightbox.previous": "Precedent", "limited_account_hint.action": "Afichar lo perfil de tota manièra", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index d415bb3811..a4ee25250a 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "aby opuścić pole wyszukiwania/pisania", "keyboard_shortcuts.up": "aby przejść na górę listy", "lightbox.close": "Zamknij", - "lightbox.compress": "Zmniejsz pole widoku obrazu", - "lightbox.expand": "Rozwiń pole widoku obrazu", "lightbox.next": "Następne", "lightbox.previous": "Poprzednie", + "lightbox.zoom_in": "Rozmiar rzeczywisty", + "lightbox.zoom_out": "Dopasuj", "limited_account_hint.action": "Pokaż profil mimo to", "limited_account_hint.title": "Ten profil został ukryty przez moderatorów {domain}.", "link_preview.author": "{name}", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index 053ae25822..1eca63ac15 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -432,10 +432,9 @@ "keyboard_shortcuts.unfocus": "desfocar de tudo", "keyboard_shortcuts.up": "mover para cima", "lightbox.close": "Fechar", - "lightbox.compress": "Fechar imagem", - "lightbox.expand": "Abrir imagem", "lightbox.next": "Próximo", "lightbox.previous": "Anterior", + "lightbox.zoom_in": "Voltar para o tamanho real", "limited_account_hint.action": "Exibir perfil mesmo assim", "limited_account_hint.title": "Este perfil foi ocultado pelos moderadores do {domain}.", "link_preview.author": "Por {name}", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index ed4bb9ba01..80f5f9f138 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -432,8 +432,6 @@ "keyboard_shortcuts.unfocus": "para remover o foco da área de texto/pesquisa", "keyboard_shortcuts.up": "para mover para cima na lista", "lightbox.close": "Fechar", - "lightbox.compress": "Compactar caixa de visualização de imagem", - "lightbox.expand": "Expandir caixa de visualização de imagem", "lightbox.next": "Próximo", "lightbox.previous": "Anterior", "limited_account_hint.action": "Exibir perfil mesmo assim", diff --git a/app/javascript/mastodon/locales/ro.json b/app/javascript/mastodon/locales/ro.json index 73b8140de6..a61f4b0d4f 100644 --- a/app/javascript/mastodon/locales/ro.json +++ b/app/javascript/mastodon/locales/ro.json @@ -334,8 +334,6 @@ "keyboard_shortcuts.unfocus": "Părăsește zona de text/bara de căutare", "keyboard_shortcuts.up": "Urcă în listă", "lightbox.close": "Închide", - "lightbox.compress": "Închide panoul de vizualizare a imaginilor", - "lightbox.expand": "Deschide panoul de vizualizare a imaginilor", "lightbox.next": "Înainte", "lightbox.previous": "Înapoi", "limited_account_hint.action": "Afișează profilul oricum", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index d97b1658c5..1366114929 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -431,8 +431,6 @@ "keyboard_shortcuts.unfocus": "убрать фокус с поля ввода/поиска", "keyboard_shortcuts.up": "вверх по списку", "lightbox.close": "Закрыть", - "lightbox.compress": "Сжать окно просмотра изображений", - "lightbox.expand": "Развернуть окно просмотра изображений", "lightbox.next": "Далее", "lightbox.previous": "Назад", "limited_account_hint.action": "Все равно показать профиль", diff --git a/app/javascript/mastodon/locales/sa.json b/app/javascript/mastodon/locales/sa.json index 85bd9aa9dd..2d48f688d5 100644 --- a/app/javascript/mastodon/locales/sa.json +++ b/app/javascript/mastodon/locales/sa.json @@ -299,8 +299,6 @@ "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", "lightbox.close": "पिधीयताम्", - "lightbox.compress": "सङ्कुच चित्रप्रदर्शनपेटकम्", - "lightbox.expand": "चित्रप्रदर्शनपेटकं विस्तारय", "lightbox.next": "परः", "lightbox.previous": "पूर्वः", "limited_account_hint.action": "प्रोफैलं दर्शय कथञ्चित्", diff --git a/app/javascript/mastodon/locales/sc.json b/app/javascript/mastodon/locales/sc.json index 071643a153..bb7d062b95 100644 --- a/app/javascript/mastodon/locales/sc.json +++ b/app/javascript/mastodon/locales/sc.json @@ -350,8 +350,6 @@ "keyboard_shortcuts.unfocus": "Essi de s'àrea de cumpositzione de testu o de chirca", "keyboard_shortcuts.up": "Move in susu in sa lista", "lightbox.close": "Serra", - "lightbox.compress": "Cumprime sa casella de visualizatzione de is immàgines", - "lightbox.expand": "Ismànnia sa casella de visualizatzione de is immàgines", "lightbox.next": "Imbeniente", "lightbox.previous": "Pretzedente", "limited_account_hint.title": "Custu profilu est istadu cuadu dae sa moderatzione de {domain}.", diff --git a/app/javascript/mastodon/locales/sco.json b/app/javascript/mastodon/locales/sco.json index 269e29a86f..c14f1cc51a 100644 --- a/app/javascript/mastodon/locales/sco.json +++ b/app/javascript/mastodon/locales/sco.json @@ -284,8 +284,6 @@ "keyboard_shortcuts.unfocus": "Unfocus scrieve textarea/seirch", "keyboard_shortcuts.up": "Muive up in the list", "lightbox.close": "Shut", - "lightbox.compress": "Compress image view box", - "lightbox.expand": "Expand image view box", "lightbox.next": "Neist", "lightbox.previous": "Last ane", "limited_account_hint.action": "Shaw profile onieweys", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index d21dc5e1ab..c989a7314c 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -396,8 +396,6 @@ "keyboard_shortcuts.unfocus": "Odísť z textového poľa", "keyboard_shortcuts.up": "Posunúť sa vyššie v zozname", "lightbox.close": "Zatvoriť", - "lightbox.compress": "Zmenšiť náhľad obrázku", - "lightbox.expand": "Rozšíriť náhľad obrázku", "lightbox.next": "Ďalej", "lightbox.previous": "Späť", "limited_account_hint.action": "Aj tak zobraziť profil", diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json index bdc1d56062..43042ca998 100644 --- a/app/javascript/mastodon/locales/sl.json +++ b/app/javascript/mastodon/locales/sl.json @@ -412,8 +412,6 @@ "keyboard_shortcuts.unfocus": "Odstrani pozornost z območja za sestavljanje besedila/iskanje", "keyboard_shortcuts.up": "Premakni navzgor po seznamu", "lightbox.close": "Zapri", - "lightbox.compress": "Strni ogledno polje slike", - "lightbox.expand": "Razširi ogledno polje slike", "lightbox.next": "Naslednji", "lightbox.previous": "Prejšnji", "limited_account_hint.action": "Vseeno pokaži profil", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index ea3c6d5c7c..c5e42aae77 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -432,8 +432,6 @@ "keyboard_shortcuts.unfocus": "Për heqjen e fokusit nga fusha e hartimit të mesazheve apo kërkimeve", "keyboard_shortcuts.up": "Për ngjitje sipër nëpër listë", "lightbox.close": "Mbylle", - "lightbox.compress": "Ngjeshe kuadratin e parjes së figurave", - "lightbox.expand": "Zgjeroje kuadratin e parjes së figurave", "lightbox.next": "Pasuesja", "lightbox.previous": "E mëparshmja", "limited_account_hint.action": "Shfaqe profilin sido qoftë", diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json index de30ca280f..e701da6078 100644 --- a/app/javascript/mastodon/locales/sr-Latn.json +++ b/app/javascript/mastodon/locales/sr-Latn.json @@ -399,8 +399,6 @@ "keyboard_shortcuts.unfocus": "Ukloni fokus sa polja za unos teksta/pretrage", "keyboard_shortcuts.up": "Premesti nagore u listi", "lightbox.close": "Zatvori", - "lightbox.compress": "Komprimuj okvir za prikaz slike", - "lightbox.expand": "Proširi okvir za prikaz slike", "lightbox.next": "Sledeće", "lightbox.previous": "Prethodno", "limited_account_hint.action": "Ipak prikaži profil", diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json index 255215a167..fd13c4ee5c 100644 --- a/app/javascript/mastodon/locales/sr.json +++ b/app/javascript/mastodon/locales/sr.json @@ -399,8 +399,6 @@ "keyboard_shortcuts.unfocus": "Уклони фокус са поља за унос текста/претраге", "keyboard_shortcuts.up": "Премести нагоре у листи", "lightbox.close": "Затвори", - "lightbox.compress": "Компримуј оквир за приказ слике", - "lightbox.expand": "Прошири оквир за приказ слике", "lightbox.next": "Следеће", "lightbox.previous": "Претходно", "limited_account_hint.action": "Ипак прикажи профил", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 4d560a6495..6bb0b1424a 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -429,8 +429,6 @@ "keyboard_shortcuts.unfocus": "för att avfokusera skrivfält/sökfält", "keyboard_shortcuts.up": "för att flytta uppåt i listan", "lightbox.close": "Stäng", - "lightbox.compress": "Komprimera bildvyrutan", - "lightbox.expand": "Utöka bildvyrutan", "lightbox.next": "Nästa", "lightbox.previous": "Tidigare", "limited_account_hint.action": "Visa profil ändå", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index 9578431ff7..d446cacb92 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -432,8 +432,6 @@ "keyboard_shortcuts.unfocus": "เลิกโฟกัสพื้นที่เขียนข้อความ/การค้นหา", "keyboard_shortcuts.up": "ย้ายขึ้นในรายการ", "lightbox.close": "ปิด", - "lightbox.compress": "บีบอัดกล่องดูภาพ", - "lightbox.expand": "ขยายกล่องดูภาพ", "lightbox.next": "ถัดไป", "lightbox.previous": "ก่อนหน้า", "limited_account_hint.action": "แสดงโปรไฟล์ต่อไป", diff --git a/app/javascript/mastodon/locales/tok.json b/app/javascript/mastodon/locales/tok.json index 6c21026bdb..9f20388182 100644 --- a/app/javascript/mastodon/locales/tok.json +++ b/app/javascript/mastodon/locales/tok.json @@ -252,8 +252,6 @@ "keyboard_shortcuts.toot": "o toki", "keyboard_shortcuts.up": "o tawa sewi lon lipu", "lightbox.close": "o pini", - "lightbox.compress": "o lili e sitelen", - "lightbox.expand": "o suli e sitelen", "lightbox.next": "sinpin", "lightbox.previous": "monsi", "link_preview.author": "tan {name}", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index d4d7d3d113..bdb2de0ef1 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "Aramada bir gönderiye odaklanmamak için", "keyboard_shortcuts.up": "Listede yukarıya çıkmak için", "lightbox.close": "Kapat", - "lightbox.compress": "Resim görüntüleme kutusunu sıkıştır", - "lightbox.expand": "Resim görüntüleme kutusunu genişlet", "lightbox.next": "Sonraki", "lightbox.previous": "Önceki", + "lightbox.zoom_in": "Özgün boyuta dön", + "lightbox.zoom_out": "Sığacak şekilde boyutla", "limited_account_hint.action": "Yine de profili göster", "limited_account_hint.title": "Bu profil {domain} moderatörleri tarafından gizlendi.", "link_preview.author": "Yazar: {name}", diff --git a/app/javascript/mastodon/locales/tt.json b/app/javascript/mastodon/locales/tt.json index 01429f9431..08bb7979a1 100644 --- a/app/javascript/mastodon/locales/tt.json +++ b/app/javascript/mastodon/locales/tt.json @@ -271,8 +271,6 @@ "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", "lightbox.close": "Ябу", - "lightbox.compress": "Кысылган рәсемне карау тәрәзәсе", - "lightbox.expand": "Рәсемне карау тәрәзәсен ачыгыз", "lightbox.next": "Киләсе", "lightbox.previous": "Алдагы", "limited_account_hint.action": "Барыбер профильне күрсәтергә", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index a1d3a872df..3bf8d00248 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "Розфокусуватися з нового допису чи пошуку", "keyboard_shortcuts.up": "Рухатися вгору списком", "lightbox.close": "Закрити", - "lightbox.compress": "Стиснути поле перегляду зображень", - "lightbox.expand": "Розгорнути поле перегляду зображень", "lightbox.next": "Далі", "lightbox.previous": "Назад", + "lightbox.zoom_in": "Масштаб за реальним розміром", + "lightbox.zoom_out": "Збільшити відповідно до розміру", "limited_account_hint.action": "Усе одно показати профіль", "limited_account_hint.title": "Цей профіль сховали модератори {domain}.", "link_preview.author": "Від {name}", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index 9fcd00ddc7..74c46c7b33 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -3,7 +3,7 @@ "about.contact": "Liên lạc:", "about.disclaimer": "Mastodon là phần mềm tự do nguồn mở của Mastodon gGmbH.", "about.domain_blocks.no_reason_available": "Lý do không được cung cấp", - "about.domain_blocks.preamble": "Mastodon cho phép bạn tương tác nội dung và giao tiếp với mọi người từ bất kỳ máy chủ nào khác trong mạng liên hợp. Còn máy chủ này có những ngoại lệ riêng.", + "about.domain_blocks.preamble": "Mastodon cho phép bạn đọc nội dung và giao tiếp với mọi người từ bất kỳ máy chủ nào. Còn đây là những ngoại lệ trên máy chủ này.", "about.domain_blocks.silenced.explanation": "Nói chung, bạn sẽ không thấy người và nội dung từ máy chủ này, trừ khi bạn tự tìm kiếm hoặc tự theo dõi.", "about.domain_blocks.silenced.title": "Hạn chế", "about.domain_blocks.suspended.explanation": "Dữ liệu từ máy chủ này sẽ không được xử lý, lưu trữ hoặc trao đổi. Mọi tương tác hoặc giao tiếp với người từ máy chủ này đều bị cấm.", @@ -34,9 +34,9 @@ "account.follow_back": "Theo dõi lại", "account.followers": "Người theo dõi", "account.followers.empty": "Chưa có người theo dõi nào.", - "account.followers_counter": "{count, plural, other {{counter} người theo dõi}}", + "account.followers_counter": "{count, plural, other {{counter} Người theo dõi}}", "account.following": "Đang theo dõi", - "account.following_counter": "{count, plural, other {{counter} đang theo dõi}}", + "account.following_counter": "{count, plural, other {{counter} Đang theo dõi}}", "account.follows.empty": "Người này chưa theo dõi ai.", "account.go_to_profile": "Xem hồ sơ", "account.hide_reblogs": "Ẩn tút @{name} đăng lại", @@ -62,7 +62,7 @@ "account.requested_follow": "{name} yêu cầu theo dõi bạn", "account.share": "Chia sẻ @{name}", "account.show_reblogs": "Hiện tút do @{name} đăng lại", - "account.statuses_counter": "{count, plural, other {{counter} tút}}", + "account.statuses_counter": "{count, plural, other {{counter} Tút}}", "account.unblock": "Bỏ chặn @{name}", "account.unblock_domain": "Bỏ ẩn {domain}", "account.unblock_short": "Bỏ chặn", @@ -145,7 +145,7 @@ "community.column_settings.remote_only": "Chỉ người ở máy chủ khác", "compose.language.change": "Chọn ngôn ngữ tút", "compose.language.search": "Tìm ngôn ngữ...", - "compose.published.body": "Đã đăng.", + "compose.published.body": "Tút đã được đăng.", "compose.published.open": "Xem lại", "compose.saved.body": "Đã lưu tút.", "compose_form.direct_message_warning_learn_more": "Tìm hiểu thêm", @@ -154,8 +154,8 @@ "compose_form.lock_disclaimer": "Tài khoản của bạn không {locked}. Bất cứ ai cũng có thể theo dõi và xem tút riêng tư của bạn.", "compose_form.lock_disclaimer.lock": "khóa", "compose_form.placeholder": "Bạn đang nghĩ gì?", - "compose_form.poll.duration": "Hết hạn vào", - "compose_form.poll.multiple": "Nhiều lựa chọn", + "compose_form.poll.duration": "Hết hạn", + "compose_form.poll.multiple": "Chọn nhiều", "compose_form.poll.option_placeholder": "Lựa chọn {number}", "compose_form.poll.single": "Chọn một", "compose_form.poll.switch_to_multiple": "Có thể chọn nhiều lựa chọn", @@ -170,30 +170,30 @@ "compose_form.spoiler_placeholder": "Nội dung ẩn (tùy chọn)", "confirmation_modal.cancel": "Hủy bỏ", "confirmations.block.confirm": "Chặn", - "confirmations.delete.confirm": "Xóa bỏ", - "confirmations.delete.message": "Bạn thật sự muốn xóa tút này?", - "confirmations.delete.title": "Xóa tút?", - "confirmations.delete_list.confirm": "Xóa bỏ", - "confirmations.delete_list.message": "Bạn thật sự muốn xóa vĩnh viễn danh sách này?", - "confirmations.delete_list.title": "Xóa danh sách?", + "confirmations.delete.confirm": "Vẫn xóa", + "confirmations.delete.message": "Bạn có chắc muốn xóa tút này?", + "confirmations.delete.title": "Xóa tút", + "confirmations.delete_list.confirm": "Vẫn xóa", + "confirmations.delete_list.message": "Bạn có chắc muốn xóa vĩnh viễn danh sách này?", + "confirmations.delete_list.title": "Xóa danh sách", "confirmations.discard_edit_media.confirm": "Bỏ qua", "confirmations.discard_edit_media.message": "Bạn chưa lưu thay đổi đối với phần mô tả hoặc bản xem trước của media, vẫn bỏ luôn?", "confirmations.edit.confirm": "Sửa", "confirmations.edit.message": "Nội dung tút cũ sẽ bị ghi đè, bạn có tiếp tục?", - "confirmations.edit.title": "Viết đè lên tút cũ?", + "confirmations.edit.title": "Viết đè lên tút cũ", "confirmations.logout.confirm": "Đăng xuất", - "confirmations.logout.message": "Bạn có thật sự muốn thoát?", - "confirmations.logout.title": "Đăng xuất?", + "confirmations.logout.message": "Bạn có chắc muốn thoát?", + "confirmations.logout.title": "Đăng xuất", "confirmations.mute.confirm": "Ẩn", "confirmations.redraft.confirm": "Xóa & viết lại", - "confirmations.redraft.message": "Bạn thật sự muốn xóa tút và viết lại? Điều này sẽ xóa mất những lượt thích và đăng lại của tút, cũng như những trả lời sẽ không còn nội dung gốc.", - "confirmations.redraft.title": "Xóa & viết lại?", + "confirmations.redraft.message": "Điều này sẽ khiến những lượt thích và đăng lại của tút bị mất, cũng như những trả lời sẽ không còn nội dung gốc.", + "confirmations.redraft.title": "Xóa & viết lại", "confirmations.reply.confirm": "Trả lời", "confirmations.reply.message": "Nội dung bạn đang soạn thảo sẽ bị ghi đè, bạn có tiếp tục?", - "confirmations.reply.title": "Viết đè lên tút cũ?", + "confirmations.reply.title": "Viết đè lên tút cũ", "confirmations.unfollow.confirm": "Bỏ theo dõi", - "confirmations.unfollow.message": "Bạn thật sự muốn bỏ theo dõi {name}?", - "confirmations.unfollow.title": "Bỏ theo dõi?", + "confirmations.unfollow.message": "Bạn có chắc muốn bỏ theo dõi {name}?", + "confirmations.unfollow.title": "Bỏ theo dõi", "content_warning.hide": "Ẩn tút", "content_warning.show": "Nhấn để xem", "conversation.delete": "Xóa tin nhắn này", @@ -227,15 +227,15 @@ "domain_pill.activitypub_like_language": "ActivityPub giống như ngôn ngữ Mastodon giao tiếp với các mạng xã hội khác.", "domain_pill.server": "Máy chủ", "domain_pill.their_handle": "Địa chỉ Mastodon:", - "domain_pill.their_server": "Ngôi nhà kỹ thuật số, nơi lưu giữ tút của ai đó.", - "domain_pill.their_username": "Danh tính duy nhất của họ trên máy chủ này. Có thể có tên người dùng giống nhau trên các máy chủ khác.", + "domain_pill.their_server": "Nơi lưu trữ tút của người này.", + "domain_pill.their_username": "Độc nhất trên máy chủ này. Những máy chủ khác có thể cũng có tên người dùng giống vậy.", "domain_pill.username": "Tên người dùng", "domain_pill.whats_in_a_handle": "Địa chỉ Mastodon là gì?", "domain_pill.who_they_are": "Vì địa chỉ Mastodon cho biết một người là ai và họ ở đâu, nên bạn có thể tương tác với mọi người trên các nền tảng có .", "domain_pill.who_you_are": "Vì địa chỉ Mastodon cho biết bạn là ai và bạn ở đâu, nên bạn có thể tương tác với mọi người trên các nền tảng có .", "domain_pill.your_handle": "Địa chỉ Mastodon của bạn:", - "domain_pill.your_server": "Ngôi nhà kỹ thuật số, nơi lưu giữ tút của bạn. Không thích ở đây? Chuyển sang máy chủ khác và mang theo người theo dõi của bạn.", - "domain_pill.your_username": "Danh tính duy nhất của bạn trên máy chủ này. Có thể có tên người dùng giống bạn trên các máy chủ khác.", + "domain_pill.your_server": "Nơi lưu trữ tút của bạn. Không thích ở đây? Chuyển sang máy chủ khác và giữ nguyên người theo dõi của bạn.", + "domain_pill.your_username": "Chỉ riêng bạn trên máy chủ này. Những máy chủ khác có thể cũng có tên người dùng giống vậy.", "embed.instructions": "Sao chép đoạn mã dưới đây và chèn vào trang web của bạn.", "embed.preview": "Nó sẽ hiển thị như vầy:", "emoji_button.activity": "Hoạt động", @@ -359,7 +359,7 @@ "hints.profiles.see_more_followers": "Xem thêm người theo dõi ở {domain}", "hints.profiles.see_more_follows": "Xem thêm người mà người này theo dõi ở {domain}", "hints.profiles.see_more_posts": "Xem thêm tút ở {domain}", - "hints.threads.replies_may_be_missing": "Lượt trả lời trên máy chủ khác có thể không đầy đủ.", + "hints.threads.replies_may_be_missing": "Lượt trả lời từ máy chủ khác có thể không đầy đủ.", "hints.threads.see_more": "Xem thêm ở {domain}", "home.column_settings.show_reblogs": "Hiện những lượt đăng lại", "home.column_settings.show_replies": "Hiện những tút dạng trả lời", @@ -432,12 +432,12 @@ "keyboard_shortcuts.unfocus": "đưa con trỏ ra khỏi ô soạn thảo hoặc ô tìm kiếm", "keyboard_shortcuts.up": "di chuyển lên trên danh sách", "lightbox.close": "Đóng", - "lightbox.compress": "Thu nhỏ hình", - "lightbox.expand": "Phóng to hình", "lightbox.next": "Tiếp", "lightbox.previous": "Trước", + "lightbox.zoom_in": "Kích cỡ gốc", + "lightbox.zoom_out": "Vừa màn hình", "limited_account_hint.action": "Vẫn cứ xem", - "limited_account_hint.title": "Người này đã bị ẩn bởi quản trị viên của {domain}.", + "limited_account_hint.title": "Người này đã bị ẩn bởi quản trị viên {domain}.", "link_preview.author": "Bởi {name}", "link_preview.more_from_author": "Thêm từ {name}", "link_preview.shares": "{count, plural, other {{counter} lượt chia sẻ}}", @@ -533,7 +533,7 @@ "notification.relationships_severance_event.learn_more": "Tìm hiểu thêm", "notification.relationships_severance_event.user_domain_block": "Bạn đã chặn {target}, xóa {followersCount} người theo dõi bạn và {followingCount, plural, other {# người}} theo dõi bạn.", "notification.status": "{name} đăng tút mới", - "notification.update": "{name} đã sửa tút", + "notification.update": "{name} cập nhật tút", "notification_requests.accept": "Chấp nhận", "notification_requests.accept_multiple": "{count, plural, other {Duyệt # yêu cầu…}}", "notification_requests.confirm_accept_multiple.button": "{count, plural, other {Yêu cầu cần duyệt}}", @@ -554,7 +554,7 @@ "notification_requests.title": "Thông báo đã lọc", "notification_requests.view": "Hiện thông báo", "notifications.clear": "Xóa hết thông báo", - "notifications.clear_confirmation": "Bạn thật sự muốn xóa vĩnh viễn tất cả thông báo của mình?", + "notifications.clear_confirmation": "Bạn có chắc muốn xóa vĩnh viễn tất cả thông báo của mình?", "notifications.clear_title": "Xóa hết thông báo?", "notifications.column_settings.admin.report": "Báo cáo mới:", "notifications.column_settings.admin.sign_up": "Người mới tham gia:", @@ -621,7 +621,7 @@ "onboarding.profile.display_name_hint": "Tên đầy đủ hoặc biệt danh đều được…", "onboarding.profile.lead": "Bạn có thể cài đặt lại trong phần cài đặt, nơi thậm chí còn có nhiều tùy chọn hơn.", "onboarding.profile.note": "Giới thiệu", - "onboarding.profile.note_hint": "Bạn có thể @nhắnriêng ai đó hoặc #hashtags…", + "onboarding.profile.note_hint": "Bạn có thể @aiđó hoặc #hashtags…", "onboarding.profile.save_and_continue": "Lưu và tiếp tục", "onboarding.profile.title": "Thiết lập hồ sơ", "onboarding.profile.upload_avatar": "Tải lên ảnh đại diện", @@ -657,7 +657,7 @@ "poll.voted": "Bạn đã bình chọn rồi", "poll.votes": "{votes, plural, other {# lượt bình chọn}}", "poll_button.add_poll": "Tạo bình chọn", - "poll_button.remove_poll": "Hủy cuộc bình chọn", + "poll_button.remove_poll": "Xóa bình chọn", "privacy.change": "Chọn kiểu tút", "privacy.direct.long": "Những người được nhắc trong tút", "privacy.direct.short": "Người cụ thể", @@ -738,7 +738,7 @@ "report_notification.categories.violation": "Vi phạm nội quy", "report_notification.categories.violation_sentence": "vi phạm nội quy", "report_notification.open": "Mở báo cáo", - "search.no_recent_searches": "Không có tìm kiếm gần đây", + "search.no_recent_searches": "Gần đây chưa tìm gì", "search.placeholder": "Tìm kiếm", "search.quick_action.account_search": "Người có tên {x}", "search.quick_action.go_to_account": "Xem trang {x}", @@ -748,12 +748,12 @@ "search.search_or_paste": "Tìm kiếm hoặc nhập URL", "search_popout.full_text_search_disabled_message": "Không khả dụng trên {domain}.", "search_popout.full_text_search_logged_out_message": "Cần đăng nhập trước.", - "search_popout.language_code": "Mã ngôn ngữ ISO", - "search_popout.options": "Tùy chọn tìm kiếm", + "search_popout.language_code": "mã ngôn ngữ ISO", + "search_popout.options": "Tìm nâng cao", "search_popout.quick_actions": "Thao tác nhanh", - "search_popout.recent": "Tìm kiếm gần đây", + "search_popout.recent": "Bạn đã tìm", "search_popout.specific_date": "ngày cụ thể", - "search_popout.user": "mọi người", + "search_popout.user": "địa chỉ Mastodon", "search_results.accounts": "Mọi người", "search_results.all": "Toàn bộ", "search_results.hashtags": "Hashtag", @@ -789,7 +789,7 @@ "status.edited_x_times": "Đã sửa {count, plural, other {{count} lần}}", "status.embed": "Lấy mã nhúng", "status.favourite": "Thích", - "status.favourites": "{count, plural, other {lượt thích}}", + "status.favourites": "{count, plural, other {Thích}}", "status.filter": "Lọc tút này", "status.history.created": "{name} đăng {date}", "status.history.edited": "{name} đã sửa {date}", @@ -808,7 +808,7 @@ "status.reblog": "Đăng lại", "status.reblog_private": "Đăng lại (Riêng tư)", "status.reblogged_by": "{name} đăng lại", - "status.reblogs": "{count, plural, other {đăng lại}}", + "status.reblogs": "{count, plural, other {Đăng lại}}", "status.reblogs.empty": "Tút này chưa có ai đăng lại. Nếu có, nó sẽ hiển thị ở đây.", "status.redraft": "Xóa và viết lại", "status.remove_bookmark": "Bỏ lưu", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index 6df3cde208..a7cd089948 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "取消输入/搜索", "keyboard_shortcuts.up": "在列表中让光标上移", "lightbox.close": "关闭", - "lightbox.compress": "返回图片全览", - "lightbox.expand": "放大查看图片", "lightbox.next": "下一个", "lightbox.previous": "上一个", + "lightbox.zoom_in": "缩放为实际大小", + "lightbox.zoom_out": "缩放到适合窗口大小", "limited_account_hint.action": "仍要显示个人资料", "limited_account_hint.title": "此账号资料已被 {domain} 管理员隐藏。", "link_preview.author": "由 {name}", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index 6fc1b9d267..22f51a8413 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -398,8 +398,6 @@ "keyboard_shortcuts.unfocus": "把標示移離文字輸入和搜索", "keyboard_shortcuts.up": "在列表往上移動", "lightbox.close": "關閉", - "lightbox.compress": "縮小檢視", - "lightbox.expand": "擴大檢視", "lightbox.next": "下一頁", "lightbox.previous": "上一頁", "limited_account_hint.action": "一律顯示個人檔案", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index d69f54f7c8..7f2caeaf4b 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -432,10 +432,10 @@ "keyboard_shortcuts.unfocus": "跳離文字撰寫區塊或搜尋框", "keyboard_shortcuts.up": "向上移動", "lightbox.close": "關閉", - "lightbox.compress": "折疊圖片檢視框", - "lightbox.expand": "展開圖片檢視框", "lightbox.next": "下一步", "lightbox.previous": "上一步", + "lightbox.zoom_in": "縮放至實際大小", + "lightbox.zoom_out": "縮放至合適大小", "limited_account_hint.action": "一律顯示個人檔案", "limited_account_hint.title": "此個人檔案已被 {domain} 的管理員隱藏。", "link_preview.author": "來自 {name}", diff --git a/config/locales/devise.kab.yml b/config/locales/devise.kab.yml index 20249aae94..32a98a37c3 100644 --- a/config/locales/devise.kab.yml +++ b/config/locales/devise.kab.yml @@ -11,9 +11,9 @@ kab: invalid: Tella tuccḍa deg %{authentication_keys} neγ deg wawal uffir. 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. + 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. + pending: Amiḍan-inek·inem 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ḍ send ad tkemmelḍ. unconfirmed: Ilaq ad wekdeḍ tansa-inek·inem imayl send ad tkemmelḍ. diff --git a/config/locales/doorkeeper.kab.yml b/config/locales/doorkeeper.kab.yml index 13d8d93258..fa9e1c540a 100644 --- a/config/locales/doorkeeper.kab.yml +++ b/config/locales/doorkeeper.kab.yml @@ -125,10 +125,10 @@ kab: title: Tlaq tsiregt n OAuth scopes: admin:read: ad iɣeṛ akk isefka ɣef uqeddac - admin:write: ẓreg akk isefka γef uqeddac - follow: beddel assaγen n umiḍan - push: ṭṭef-d alɣuten-ik·im yettwademren - read: γeṛ akk isefka n umiḍan-ik + admin:write: ad iẓreg akk isefka ɣef uqeddac + follow: ad ibeddel assaɣen n umiḍan + push: ad iṭṭef-d alɣuten-ik·im yettwademren + read: ad iɣeṛ akk isefka n umiḍan-ik·im read:accounts: ẓer isallen n yimiḍanen read:blocks: ẓer imiḍanen i tesḥebseḍ read:bookmarks: ẓer ticraḍ-ik @@ -139,10 +139,10 @@ kab: read:notifications: ad iẓer alɣuten-inek·inem read:reports: ẓer ineqqisen-ik·im read:search: anadi deg umkan-ik·im - read:statuses: ẓer meṛṛa tisuffaɣ + read:statuses: ad iẓer meṛṛa tisuffaɣ write: beddel meṛṛa isefka n umiḍan-ik write:accounts: ad iẓreg amaɣnu-ik·im - write:blocks: seḥbes imiḍanen d tγula + write:blocks: ad iseḥbes imiḍanen akked tɣula write:bookmarks: ad yernu tisuffaɣ ɣer ticraḍ write:filters: ad isnulfu imsizedgen write:follows: ḍfeṛ imdanen diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml index 8aac7cbb47..0325757dca 100644 --- a/config/locales/es-MX.yml +++ b/config/locales/es-MX.yml @@ -1203,7 +1203,7 @@ es-MX: caches: El contenido que ha sido almacenado en caché por otros servidores puede persistir data_removal: Tus publicaciones y el resto de datos se eliminarán definitivamente email_change_html: Puedes cambiar tu dirección de correo electrónico sin eliminar tu cuenta - email_contact_html: Si aún no te ha llegado, puedes escribir a %{email} para pedir ayuda + email_contact_html: Si todavía no te ha llegado, puedes escribir a %{email} para pedir ayuda email_reconfirmation_html: Si no te ha llegado el correo de confirmación, puedes volver a solicitarlo irreversible: No podrás restaurar ni reactivar tu cuenta more_details_html: Para más detalles, ver la política de privacidad. diff --git a/config/locales/ja.yml b/config/locales/ja.yml index ed6293e1ca..cdc1fd18a8 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -22,6 +22,8 @@ ja: admin: account_actions: action: アクションを実行 + already_silenced: すでに制限されているアカウントです。 + already_suspended: すでに停止されているアカウントです。 title: "%{acct}さんに対してアクションを実行" account_moderation_notes: create: 書き込む @@ -589,6 +591,7 @@ ja: suspend_description_html: アカウントとそのすべての内容にアクセスできなくなり、最終的に削除され、やり取りは不可能になります。 30日以内であれば元に戻すことができます。このアカウントに対するすべての通報をクローズします。 actions_description_html: このレポートへのアクションを決定してください。アカウントへ懲罰的な措置を取った場合、Spanカテゴリが選択されている場合を除き、メール通知が送信されます。 actions_description_remote_html: この通報を解決するためのアクションを選択してください。これはあなたのサーバーがこのリモートアカウントと通信し、そのコンテンツを処理する時のみ影響します。 + actions_no_posts: 削除対象となる投稿がレポートに含まれていません add_to_report: 通報にさらに追加 already_suspended_badges: local: このサーバーで停止済み diff --git a/config/locales/kab.yml b/config/locales/kab.yml index 7044983ac9..88193f8434 100644 --- a/config/locales/kab.yml +++ b/config/locales/kab.yml @@ -222,8 +222,8 @@ kab: shortcode: Tangalt tawezlant title: Imujiten udmawanen uncategorized: War-taggayt - unlist: Kkes seg wumuγ - unlisted: Yettwakkes seg wumuγ + unlist: Kkes seg wumuɣ + unlisted: Yettwakkes seg wumuɣ update_failed_msg: Ur izmir ara ad-issali umuji-a upload: Sali dashboard: @@ -710,8 +710,8 @@ kab: privacy_policy: title: Tasertit tabaḍnit redirects: - prompt: Ma tumneḍ aseɣwen-a, sit fell-as akken ad tkemmleḍ. - title: Aql-ik·em ad teffɣeḍ seg %{instance}. + prompt: Ma tumneḍ asaɣ-a, sit fell-as akken ad tkemmleḍ. + title: Ad teffɣeḍ seg %{instance}. relationships: activity: Armud n umiḍan followers: Imeḍfaṛen diff --git a/config/locales/lad.yml b/config/locales/lad.yml index 3d1f1a61e2..4136877ede 100644 --- a/config/locales/lad.yml +++ b/config/locales/lad.yml @@ -874,8 +874,12 @@ lad: message_html: "Tu magazinaje de objektos es mal konfigurado. La privasita de tus utilizadores esta en riziko." tags: moderation: + pending_review: Revizion esta asperando title: Estado + usable: Uzavle name: Nombre + newest: Mas muevos + oldest: Mas viejos reset: Reinisya review: Estado de revizion search: Bushka @@ -1027,7 +1031,9 @@ lad: guide_link_text: Todos pueden kontribuir. sensitive_content: Kontenido sensivle application_mailer: + notification_preferences: Troka preferensyas de posta salutation: "%{name}," + settings: 'Troka preferensyas de posta: %{link}' unsubscribe: Dezabona view: 'Mira:' view_profile: Ve profil @@ -1047,6 +1053,7 @@ lad: hint_html: Una koza mas! Tenemos ke konfirmar ke eres umano (para evitar spam!). Rezolve el CAPTCHA abasho i klika "Kontinua". title: Kontrolo de sigurita confirmations: + awaiting_review: Tu adreso de posta tiene sido konfirmado! La taifa de %{domain} esta revizando tu enrejistrasyon. Risiviras un meil si acheten tu kuento! awaiting_review_title: Estamos revizando tu enrejistramiento clicking_this_link: klikando en este atadijo login_link: konektate kon kuento @@ -1054,6 +1061,7 @@ lad: redirect_to_app_html: Seras readresado a la aplikasyon %{app_name}. Si esto no afita, aprova %{clicking_this_link} o regresa manualmente a la aplikasyon. registration_complete: Tu enrejistrasyon en %{domain} ya esta kompletada! welcome_title: Bienvenido, %{name}! + wrong_email_hint: Si este adreso de posta es inkorekto, puedes trokarlo en las preferensyas del kuento. delete_account: Efasa kuento delete_account_html: Si keres supremir tu kuento, puedes ir aki. Seras pedido de una konfirmasyon. description: @@ -1096,8 +1104,10 @@ lad: security: Sigurita set_new_password: Establese muevo kod setup: + email_below_hint_html: Mira en tu kuti de spam o solisita de muevo. Si el adreso de posta elektronika ke aparese aki es yerrado, puedes trokarlo aki. email_settings_hint_html: Klika el atadjiko ke te embimos para verifikar %{email}. Asperaremos aki. link_not_received: No risivites un atadijo? + new_confirmation_instructions_sent: Resiviras un muevo mesaj de posta elektronika kon el atadjio de konfirmasyon en unos minutos! title: Reviza tu kuti de arivo sign_in: preamble_html: Konektate kon tus kredensiales de %{domain}. Si tu kuento esta balabayado en otruno servidor, no puedras konektarte aki. @@ -1109,11 +1119,14 @@ lad: status: account_status: Estado del kuento functional: Tu kuento esta kompletamente funksyonal. + pending: Tu solisitasyon esta asperando la revizion por muestros administradores. Esto puede tadrar algun tiempo. Arisiviras una posta elektronika si la solisitasyon sea achetada. redirecting_to: Tu kuento se topa inaktivo porke esta siendo readresado a %{acct}. self_destruct: Deke %{domain} va a serrarse, solo tendras akseso limitado a tu kuento. view_strikes: Ve amonestamientos pasados kontra tu kuento too_fast: Formulario enviado demaziado rapido, aprovalo de muevo. use_security_key: Uza la yave de sigurita + author_attribution: + more_from_html: Mas de %{name} challenge: confirm: Kontinua hint_html: "Konsejo: No retornaremos a demandarte por el kod durante la sigiente ora." @@ -1150,6 +1163,9 @@ lad: before: 'Antes de kontinuar, por favor melda kon atensyon las sigientes notas:' caches: El kontenido ke tiene sido magazinado en kashe por otros sirvidores puede persistir data_removal: Tus publikasyones i el resto de datos se supremiran definitivamente + email_change_html: Puedes trokar tu adreso de posta elektronika sin supremir tu kuento + email_contact_html: Si ainda no te tiene parvenido, puedes eskrivir a %{email} para pider ayuda + email_reconfirmation_html: Si no te tiene parvenido la posta de konfirmasyon, puedes retornar a solisitarlo irreversible: No podras restaurar ni reaktivar tu kuento more_details_html: Para mas detalyos, ver la politika de privasita. username_available: Tu nombre de utilizador retornara a estar desponivle @@ -1380,6 +1396,7 @@ lad: authentication_methods: otp: aplikasyon de autentifikasyon en dos pasos password: kod + sign_in_token: kodiche de sigurita por posta elektronika webauthn: yaves de sigurita description_html: Si ves una aktivita ke no rekoneses, konsidera trokar tu kod i kapasitar la autentifikasyon en dos pasos. empty: No ay estoria de autentifikasyon desponivle @@ -1390,6 +1407,7 @@ lad: unsubscribe: action: Si, dezabona complete: Dezabonado + confirmation_html: Estas siguro de ke ya no keres risivir %{type} de Mastodon en %{domain} a tu posta elektronika %{email}? Syempre podras reabonarte dizde las opsyones de avizos por posta.. emails: notification_emails: favourite: avizos de favoritos por posta @@ -1864,6 +1882,7 @@ lad: invalid_otp_token: Kodiche de dos pasos no valido otp_lost_help_html: Si pedriste akseso a los dos, puedes kontaktarte kon %{email} rate_limited: Demaziadas provas de autentifikasyon, aprova de muevo dempues. + seamless_external_login: Estas konektado por un servisyo eksterno i estonses la konfigurasyon de kod i konto de posta no estan disponivles. signed_in_as: 'Konektado komo:' verification: extra_instructions_html: Konsejo: El atadijo en tu web puede ser invizivle. La parte importante es rel="me", ke evita la suplantasyon de identita en sitios kon kontenido jenerado por el utilizador. Puedes inkluzo uzar una etiketa atadijo en la kavesera de la pajina en vez de a, ama el HTML deve ser aksesivle sin exekutar JavaScript. diff --git a/config/locales/simple_form.kab.yml b/config/locales/simple_form.kab.yml index 203b023715..f390d42132 100644 --- a/config/locales/simple_form.kab.yml +++ b/config/locales/simple_form.kab.yml @@ -92,7 +92,7 @@ kab: setting_display_media_default: Akk-a kan setting_display_media_hide_all: Ffer-iten akk setting_display_media_show_all: Sken-iten-id akk - setting_hide_network: Ffer azetta-k·m + setting_hide_network: Ffer azetta-k·m inmetti setting_theme: Asental n wesmel setting_use_pending_items: Askar aleɣwayan sign_in_token_attempt: Tangalt n tɣellist diff --git a/config/locales/simple_form.lad.yml b/config/locales/simple_form.lad.yml index 2a381534ba..94e387107a 100644 --- a/config/locales/simple_form.lad.yml +++ b/config/locales/simple_form.lad.yml @@ -237,6 +237,7 @@ lad: warn: Eskonde kon una avertensya form_admin_settings: activity_api_enabled: Publika estatistikas adjustadas sovre la aktivita del utilizador kon la API + app_icon: Ikono de aplikasyon backups_retention_period: Periodo de retensyon de la dosya de utilizador bootstrap_timeline_accounts: Rekomenda siempre estos kuentos a muevos utilizadores closed_registrations_message: Mesaj personalizado kuando las enrejistrasyones no estan desponivles diff --git a/config/locales/simple_form.vi.yml b/config/locales/simple_form.vi.yml index eab025c665..e675209017 100644 --- a/config/locales/simple_form.vi.yml +++ b/config/locales/simple_form.vi.yml @@ -4,12 +4,12 @@ vi: hints: account: attribution_domains_as_text: Bảo vệ khỏi những sự gán ghép sai. - discoverable: Các tút và hồ sơ công khai của bạn có thể được giới thiệu hoặc đề xuất ở nhiều khu vực khác nhau của Mastodon và hồ sơ của bạn có thể được đề xuất cho những người dùng khác. + discoverable: Hồ sơ và tút công khai của bạn được đề xuất cho những người dùng Mastodon khác. display_name: Tên đầy đủ hoặc biệt danh đều được. fields: Trang blog của bạn, nghề nghiệp, tuổi hoặc bất cứ thứ gì. - indexable: Tút công khai của bạn sẽ xuất hiện khi tìm kiếm trên Mastodon. Những người đã tương tác với tút của bạn có thể tìm kiếm chúng. - note: 'Bạn có thể @nhắnriêng ai đó hoặc #hashtags.' - show_collections: Mọi người sẽ biết những bạn theo dõi và người theo dõi bạn. Những người bạn theo dõi sẽ vẫn thấy rằng bạn theo dõi họ. + indexable: Mọi người có thể tìm kiếm và tương tác với những tút công khai của bạn trên Mastodon. + note: 'Bạn có thể @aiđó hoặc #hashtags.' + show_collections: Mọi người sẽ biết những bạn theo dõi và người theo dõi bạn. unlocked: Mọi người sẽ theo dõi bạn mà không cần bạn cho phép. account_alias: acct: Nhập tên_người_dùng@máy chủ của tài khoản cũ @@ -214,7 +214,7 @@ vi: 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_disable_swiping: Tắt thao tác vuốt setting_display_media: Media nhạy cảm setting_display_media_default: Mặc định setting_display_media_hide_all: Ẩn toàn bộ @@ -311,7 +311,7 @@ vi: text: Nội quy settings: indexable: Cho phép hiện hồ sơ trong công cụ tìm kiếm - show_application: Cho phép hiện ứng dụng dùng để đăng tút + show_application: Hiện ứng dụng dùng để đăng tút tag: listable: Cho phép xuất hiện trong tìm kiếm và đề xuất name: Hashtag diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 98696aef7c..83531c6561 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -1037,7 +1037,7 @@ vi: remove: Bỏ liên kết bí danh appearance: advanced_web_interface: Bố cục - advanced_web_interface_hint: Bố cục nhiều cột cho phép bạn chuyển bố cục hiển thị thành nhiều cột khác nhau. Thích hợp với màn hình rộng. + advanced_web_interface_hint: Chia giao diện thành nhiều cột, phù hợp với màn hình rộng. animations_and_accessibility: Hiệu ứng confirmation_dialogs: Hộp thoại xác nhận discovery: Khám phá @@ -1262,7 +1262,7 @@ vi: add_new: Thêm mới errors: limit: Bạn đã đạt tới số lượng hashtag tối đa - hint_html: "Làm nổi bật những hashtag thường dùng. Một công cụ tuyệt vời để theo dõi các tác phẩm sáng tạo và dự án dài hạn của bạn, các hashtag thường dùng sẽ hiển thị nổi bật trên hồ sơ của bạn và cho phép truy cập nhanh vào các tút." + hint_html: "Làm nổi bật những hashtag thường dùng. Một công cụ tuyệt vời để theo dõi các tác phẩm sáng tạo và dự án dài hạn của bạn, cũng như giúp truy cập nhanh vào các tút." filters: contexts: account: Trang hồ sơ @@ -1555,13 +1555,13 @@ vi: posting_defaults: Mặc định cho tút public_timelines: Bảng tin privacy: - hint_html: "Tùy chỉnh cách mọi người tìm thấy hồ sơ và các tút của bạn. Nhiều tính năng trong Mastodon có thể giúp bạn tiếp cận nhiều đối tượng hơn khi được bật. Hãy xem lại các cài đặt này để đảm bảo chúng phù hợp với bạn." + hint_html: "Tùy chỉnh cách mọi người tìm thấy hồ sơ và tút của bạn. Hãy đảm bảo các thiết lập sau phù hợp với bạn." privacy: Riêng tư privacy_hint_html: Kiểm soát mức độ chi tiết bạn muốn tiết lộ. Mọi người khám phá các hồ sơ thú vị và các ứng dụng thú vị bằng cách theo dõi những người khác và xem họ đăng từ ứng dụng nào, nhưng có thể bạn muốn ẩn nó đi. reach: Tiếp cận reach_hint_html: Kiểm soát cách bạn được khám phá và theo dõi. Bạn có muốn tút của mình xuất hiện trên màn hình Khám phá không? Bạn có muốn người khác nhìn thấy bạn trong các đề xuất theo dõi của họ không? Bạn muốn tự động chấp nhận tất cả những người theo dõi mới hay tự duyệt từng người theo dõi? search: Tìm kiếm - search_hint_html: Kiểm soát cách tìm thấy bạn. Bạn có muốn mọi người tìm thấy bạn bằng những gì bạn đã đăng công khai không? Bạn có muốn những người bên ngoài Mastodon tìm thấy hồ sơ của bạn khi tìm kiếm trên web không? Xin lưu ý rằng không thể đảm bảo loại trừ hoàn toàn khỏi tất cả các công cụ tìm kiếm đối với thông tin công khai. + search_hint_html: Kiểm soát cách tìm thấy bạn. Bạn có muốn mọi người tìm thấy bạn bằng những gì bạn đã đăng công khai không? Bạn có muốn những người bên ngoài Mastodon tìm thấy hồ sơ của bạn khi tìm kiếm trên web không? Lưu ý là không thể đảm bảo tránh khỏi các công cụ tìm kiếm đối với thông tin công khai. title: Riêng tư và tiếp cận privacy_policy: title: Chính sách bảo mật @@ -1675,7 +1675,7 @@ vi: preferences: Chung profile: Hồ sơ relationships: Quan hệ - severed_relationships: Mối quan hệ bị cắt đứt + severed_relationships: Quan hệ bị cắt đứt statuses_cleanup: Tự động xóa tút cũ strikes: Lần cảnh cáo two_factor_authentication: Xác minh 2 bước @@ -1688,7 +1688,7 @@ vi: user_domain_block: Bạn đã chặn %{target_name} lost_followers: Mất người theo dõi lost_follows: Mất người đang theo dõi - preamble: Bạn có thể mất số lượt theo dõi và người theo dõi khi chặn một máy chủ hoặc khi kiểm duyệt viên của bạn quyết định tạm dừng máy chủ từ xa. Khi điều đó xảy ra, bạn sẽ có thể tải xuống danh sách các mối quan hệ đã bị cắt đứt, để kiểm tra và nhập vào máy chủ khác. + preamble: Khi bạn hoặc kiểm duyệt viên máy chủ của bạn chặn một máy chủ, người bạn đang theo dõi và người theo dõi bạn có thể sẽ bị mất. Khi điều đó xảy ra, hãy vào đây tải xuống danh sách các mối quan hệ đã bị cắt đứt, và nhập thủ công. purged: Thông tin về máy chủ này đã bị quản trị viên máy chủ của bạn xóa sạch. type: Sự kiện statuses: @@ -1769,7 +1769,7 @@ vi: contrast: Mastodon (Tương phản) default: Mastodon (Tối) mastodon-light: Mastodon (Sáng) - system: Tự động (chủ đề hệ thống) + system: Theo hệ thống time: formats: default: "%-d.%m.%Y %H:%M" @@ -1905,7 +1905,7 @@ vi: seamless_external_login: Bạn đã đăng nhập thông qua một dịch vụ bên ngoài, vì vậy mật khẩu và email không khả dụng. signed_in_as: 'Đăng nhập bằng:' verification: - extra_instructions_html: Mẹo: Liên kết trên trang web của bạn có thể ẩn. Phần quan trọng là rel="me" ngăn chặn việc mạo danh trên các trang web có nội dung do người dùng tạo. Bạn thậm chí có thể sử dụng một thẻ link trên header của trang thay vì a, nhưng HTML phải có thể truy cập được mà không cần thực thi JavaScript. + extra_instructions_html: Mẹo: Có thể ẩn liên kết trên trang web của bạn. Quan trọng là rel="me" giúp ngăn chặn việc mạo danh trên các trang web có nội dung do người dùng tạo. Bạn cũng có thể sử dụng một thẻ link trên header của trang thay vì a, nhưng HTML phải có thể truy cập được mà không cần thực thi JavaScript. here_is_how: Cách thực hiện hint_html: "Xác minh danh tính trên Mastodon là dành cho tất cả mọi người. Dựa trên các tiêu chuẩn web mở, miễn phí bây giờ và mãi mãi. Tất cả những gì bạn cần là một trang web cá nhân để mọi người nhận ra bạn. Khi bạn liên kết đến trang web này từ hồ sơ của mình, chúng tôi sẽ kiểm tra xem trang web đó có liên kết lại với hồ sơ của bạn hay không và hiển thị một chỉ báo trực quan trên đó." instructions_html: Sao chép và dán mã bên dưới vào mã nguồn trang web của bạn. Sau đó, thêm địa chỉ trang web của bạn vào một trong các trường metadata trên hồ sơ của bạn từ tab "Sửa hồ sơ" và lưu thay đổi. @@ -1918,7 +1918,7 @@ vi: error: Có vấn đề khi thêm khóa bảo mật. Xin thử lại. success: Đã thêm khóa bảo mật mới thành công. delete: Xóa - delete_confirmation: Bạn thật sự muốn xóa khóa bảo mật này? + delete_confirmation: Bạn có chắc muốn xóa khóa bảo mật này? description_html: Nếu bạn kích hoạt khóa bảo mật, bạn sẽ cần dùng một trong những khóa bảo mật đó mỗi khi đăng nhập. destroy: error: Có vấn đề khi xóa khóa bảo mật. Xin thử lại. From 11eae691ba91c2e36ce7cbdab18bf5a60a4fb9c8 Mon Sep 17 00:00:00 2001 From: Tim Campbell Date: Mon, 23 Sep 2024 10:55:35 +0200 Subject: [PATCH 56/93] Feature more otel customization (#31998) --- config/initializers/opentelemetry.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/config/initializers/opentelemetry.rb b/config/initializers/opentelemetry.rb index d121a95a36..8edce03b90 100644 --- a/config/initializers/opentelemetry.rb +++ b/config/initializers/opentelemetry.rb @@ -56,12 +56,13 @@ if ENV.keys.any? { |name| name.match?(/OTEL_.*_ENDPOINT/) } }, }) - prefix = ENV.fetch('OTEL_SERVICE_NAME_PREFIX', 'mastodon') + prefix = ENV.fetch('OTEL_SERVICE_NAME_PREFIX', 'mastodon') + separator = ENV.fetch('OTEL_SERVICE_NAME_SEPARATOR', '/') c.service_name = case $PROGRAM_NAME - when /puma/ then "#{prefix}/web" + when /puma/ then "#{prefix}#{separator}web" else - "#{prefix}/#{$PROGRAM_NAME.split('/').last}" + "#{prefix}#{separator}#{$PROGRAM_NAME.split('/').last}" end c.service_version = Mastodon::Version.to_s end From 770ec9240a8ec4f9a554533821e8b9a60be71bdf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 09:02:16 +0000 Subject: [PATCH 57/93] Update Yarn to v4.5.0 (#31914) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- streaming/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ea0c0246cb..08fec76465 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@mastodon/mastodon", "license": "AGPL-3.0-or-later", - "packageManager": "yarn@4.4.1", + "packageManager": "yarn@4.5.0", "engines": { "node": ">=18" }, diff --git a/streaming/package.json b/streaming/package.json index 64947749a1..d573c9b284 100644 --- a/streaming/package.json +++ b/streaming/package.json @@ -1,7 +1,7 @@ { "name": "@mastodon/streaming", "license": "AGPL-3.0-or-later", - "packageManager": "yarn@4.4.1", + "packageManager": "yarn@4.5.0", "engines": { "node": ">=18" }, From 5d6a3f2cb07de926da007e855e24ee852dc17b91 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 23 Sep 2024 11:13:51 +0200 Subject: [PATCH 58/93] Update dependency google-protobuf (#32029) --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8fece04a6d..8ba091038d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -289,7 +289,7 @@ GEM raabro (~> 1.4) globalid (1.2.1) activesupport (>= 6.1) - google-protobuf (3.25.4) + google-protobuf (3.25.5) googleapis-common-protos-types (1.15.0) google-protobuf (>= 3.18, < 5.a) haml (6.3.0) From cd7b670cd8de145510091c5737f5ed158e16cd00 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 23 Sep 2024 05:18:04 -0400 Subject: [PATCH 59/93] Reduce factory creation in `User#reset_password!` spec (#32021) --- spec/models/user_spec.rb | 47 +++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 972453cd69..d28e6658f1 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -434,7 +434,9 @@ RSpec.describe User do end describe '#reset_password!' do - subject(:user) { Fabricate(:user, password: 'foobar12345') } + subject(:user) { Fabricate(:user, password: original_password) } + + let(:original_password) { 'foobar12345' } let!(:session_activation) { Fabricate(:session_activation, user: user) } let!(:access_token) { Fabricate(:access_token, resource_owner_id: user.id) } @@ -442,31 +444,40 @@ RSpec.describe User do let(:redis_pipeline_stub) { instance_double(Redis::Namespace, publish: nil) } - before do - allow(redis).to receive(:pipelined).and_yield(redis_pipeline_stub) - user.reset_password! + before { stub_redis } + + it 'changes the password immediately and revokes related access' do + expect { user.reset_password! } + .to remove_activated_sessions + .and remove_active_user_tokens + .and remove_user_web_subscriptions + + expect(user) + .to_not be_external_or_valid_password(original_password) + expect { session_activation.reload } + .to raise_error(ActiveRecord::RecordNotFound) + expect { web_push_subscription.reload } + .to raise_error(ActiveRecord::RecordNotFound) + expect(redis_pipeline_stub) + .to have_received(:publish).with("timeline:access_token:#{access_token.id}", Oj.dump(event: :kill)).once end - it 'changes the password immediately' do - expect(user.external_or_valid_password?('foobar12345')).to be false + def remove_activated_sessions + change(user.session_activations, :count).to(0) end - it 'deactivates all sessions' do - expect(user.session_activations.count).to eq 0 - expect { session_activation.reload }.to raise_error(ActiveRecord::RecordNotFound) + def remove_active_user_tokens + change { Doorkeeper::AccessToken.active_for(user).count }.to(0) end - it 'revokes all access tokens' do - expect(Doorkeeper::AccessToken.active_for(user).count).to eq 0 + def remove_user_web_subscriptions + change { Web::PushSubscription.where(user: user).or(Web::PushSubscription.where(access_token: access_token)).count }.to(0) end - it 'revokes streaming access for all access tokens' do - expect(redis_pipeline_stub).to have_received(:publish).with("timeline:access_token:#{access_token.id}", Oj.dump(event: :kill)).once - end - - it 'removes push subscriptions' do - expect(Web::PushSubscription.where(user: user).or(Web::PushSubscription.where(access_token: access_token)).count).to eq 0 - expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound) + def stub_redis + allow(redis) + .to receive(:pipelined) + .and_yield(redis_pipeline_stub) end end From 66ed7ea4b5a5c022da94eca9cb5931eb4151080d Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 23 Sep 2024 05:20:43 -0400 Subject: [PATCH 60/93] Move status creation to "with rss" context in accounts request spec (#32020) --- spec/requests/accounts_spec.rb | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/spec/requests/accounts_spec.rb b/spec/requests/accounts_spec.rb index e657ae6060..afd9ac80e2 100644 --- a/spec/requests/accounts_spec.rb +++ b/spec/requests/accounts_spec.rb @@ -46,21 +46,6 @@ RSpec.describe 'Accounts show response' do describe 'GET to short username paths' do context 'with existing statuses' do - let!(:status) { Fabricate(:status, account: account) } - let!(:status_reply) { Fabricate(:status, account: account, thread: Fabricate(:status)) } - let!(:status_self_reply) { Fabricate(:status, account: account, thread: status) } - let!(:status_media) { Fabricate(:status, account: account) } - let!(:status_pinned) { Fabricate(:status, account: account) } - let!(:status_private) { Fabricate(:status, account: account, visibility: :private) } - let!(:status_direct) { Fabricate(:status, account: account, visibility: :direct) } - let!(:status_reblog) { Fabricate(:status, account: account, reblog: Fabricate(:status)) } - - before do - status_media.media_attachments << Fabricate(:media_attachment, account: account, type: :image) - account.pinned_statuses << status_pinned - account.pinned_statuses << status_private - end - context 'with HTML' do let(:format) { 'html' } @@ -207,6 +192,21 @@ RSpec.describe 'Accounts show response' do context 'with RSS' do let(:format) { 'rss' } + let!(:status) { Fabricate(:status, account: account) } + let!(:status_reply) { Fabricate(:status, account: account, thread: Fabricate(:status)) } + let!(:status_self_reply) { Fabricate(:status, account: account, thread: status) } + let!(:status_media) { Fabricate(:status, account: account) } + let!(:status_pinned) { Fabricate(:status, account: account) } + let!(:status_private) { Fabricate(:status, account: account, visibility: :private) } + let!(:status_direct) { Fabricate(:status, account: account, visibility: :direct) } + let!(:status_reblog) { Fabricate(:status, account: account, reblog: Fabricate(:status)) } + + before do + status_media.media_attachments << Fabricate(:media_attachment, account: account, type: :image) + account.pinned_statuses << status_pinned + account.pinned_statuses << status_private + end + context 'with a normal account in an RSS request' do before do get short_account_path(username: account.username, format: format) From 447d0a3e880221973fc81840c9ba5e3cc6c06dcb Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 23 Sep 2024 05:27:53 -0400 Subject: [PATCH 61/93] Remove double no-records cases in `api/v1/admin` req specs (#32014) --- .../v1/admin/canonical_email_blocks_spec.rb | 22 ++++++++++--------- .../api/v1/admin/domain_allows_spec.rb | 19 ++++++++-------- .../api/v1/admin/domain_blocks_spec.rb | 22 ++++++++++--------- .../api/v1/admin/email_domain_blocks_spec.rb | 22 ++++++++++--------- spec/requests/api/v1/admin/ip_blocks_spec.rb | 22 ++++++++++--------- spec/requests/api/v1/admin/reports_spec.rb | 22 ++++++++++--------- spec/requests/api/v1/admin/tags_spec.rb | 20 +++++++++-------- 7 files changed, 81 insertions(+), 68 deletions(-) diff --git a/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb b/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb index eaa011d516..25af0a26af 100644 --- a/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb +++ b/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb @@ -20,19 +20,16 @@ RSpec.describe 'Canonical Email Blocks' do it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', 'Moderator' - it 'returns http success' do - subject - - expect(response).to have_http_status(200) - expect(response.content_type) - .to start_with('application/json') - end - context 'when there is no canonical email block' do it 'returns an empty list' do subject - expect(response.parsed_body).to be_empty + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) + .to be_empty end end @@ -43,7 +40,12 @@ RSpec.describe 'Canonical Email Blocks' do it 'returns the correct canonical email hashes' do subject - expect(response.parsed_body.pluck(:canonical_email_hash)).to match_array(expected_email_hashes) + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body.pluck(:canonical_email_hash)) + .to match_array(expected_email_hashes) end context 'with limit param' do diff --git a/spec/requests/api/v1/admin/domain_allows_spec.rb b/spec/requests/api/v1/admin/domain_allows_spec.rb index eb5128e420..fba1eb15d3 100644 --- a/spec/requests/api/v1/admin/domain_allows_spec.rb +++ b/spec/requests/api/v1/admin/domain_allows_spec.rb @@ -20,18 +20,14 @@ RSpec.describe 'Domain Allows' do it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', 'Moderator' - it 'returns http success' do - subject - - expect(response).to have_http_status(200) - expect(response.content_type) - .to start_with('application/json') - end - context 'when there is no allowed domains' do it 'returns an empty body' do subject + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') expect(response.parsed_body).to be_empty end end @@ -51,7 +47,12 @@ RSpec.describe 'Domain Allows' do it 'returns the correct allowed domains' do subject - expect(response.parsed_body).to match_array(expected_response) + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) + .to match_array(expected_response) end context 'with limit param' do diff --git a/spec/requests/api/v1/admin/domain_blocks_spec.rb b/spec/requests/api/v1/admin/domain_blocks_spec.rb index 1a506bd9be..029de72fd7 100644 --- a/spec/requests/api/v1/admin/domain_blocks_spec.rb +++ b/spec/requests/api/v1/admin/domain_blocks_spec.rb @@ -20,19 +20,16 @@ RSpec.describe 'Domain Blocks' do it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', 'Moderator' - it 'returns http success' do - subject - - expect(response).to have_http_status(200) - expect(response.content_type) - .to start_with('application/json') - end - context 'when there are no domain blocks' do it 'returns an empty list' do subject - expect(response.parsed_body).to be_empty + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) + .to be_empty end end @@ -66,7 +63,12 @@ RSpec.describe 'Domain Blocks' do it 'returns the expected domain blocks' do subject - expect(response.parsed_body).to match_array(expected_responde) + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) + .to match_array(expected_responde) end context 'with limit param' do diff --git a/spec/requests/api/v1/admin/email_domain_blocks_spec.rb b/spec/requests/api/v1/admin/email_domain_blocks_spec.rb index 3f51a3ea2d..2788a45a4a 100644 --- a/spec/requests/api/v1/admin/email_domain_blocks_spec.rb +++ b/spec/requests/api/v1/admin/email_domain_blocks_spec.rb @@ -21,19 +21,16 @@ RSpec.describe 'Email Domain Blocks' do it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', 'Moderator' - it 'returns http success' do - subject - - expect(response).to have_http_status(200) - expect(response.content_type) - .to start_with('application/json') - end - context 'when there is no email domain block' do it 'returns an empty list' do subject - expect(response.parsed_body).to be_empty + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) + .to be_empty end end @@ -44,7 +41,12 @@ RSpec.describe 'Email Domain Blocks' do it 'return the correct blocked email domains' do subject - expect(response.parsed_body.pluck(:domain)).to match_array(blocked_email_domains) + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body.pluck(:domain)) + .to match_array(blocked_email_domains) end context 'with limit param' do diff --git a/spec/requests/api/v1/admin/ip_blocks_spec.rb b/spec/requests/api/v1/admin/ip_blocks_spec.rb index c096aa3328..aa3db33915 100644 --- a/spec/requests/api/v1/admin/ip_blocks_spec.rb +++ b/spec/requests/api/v1/admin/ip_blocks_spec.rb @@ -20,19 +20,16 @@ RSpec.describe 'IP Blocks' do it_behaves_like 'forbidden for wrong role', '' it_behaves_like 'forbidden for wrong role', 'Moderator' - it 'returns http success' do - subject - - expect(response).to have_http_status(200) - expect(response.content_type) - .to start_with('application/json') - end - context 'when there is no ip block' do it 'returns an empty body' do subject - expect(response.parsed_body).to be_empty + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) + .to be_empty end end @@ -60,7 +57,12 @@ RSpec.describe 'IP Blocks' do it 'returns the correct blocked ips' do subject - expect(response.parsed_body).to match_array(expected_response) + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) + .to match_array(expected_response) end context 'with limit param' do diff --git a/spec/requests/api/v1/admin/reports_spec.rb b/spec/requests/api/v1/admin/reports_spec.rb index a0e7b0a369..987f0eda7f 100644 --- a/spec/requests/api/v1/admin/reports_spec.rb +++ b/spec/requests/api/v1/admin/reports_spec.rb @@ -19,19 +19,16 @@ RSpec.describe 'Reports' do it_behaves_like 'forbidden for wrong scope', 'write:statuses' it_behaves_like 'forbidden for wrong role', '' - it 'returns http success' do - subject - - expect(response).to have_http_status(200) - expect(response.content_type) - .to start_with('application/json') - end - context 'when there are no reports' do it 'returns an empty list' do subject - expect(response.parsed_body).to be_empty + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) + .to be_empty end end @@ -66,7 +63,12 @@ RSpec.describe 'Reports' do it 'returns all unresolved reports' do subject - expect(response.parsed_body).to match_array(expected_response) + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) + .to match_array(expected_response) end context 'with resolved param' do diff --git a/spec/requests/api/v1/admin/tags_spec.rb b/spec/requests/api/v1/admin/tags_spec.rb index 696a11da67..3a57432af7 100644 --- a/spec/requests/api/v1/admin/tags_spec.rb +++ b/spec/requests/api/v1/admin/tags_spec.rb @@ -20,19 +20,16 @@ RSpec.describe 'Tags' do it_behaves_like 'forbidden for wrong scope', 'write:statuses' it_behaves_like 'forbidden for wrong role', '' - it 'returns http success' do - subject - - expect(response).to have_http_status(200) - expect(response.content_type) - .to start_with('application/json') - end - context 'when there are no tags' do it 'returns an empty list' do subject - expect(response.parsed_body).to be_empty + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body) + .to be_empty end end @@ -48,6 +45,11 @@ RSpec.describe 'Tags' do it 'returns the expected tags' do subject + + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') tags.each do |tag| expect(response.parsed_body.find { |item| item[:id] == tag.id.to_s && item[:name] == tag.name }).to_not be_nil end From 2b4bda800448337c3eb07b679e3e1031ea301102 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 23 Sep 2024 06:44:52 -0400 Subject: [PATCH 62/93] Add `response_avatar_link` helper to webfinger request spec (#31999) --- spec/requests/well_known/webfinger_spec.rb | 28 ++++++++++++---------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/spec/requests/well_known/webfinger_spec.rb b/spec/requests/well_known/webfinger_spec.rb index 6880ba4b58..aeff56aebf 100644 --- a/spec/requests/well_known/webfinger_spec.rb +++ b/spec/requests/well_known/webfinger_spec.rb @@ -168,10 +168,12 @@ RSpec.describe 'The /.well-known/webfinger endpoint' do it 'returns avatar in response' do perform_request! - avatar_link = get_avatar_link(response.parsed_body) - expect(avatar_link).to_not be_nil - expect(avatar_link[:type]).to eq alice.avatar.content_type - expect(avatar_link[:href]).to eq Addressable::URI.new(host: Rails.configuration.x.local_domain, path: alice.avatar.to_s, scheme: 'https').to_s + expect(response_avatar_link) + .to be_present + .and include( + type: eq(alice.avatar.content_type), + href: eq(Addressable::URI.new(host: Rails.configuration.x.local_domain, path: alice.avatar.to_s, scheme: 'https').to_s) + ) end context 'with limited federation mode' do @@ -182,8 +184,8 @@ RSpec.describe 'The /.well-known/webfinger endpoint' do it 'does not return avatar in response' do perform_request! - avatar_link = get_avatar_link(response.parsed_body) - expect(avatar_link).to be_nil + expect(response_avatar_link) + .to be_nil end end @@ -197,8 +199,8 @@ RSpec.describe 'The /.well-known/webfinger endpoint' do it 'does not return avatar in response' do perform_request! - avatar_link = get_avatar_link(response.parsed_body) - expect(avatar_link).to be_nil + expect(response_avatar_link) + .to be_nil end end end @@ -212,8 +214,8 @@ RSpec.describe 'The /.well-known/webfinger endpoint' do end it 'does not return avatar in response' do - avatar_link = get_avatar_link(response.parsed_body) - expect(avatar_link).to be_nil + expect(response_avatar_link) + .to be_nil end end @@ -247,7 +249,9 @@ RSpec.describe 'The /.well-known/webfinger endpoint' do private - def get_avatar_link(json) - json[:links].find { |link| link[:rel] == 'http://webfinger.net/rel/avatar' } + def response_avatar_link + response + .parsed_body[:links] + .find { |link| link[:rel] == 'http://webfinger.net/rel/avatar' } end end From bbf77522569ad284893d2380d0b688d361391889 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 23 Sep 2024 06:45:34 -0400 Subject: [PATCH 63/93] Combine assertions in `Notification` model spec (#32015) --- spec/models/notification_spec.rb | 127 +++++++++++++++---------------- 1 file changed, 61 insertions(+), 66 deletions(-) diff --git a/spec/models/notification_spec.rb b/spec/models/notification_spec.rb index d498ee02a5..67c85b40f3 100644 --- a/spec/models/notification_spec.rb +++ b/spec/models/notification_spec.rb @@ -252,88 +252,83 @@ RSpec.describe Notification do ] end - context 'with a preloaded target status' do - it 'preloads mention' do - expect(subject[0].type).to eq :mention - expect(subject[0].association(:mention)).to be_loaded - expect(subject[0].mention.association(:status)).to be_loaded + context 'with a preloaded target status and a cached status' do + it 'preloads association records and replaces association records' do + expect(subject) + .to contain_exactly( + mention_attributes, + status_attributes, + reblog_attributes, + follow_attributes, + follow_request_attributes, + favourite_attributes, + poll_attributes + ) end - it 'preloads status' do - expect(subject[1].type).to eq :status - expect(subject[1].association(:status)).to be_loaded + def mention_attributes + have_attributes( + type: :mention, + target_status: eq(mention.status).and(have_loaded_association(:account)), + mention: have_loaded_association(:status) + ).and(have_loaded_association(:mention)) end - it 'preloads reblog' do - expect(subject[2].type).to eq :reblog - expect(subject[2].association(:status)).to be_loaded - expect(subject[2].status.association(:reblog)).to be_loaded + def status_attributes + have_attributes( + type: :status, + target_status: eq(status).and(have_loaded_association(:account)) + ).and(have_loaded_association(:status)) end - it 'preloads follow as nil' do - expect(subject[3].type).to eq :follow - expect(subject[3].target_status).to be_nil + def reblog_attributes + have_attributes( + type: :reblog, + status: have_loaded_association(:reblog), + target_status: eq(reblog.reblog).and(have_loaded_association(:account)) + ).and(have_loaded_association(:status)) end - it 'preloads follow_request as nill' do - expect(subject[4].type).to eq :follow_request - expect(subject[4].target_status).to be_nil + def follow_attributes + have_attributes( + type: :follow, + target_status: be_nil + ) end - it 'preloads favourite' do - expect(subject[5].type).to eq :favourite - expect(subject[5].association(:favourite)).to be_loaded - expect(subject[5].favourite.association(:status)).to be_loaded + def follow_request_attributes + have_attributes( + type: :follow_request, + target_status: be_nil + ) end - it 'preloads poll' do - expect(subject[6].type).to eq :poll - expect(subject[6].association(:poll)).to be_loaded - expect(subject[6].poll.association(:status)).to be_loaded - end - end - - context 'with a cached status' do - it 'replaces mention' do - expect(subject[0].type).to eq :mention - expect(subject[0].target_status.association(:account)).to be_loaded - expect(subject[0].target_status).to eq mention.status + def favourite_attributes + have_attributes( + type: :favourite, + favourite: have_loaded_association(:status), + target_status: eq(favourite.status).and(have_loaded_association(:account)) + ).and(have_loaded_association(:favourite)) end - it 'replaces status' do - expect(subject[1].type).to eq :status - expect(subject[1].target_status.association(:account)).to be_loaded - expect(subject[1].target_status).to eq status - end - - it 'replaces reblog' do - expect(subject[2].type).to eq :reblog - expect(subject[2].target_status.association(:account)).to be_loaded - expect(subject[2].target_status).to eq reblog.reblog - end - - it 'replaces follow' do - expect(subject[3].type).to eq :follow - expect(subject[3].target_status).to be_nil - end - - it 'replaces follow_request' do - expect(subject[4].type).to eq :follow_request - expect(subject[4].target_status).to be_nil - end - - it 'replaces favourite' do - expect(subject[5].type).to eq :favourite - expect(subject[5].target_status.association(:account)).to be_loaded - expect(subject[5].target_status).to eq favourite.status - end - - it 'replaces poll' do - expect(subject[6].type).to eq :poll - expect(subject[6].target_status.association(:account)).to be_loaded - expect(subject[6].target_status).to eq poll.status + def poll_attributes + have_attributes( + type: :poll, + poll: have_loaded_association(:status), + target_status: eq(poll.status).and(have_loaded_association(:account)) + ).and(have_loaded_association(:poll)) end end end end end + +RSpec::Matchers.define :have_loaded_association do |association| + match do |record| + record.association(association).loaded? + end + + failure_message do |record| + "expected #{record} to have loaded association #{association} but it did not." + end +end From b5bdc69f7ba75852e6de651d2cd16e1e8897823f Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 23 Sep 2024 14:53:35 +0200 Subject: [PATCH 64/93] Change mobile breakpoint back to old version and allow main column to shrink (#32033) --- app/javascript/mastodon/features/ui/components/columns_area.jsx | 2 +- app/javascript/styles/mastodon/components.scss | 2 +- app/javascript/styles/mastodon/variables.scss | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/features/ui/components/columns_area.jsx b/app/javascript/mastodon/features/ui/components/columns_area.jsx index ff76d5bcb2..de957a79b6 100644 --- a/app/javascript/mastodon/features/ui/components/columns_area.jsx +++ b/app/javascript/mastodon/features/ui/components/columns_area.jsx @@ -62,7 +62,7 @@ export default class ColumnsArea extends ImmutablePureComponent { }; // Corresponds to (max-width: $no-gap-breakpoint - 1px) in SCSS - mediaQuery = 'matchMedia' in window && window.matchMedia('(max-width: 1206px)'); + mediaQuery = 'matchMedia' in window && window.matchMedia('(max-width: 1174px)'); state = { renderComposePanel: !(this.mediaQuery && this.mediaQuery.matches), diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 53becb3b92..f8febeae18 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2708,7 +2708,7 @@ a.account__display-name { &__main { box-sizing: border-box; width: 100%; - flex: 0 0 auto; + flex: 0 1 auto; display: flex; flex-direction: column; diff --git a/app/javascript/styles/mastodon/variables.scss b/app/javascript/styles/mastodon/variables.scss index 17442b9748..c477e7a750 100644 --- a/app/javascript/styles/mastodon/variables.scss +++ b/app/javascript/styles/mastodon/variables.scss @@ -87,7 +87,7 @@ $media-modal-media-max-width: 100%; // put margins on top and bottom of image to avoid the screen covered by image. $media-modal-media-max-height: 80%; -$no-gap-breakpoint: 1207px; +$no-gap-breakpoint: 1175px; $mobile-breakpoint: 630px; $font-sans-serif: 'mastodon-font-sans-serif' !default; From aaab6b7adc736a17ea9adc79309ef2efc90146e9 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 23 Sep 2024 15:14:15 +0200 Subject: [PATCH 65/93] Add reblogs and favourites counts to statuses in ActivityPub (#32007) --- .../activitypub/likes_controller.rb | 36 +++++++++++++++++++ .../activitypub/replies_controller.rb | 2 +- .../activitypub/shares_controller.rb | 36 +++++++++++++++++++ app/lib/activitypub/tag_manager.rb | 12 +++++++ .../activitypub/note_serializer.rb | 18 ++++++++++ config/routes.rb | 2 ++ 6 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 app/controllers/activitypub/likes_controller.rb create mode 100644 app/controllers/activitypub/shares_controller.rb diff --git a/app/controllers/activitypub/likes_controller.rb b/app/controllers/activitypub/likes_controller.rb new file mode 100644 index 0000000000..4aa6a4a771 --- /dev/null +++ b/app/controllers/activitypub/likes_controller.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +class ActivityPub::LikesController < ActivityPub::BaseController + include Authorization + + vary_by -> { 'Signature' if authorized_fetch_mode? } + + before_action :require_account_signature!, if: :authorized_fetch_mode? + before_action :set_status + + def index + expires_in 0, public: @status.distributable? && public_fetch_mode? + render json: likes_collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json' + end + + private + + def pundit_user + signed_request_account + end + + def set_status + @status = @account.statuses.find(params[:status_id]) + authorize @status, :show? + rescue Mastodon::NotPermittedError + not_found + end + + def likes_collection_presenter + ActivityPub::CollectionPresenter.new( + id: account_status_likes_url(@account, @status), + type: :unordered, + size: @status.favourites_count + ) + end +end diff --git a/app/controllers/activitypub/replies_controller.rb b/app/controllers/activitypub/replies_controller.rb index 11aac48c9c..0a19275d38 100644 --- a/app/controllers/activitypub/replies_controller.rb +++ b/app/controllers/activitypub/replies_controller.rb @@ -12,7 +12,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController before_action :set_replies def index - expires_in 0, public: public_fetch_mode? + expires_in 0, public: @status.distributable? && public_fetch_mode? render json: replies_collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json', skip_activities: true end diff --git a/app/controllers/activitypub/shares_controller.rb b/app/controllers/activitypub/shares_controller.rb new file mode 100644 index 0000000000..65b4a5b383 --- /dev/null +++ b/app/controllers/activitypub/shares_controller.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +class ActivityPub::SharesController < ActivityPub::BaseController + include Authorization + + vary_by -> { 'Signature' if authorized_fetch_mode? } + + before_action :require_account_signature!, if: :authorized_fetch_mode? + before_action :set_status + + def index + expires_in 0, public: @status.distributable? && public_fetch_mode? + render json: shares_collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json' + end + + private + + def pundit_user + signed_request_account + end + + def set_status + @status = @account.statuses.find(params[:status_id]) + authorize @status, :show? + rescue Mastodon::NotPermittedError + not_found + end + + def shares_collection_presenter + ActivityPub::CollectionPresenter.new( + id: account_status_shares_url(@account, @status), + type: :unordered, + size: @status.reblogs_count + ) + end +end diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb index 8643286317..23b44be372 100644 --- a/app/lib/activitypub/tag_manager.rb +++ b/app/lib/activitypub/tag_manager.rb @@ -74,6 +74,18 @@ class ActivityPub::TagManager account_status_replies_url(target.account, target, page_params) end + def likes_uri_for(target) + raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local? + + account_status_likes_url(target.account, target) + end + + def shares_uri_for(target) + raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local? + + account_status_shares_url(target.account, target) + end + def followers_uri_for(target) target.local? ? account_followers_url(target) : target.followers_url.presence end diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb index 27e058199d..7b29e6d69b 100644 --- a/app/serializers/activitypub/note_serializer.rb +++ b/app/serializers/activitypub/note_serializer.rb @@ -19,6 +19,8 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer has_many :virtual_tags, key: :tag has_one :replies, serializer: ActivityPub::CollectionSerializer, if: :local? + has_one :likes, serializer: ActivityPub::CollectionSerializer, if: :local? + has_one :shares, serializer: ActivityPub::CollectionSerializer, if: :local? has_many :poll_options, key: :one_of, if: :poll_and_not_multiple? has_many :poll_options, key: :any_of, if: :poll_and_multiple? @@ -64,6 +66,22 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer ) end + def likes + ActivityPub::CollectionPresenter.new( + id: ActivityPub::TagManager.instance.likes_uri_for(object), + type: :unordered, + size: object.favourites_count + ) + end + + def shares + ActivityPub::CollectionPresenter.new( + id: ActivityPub::TagManager.instance.shares_uri_for(object), + type: :unordered, + size: object.reblogs_count + ) + end + def language? object.language.present? end diff --git a/config/routes.rb b/config/routes.rb index ad607e537b..890102955c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -126,6 +126,8 @@ Rails.application.routes.draw do end resources :replies, only: [:index], module: :activitypub + resources :likes, only: [:index], module: :activitypub + resources :shares, only: [:index], module: :activitypub end resources :followers, only: [:index], controller: :follower_accounts From 5dfdec645313e556413147597138a8008bc35996 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 23 Sep 2024 09:37:32 -0400 Subject: [PATCH 66/93] Convert `settings/applications` controller spec to system/request specs (#32006) --- .../settings/applications/_fields.html.haml | 36 ---- .../settings/applications/_form.html.haml | 36 ++++ app/views/settings/applications/new.html.haml | 6 +- .../settings/applications/show.html.haml | 6 +- .../settings/applications_controller_spec.rb | 178 ------------------ spec/requests/settings/applications_spec.rb | 44 +++++ spec/system/settings/applications_spec.rb | 144 ++++++++++++++ 7 files changed, 230 insertions(+), 220 deletions(-) delete mode 100644 app/views/settings/applications/_fields.html.haml create mode 100644 app/views/settings/applications/_form.html.haml delete mode 100644 spec/controllers/settings/applications_controller_spec.rb create mode 100644 spec/requests/settings/applications_spec.rb create mode 100644 spec/system/settings/applications_spec.rb diff --git a/app/views/settings/applications/_fields.html.haml b/app/views/settings/applications/_fields.html.haml deleted file mode 100644 index d539848952..0000000000 --- a/app/views/settings/applications/_fields.html.haml +++ /dev/null @@ -1,36 +0,0 @@ -.fields-group - = f.input :name, - label: t('activerecord.attributes.doorkeeper/application.name'), - wrapper: :with_label - -.fields-group - = f.input :website, - label: t('activerecord.attributes.doorkeeper/application.website'), - wrapper: :with_label - -.fields-group - = f.input :redirect_uri, - label: t('activerecord.attributes.doorkeeper/application.redirect_uri'), hint: t('doorkeeper.applications.help.redirect_uri'), - required: true, - wrapper: :with_block_label - - %p.hint= t('doorkeeper.applications.help.native_redirect_uri', native_redirect_uri: content_tag(:code, Doorkeeper.configuration.native_redirect_uri)).html_safe - -.field-group - .input.with_block_label - %label= t('activerecord.attributes.doorkeeper/application.scopes') - %span.hint= t('simple_form.hints.defaults.scopes') - - - Doorkeeper.configuration.scopes.group_by { |s| s.split(':').first }.each_value do |value| - = f.input :scopes, - as: :check_boxes, - collection_wrapper_tag: 'ul', - collection: value.sort, - hint: false, - include_blank: false, - item_wrapper_tag: 'li', - label_method: ->(scope) { safe_join([content_tag(:samp, scope, class: class_for_scope(scope)), content_tag(:span, t("doorkeeper.scopes.#{scope}"), class: 'hint')]) }, - label: false, - required: false, - selected: f.object.scopes.all, - wrapper: :with_block_label diff --git a/app/views/settings/applications/_form.html.haml b/app/views/settings/applications/_form.html.haml new file mode 100644 index 0000000000..66ea8bc12b --- /dev/null +++ b/app/views/settings/applications/_form.html.haml @@ -0,0 +1,36 @@ +.fields-group + = form.input :name, + label: t('activerecord.attributes.doorkeeper/application.name'), + wrapper: :with_label + +.fields-group + = form.input :website, + label: t('activerecord.attributes.doorkeeper/application.website'), + wrapper: :with_label + +.fields-group + = form.input :redirect_uri, + label: t('activerecord.attributes.doorkeeper/application.redirect_uri'), hint: t('doorkeeper.applications.help.redirect_uri'), + required: true, + wrapper: :with_block_label + + %p.hint= t('doorkeeper.applications.help.native_redirect_uri', native_redirect_uri: content_tag(:code, Doorkeeper.configuration.native_redirect_uri)).html_safe + +.field-group + .input.with_block_label + %label= t('activerecord.attributes.doorkeeper/application.scopes') + %span.hint= t('simple_form.hints.defaults.scopes') + + - Doorkeeper.configuration.scopes.group_by { |s| s.split(':').first }.each_value do |value| + = form.input :scopes, + as: :check_boxes, + collection_wrapper_tag: 'ul', + collection: value.sort, + hint: false, + include_blank: false, + item_wrapper_tag: 'li', + label_method: ->(scope) { safe_join([content_tag(:samp, scope, class: class_for_scope(scope)), content_tag(:span, t("doorkeeper.scopes.#{scope}"), class: 'hint')]) }, + label: false, + required: false, + selected: form.object.scopes.all, + wrapper: :with_block_label diff --git a/app/views/settings/applications/new.html.haml b/app/views/settings/applications/new.html.haml index aa2281fea1..f6a546e8de 100644 --- a/app/views/settings/applications/new.html.haml +++ b/app/views/settings/applications/new.html.haml @@ -1,8 +1,8 @@ - content_for :page_title do = t('doorkeeper.applications.new.title') -= simple_form_for @application, url: settings_applications_path do |f| - = render 'fields', f: f += simple_form_for @application, url: settings_applications_path do |form| + = render form .actions - = f.button :button, t('doorkeeper.applications.buttons.submit'), type: :submit + = form.button :button, t('doorkeeper.applications.buttons.submit'), type: :submit diff --git a/app/views/settings/applications/show.html.haml b/app/views/settings/applications/show.html.haml index be1d13eae6..19630cf49b 100644 --- a/app/views/settings/applications/show.html.haml +++ b/app/views/settings/applications/show.html.haml @@ -23,8 +23,8 @@ %hr/ -= simple_form_for @application, url: settings_application_path(@application), method: :put do |f| - = render 'fields', f: f += simple_form_for @application, url: settings_application_path(@application), method: :put do |form| + = render form .actions - = f.button :button, t('generic.save_changes'), type: :submit + = form.button :button, t('generic.save_changes'), type: :submit diff --git a/spec/controllers/settings/applications_controller_spec.rb b/spec/controllers/settings/applications_controller_spec.rb deleted file mode 100644 index 721741bacb..0000000000 --- a/spec/controllers/settings/applications_controller_spec.rb +++ /dev/null @@ -1,178 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Settings::ApplicationsController do - render_views - - let!(:user) { Fabricate(:user) } - let!(:app) { Fabricate(:application, owner: user) } - - before do - sign_in user, scope: :user - end - - describe 'GET #index' do - before do - Fabricate(:application) - get :index - end - - it 'returns http success with private cache control headers', :aggregate_failures do - expect(response).to have_http_status(200) - expect(response.headers['Cache-Control']).to include('private, no-store') - end - end - - describe 'GET #show' do - it 'returns http success' do - get :show, params: { id: app.id } - expect(response).to have_http_status(200) - expect(assigns[:application]).to eql(app) - end - - it 'returns 404 if you dont own app' do - app.update!(owner: nil) - - get :show, params: { id: app.id } - expect(response).to have_http_status 404 - end - end - - describe 'GET #new' do - it 'returns http success' do - get :new - expect(response).to have_http_status(200) - end - end - - describe 'POST #create' do - context 'when success (passed scopes as a String)' do - subject do - post :create, params: { - doorkeeper_application: { - name: 'My New App', - redirect_uri: 'urn:ietf:wg:oauth:2.0:oob', - website: 'http://google.com', - scopes: 'read write follow', - }, - } - end - - it 'creates an entry in the database', :aggregate_failures do - expect { subject }.to change(Doorkeeper::Application, :count) - expect(response).to redirect_to(settings_applications_path) - end - end - - context 'when success (passed scopes as an Array)' do - subject do - post :create, params: { - doorkeeper_application: { - name: 'My New App', - redirect_uri: 'urn:ietf:wg:oauth:2.0:oob', - website: 'http://google.com', - scopes: %w(read write follow), - }, - } - end - - it 'creates an entry in the database', :aggregate_failures do - expect { subject }.to change(Doorkeeper::Application, :count) - expect(response).to redirect_to(settings_applications_path) - end - end - - context 'with failure request' do - before do - post :create, params: { - doorkeeper_application: { - name: '', - redirect_uri: '', - website: '', - scopes: [], - }, - } - end - - it 'returns http success and renders form', :aggregate_failures do - expect(response).to have_http_status(200) - expect(response).to render_template(:new) - end - end - end - - describe 'PATCH #update' do - context 'when success' do - subject do - patch :update, params: { - id: app.id, - doorkeeper_application: opts, - } - response - end - - let(:opts) do - { - website: 'https://foo.bar/', - } - end - - it 'updates existing application' do - subject - - expect(app.reload.website).to eql(opts[:website]) - expect(response).to redirect_to(settings_application_path(app)) - end - end - - context 'with failure request' do - before do - patch :update, params: { - id: app.id, - doorkeeper_application: { - name: '', - redirect_uri: '', - website: '', - scopes: [], - }, - } - end - - it 'returns http success and renders form', :aggregate_failures do - expect(response).to have_http_status(200) - expect(response).to render_template(:show) - end - end - end - - describe 'destroy' do - let(:redis_pipeline_stub) { instance_double(Redis::Namespace, publish: nil) } - let!(:access_token) { Fabricate(:accessible_access_token, application: app) } - - before do - allow(redis).to receive(:pipelined).and_yield(redis_pipeline_stub) - post :destroy, params: { id: app.id } - end - - it 'redirects back to applications page removes the app' do - expect(response).to redirect_to(settings_applications_path) - expect(Doorkeeper::Application.find_by(id: app.id)).to be_nil - end - - it 'sends a session kill payload to the streaming server' do - expect(redis_pipeline_stub).to have_received(:publish).with("timeline:access_token:#{access_token.id}", '{"event":"kill"}') - end - end - - describe 'regenerate' do - let(:token) { user.token_for_app(app) } - - it 'creates new token' do - expect(token).to_not be_nil - post :regenerate, params: { id: app.id } - - expect(user.token_for_app(app)).to_not eql(token) - end - end -end diff --git a/spec/requests/settings/applications_spec.rb b/spec/requests/settings/applications_spec.rb new file mode 100644 index 0000000000..8a5b3de895 --- /dev/null +++ b/spec/requests/settings/applications_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Settings / Exports' do + let(:user) { Fabricate :user } + + before { sign_in user } + + describe 'GET /settings/application/:id' do + context 'when user does not own application' do + let(:application) { Fabricate :application } + + it 'returns http missing' do + get settings_application_path(application) + + expect(response) + .to have_http_status(404) + end + end + end + + describe 'POST /settings/applications' do + subject { post '/settings/applications', params: params } + + let(:params) do + { + doorkeeper_application: { + name: 'My New App', + redirect_uri: 'urn:ietf:wg:oauth:2.0:oob', + website: 'http://google.com', + scopes: 'read write follow', + }, + } + end + + it 'supports passing scope values as string' do + expect { subject } + .to change(Doorkeeper::Application, :count).by(1) + expect(response) + .to redirect_to(settings_applications_path) + end + end +end diff --git a/spec/system/settings/applications_spec.rb b/spec/system/settings/applications_spec.rb new file mode 100644 index 0000000000..ee43da3d5d --- /dev/null +++ b/spec/system/settings/applications_spec.rb @@ -0,0 +1,144 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Settings applications page' do + let!(:application) { Fabricate :application, owner: user } + let(:user) { Fabricate :user } + + before { sign_in user } + + describe 'Viewing the list of applications' do + it 'sees the applications' do + visit settings_applications_path + + expect(page) + .to have_content(application.name) + .and have_private_cache_control + end + end + + describe 'Viewing a single application' do + it 'shows a page with application details' do + visit settings_application_path(application) + + expect(page) + .to have_content(application.name) + end + end + + describe 'Creating a new application' do + it 'accepts form input to make an application' do + visit new_settings_application_path + + fill_in_form + + expect { submit_form } + .to change(Doorkeeper::Application, :count).by(1) + expect(page) + .to have_content(I18n.t('doorkeeper.applications.index.title')) + .and have_content('My new app') + end + + it 'does not save with invalid form values' do + visit new_settings_application_path + + expect { submit_form } + .to not_change(Doorkeeper::Application, :count) + expect(page) + .to have_content("can't be blank") + end + + def fill_in_form + fill_in form_app_name_label, + with: 'My new app' + fill_in I18n.t('activerecord.attributes.doorkeeper/application.website'), + with: 'http://google.com' + fill_in I18n.t('activerecord.attributes.doorkeeper/application.redirect_uri'), + with: 'urn:ietf:wg:oauth:2.0:oob' + + check 'read', id: :doorkeeper_application_scopes_read + check 'write', id: :doorkeeper_application_scopes_write + check 'follow', id: :doorkeeper_application_scopes_follow + end + + def submit_form + click_on I18n.t('doorkeeper.applications.buttons.submit') + end + end + + describe 'Updating an application' do + it 'successfully updates with valid values' do + visit settings_application_path(application) + + fill_in form_app_name_label, + with: 'My new app name with a new value' + submit_form + + expect(page) + .to have_content('My new app name with a new value') + end + + it 'does not update with wrong values' do + visit settings_application_path(application) + + fill_in form_app_name_label, + with: '' + submit_form + + expect(page) + .to have_content("can't be blank") + end + + def submit_form + click_on I18n.t('generic.save_changes') + end + end + + describe 'Destroying an application' do + let(:redis_pipeline_stub) { instance_double(Redis::Namespace, publish: nil) } + let!(:access_token) { Fabricate(:accessible_access_token, application: application) } + + before { stub_redis_pipeline } + + it 'destroys the record and tells the broader universe about that' do + visit settings_applications_path + + expect { destroy_application } + .to change(Doorkeeper::Application, :count).by(-1) + expect(page) + .to have_no_content(application.name) + expect(redis_pipeline_stub) + .to have_received(:publish).with("timeline:access_token:#{access_token.id}", '{"event":"kill"}') + end + + def destroy_application + click_on I18n.t('doorkeeper.applications.index.delete') + end + + def stub_redis_pipeline + allow(redis) + .to receive(:pipelined) + .and_yield(redis_pipeline_stub) + end + end + + describe 'Regenerating an app token' do + it 'updates the app token' do + visit settings_application_path(application) + + expect { regenerate_token } + .to(change { user.token_for_app(application) }) + expect(page) + .to have_content(I18n.t('applications.token_regenerated')) + end + + def regenerate_token + click_on I18n.t('applications.regenerate_token') + end + end + + def form_app_name_label + I18n.t('activerecord.attributes.doorkeeper/application.name') + end +end From d54ce67dc9ac59ad1d2fc0ee719318610a5ed249 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 24 Sep 2024 10:00:20 +0200 Subject: [PATCH 67/93] Change hide media button to be in top right corner in web UI (#32048) --- app/javascript/styles/mastodon/components.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index f8febeae18..43ff4f28ef 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -6889,7 +6889,7 @@ a.status-card { .media-gallery__actions { position: absolute; - bottom: 6px; + top: 6px; inset-inline-end: 6px; display: flex; gap: 2px; @@ -6912,7 +6912,7 @@ a.status-card { .media-gallery__item__badges { position: absolute; bottom: 8px; - inset-inline-start: 8px; + inset-inline-end: 8px; display: flex; gap: 2px; } From c808055fc3ebe311b0e89cd2486e80458be29472 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 24 Sep 2024 10:16:22 +0200 Subject: [PATCH 68/93] Update dependency webrick (#32054) --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8ba091038d..8f0c0ed53c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -893,7 +893,7 @@ GEM rack-proxy (>= 0.6.1) railties (>= 5.2) semantic_range (>= 2.3.0) - webrick (1.8.1) + webrick (1.8.2) websocket (1.2.11) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) From 11ac5c892981a1d03335f81ac0f7f147f3204a43 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 11:42:14 +0200 Subject: [PATCH 69/93] New Crowdin Translations (automated) (#32052) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/el.json | 13 ++++++ app/javascript/mastodon/locales/eo.json | 48 ++++++++++++++++++++-- app/javascript/mastodon/locales/es-AR.json | 2 +- app/javascript/mastodon/locales/es.json | 6 +-- app/javascript/mastodon/locales/gd.json | 2 + app/javascript/mastodon/locales/gl.json | 8 ++-- config/locales/activerecord.be.yml | 2 + config/locales/activerecord.el.yml | 6 +++ config/locales/doorkeeper.af.yml | 1 - config/locales/doorkeeper.an.yml | 1 - config/locales/doorkeeper.ar.yml | 1 - config/locales/doorkeeper.ast.yml | 1 - config/locales/doorkeeper.be.yml | 1 - config/locales/doorkeeper.bg.yml | 1 - config/locales/doorkeeper.ca.yml | 2 +- config/locales/doorkeeper.ckb.yml | 1 - config/locales/doorkeeper.cs.yml | 1 - config/locales/doorkeeper.cy.yml | 1 - config/locales/doorkeeper.da.yml | 2 +- config/locales/doorkeeper.de.yml | 2 +- config/locales/doorkeeper.el.yml | 1 - config/locales/doorkeeper.en-GB.yml | 2 +- config/locales/doorkeeper.eo.yml | 1 - config/locales/doorkeeper.es-AR.yml | 2 +- config/locales/doorkeeper.es-MX.yml | 2 +- config/locales/doorkeeper.es.yml | 2 +- config/locales/doorkeeper.et.yml | 1 - config/locales/doorkeeper.eu.yml | 1 - config/locales/doorkeeper.fa.yml | 1 - config/locales/doorkeeper.fi.yml | 2 +- config/locales/doorkeeper.fo.yml | 2 +- config/locales/doorkeeper.fr-CA.yml | 1 - config/locales/doorkeeper.fr.yml | 1 - config/locales/doorkeeper.fy.yml | 1 - config/locales/doorkeeper.ga.yml | 1 - config/locales/doorkeeper.gd.yml | 2 +- config/locales/doorkeeper.gl.yml | 2 +- config/locales/doorkeeper.he.yml | 2 +- config/locales/doorkeeper.hu.yml | 1 - config/locales/doorkeeper.ia.yml | 1 - config/locales/doorkeeper.id.yml | 1 - config/locales/doorkeeper.ie.yml | 1 - config/locales/doorkeeper.io.yml | 1 - config/locales/doorkeeper.is.yml | 1 - config/locales/doorkeeper.it.yml | 2 +- config/locales/doorkeeper.ja.yml | 1 - config/locales/doorkeeper.ko.yml | 1 - config/locales/doorkeeper.ku.yml | 1 - config/locales/doorkeeper.lad.yml | 1 - config/locales/doorkeeper.lt.yml | 2 +- config/locales/doorkeeper.lv.yml | 1 - config/locales/doorkeeper.ms.yml | 1 - config/locales/doorkeeper.my.yml | 1 - config/locales/doorkeeper.nl.yml | 2 +- config/locales/doorkeeper.nn.yml | 2 +- config/locales/doorkeeper.no.yml | 1 - config/locales/doorkeeper.oc.yml | 1 - config/locales/doorkeeper.pl.yml | 2 +- config/locales/doorkeeper.pt-BR.yml | 1 - config/locales/doorkeeper.pt-PT.yml | 1 - config/locales/doorkeeper.ro.yml | 1 - config/locales/doorkeeper.ru.yml | 1 - config/locales/doorkeeper.sco.yml | 1 - config/locales/doorkeeper.si.yml | 1 - config/locales/doorkeeper.sk.yml | 1 - config/locales/doorkeeper.sl.yml | 1 - config/locales/doorkeeper.sq.yml | 1 - config/locales/doorkeeper.sr-Latn.yml | 1 - config/locales/doorkeeper.sr.yml | 1 - config/locales/doorkeeper.sv.yml | 1 - config/locales/doorkeeper.th.yml | 1 - config/locales/doorkeeper.tr.yml | 2 +- config/locales/doorkeeper.uk.yml | 2 +- config/locales/doorkeeper.vi.yml | 2 +- config/locales/doorkeeper.zh-CN.yml | 2 +- config/locales/doorkeeper.zh-HK.yml | 1 - config/locales/doorkeeper.zh-TW.yml | 2 +- config/locales/el.yml | 3 ++ config/locales/eo.yml | 29 +++++++++++++ config/locales/simple_form.el.yml | 45 ++++++++++++++++++++ 80 files changed, 174 insertions(+), 81 deletions(-) diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index ac6a2cf4e8..83f0227a0a 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -97,6 +97,8 @@ "block_modal.title": "Αποκλεισμός χρήστη;", "block_modal.you_wont_see_mentions": "Δε θα βλέπεις τις αναρτήσεις που τον αναφέρουν.", "boost_modal.combo": "Μπορείς να πατήσεις {combo} για να το προσπεράσεις την επόμενη φορά", + "boost_modal.reblog": "Ενίσχυση ανάρτησης;", + "boost_modal.undo_reblog": "Αναίρεση ενίσχυσης;", "bundle_column_error.copy_stacktrace": "Αντιγραφή αναφοράς σφάλματος", "bundle_column_error.error.body": "Δεν ήταν δυνατή η απόδοση της σελίδας που ζήτησες. Μπορεί να οφείλεται σε σφάλμα στον κώδικά μας ή σε πρόβλημα συμβατότητας του προγράμματος περιήγησης.", "bundle_column_error.error.title": "Ωχ όχι!", @@ -192,6 +194,8 @@ "confirmations.unfollow.confirm": "Άρση ακολούθησης", "confirmations.unfollow.message": "Σίγουρα θες να πάψεις να ακολουθείς τον/την {name};", "confirmations.unfollow.title": "Άρση ακολούθησης;", + "content_warning.hide": "Απόκρυψη ανάρτησης", + "content_warning.show": "Εμφάνιση ούτως ή άλλως", "conversation.delete": "Διαγραφή συζήτησης", "conversation.mark_as_read": "Σήμανση ως αναγνωσμένο", "conversation.open": "Προβολή συνομιλίας", @@ -299,6 +303,7 @@ "filter_modal.select_filter.subtitle": "Χρησιμοποιήστε μια υπάρχουσα κατηγορία ή δημιουργήστε μια νέα", "filter_modal.select_filter.title": "Φιλτράρισμα αυτής της ανάρτησης", "filter_modal.title.status": "Φιλτράρισμα μιας ανάρτησης", + "filter_warning.matches_filter": "Ταιριάζει με το φίλτρο “{title}”", "filtered_notifications_banner.pending_requests": "Από {count, plural, =0 {κανένα} one {ένα άτομο} other {# άτομα}} που μπορεί να ξέρεις", "filtered_notifications_banner.title": "Φιλτραρισμένες ειδοποιήσεις", "firehose.all": "Όλα", @@ -429,6 +434,8 @@ "lightbox.close": "Κλείσιμο", "lightbox.next": "Επόμενο", "lightbox.previous": "Προηγούμενο", + "lightbox.zoom_in": "Εστίαση στο πραγματικό μέγεθος", + "lightbox.zoom_out": "Εστίαση για προσαρμογή", "limited_account_hint.action": "Εμφάνιση προφίλ ούτως ή άλλως", "limited_account_hint.title": "Αυτό το προφίλ έχει αποκρυφτεί από τους διαχειριστές του διακομιστή {domain}.", "link_preview.author": "Από {name}", @@ -450,6 +457,7 @@ "lists.subheading": "Οι λίστες σου", "load_pending": "{count, plural, one {# νέο στοιχείο} other {# νέα στοιχεία}}", "loading_indicator.label": "Φόρτωση…", + "media_gallery.hide": "Απόκρυψη", "moved_to_account_banner.text": "Ο λογαριασμός σου {disabledAccount} είναι προσωρινά απενεργοποιημένος επειδή μεταφέρθηκες στον {movedToAccount}.", "mute_modal.hide_from_notifications": "Απόκρυψη από ειδοποιήσεις", "mute_modal.hide_options": "Απόκρυψη επιλογών", @@ -461,6 +469,7 @@ "mute_modal.you_wont_see_mentions": "Δε θα βλέπεις τις αναρτήσεις που τον αναφέρουν.", "mute_modal.you_wont_see_posts": "Μπορεί ακόμα να δει τις αναρτήσεις σου, αλλά δε θα βλέπεις τις δικές του.", "navigation_bar.about": "Σχετικά με", + "navigation_bar.administration": "Διαχείριση", "navigation_bar.advanced_interface": "Άνοιγμα σε προηγμένη διεπαφή ιστού", "navigation_bar.blocks": "Αποκλεισμένοι χρήστες", "navigation_bar.bookmarks": "Σελιδοδείκτες", @@ -477,6 +486,7 @@ "navigation_bar.follows_and_followers": "Ακολουθείς και σε ακολουθούν", "navigation_bar.lists": "Λίστες", "navigation_bar.logout": "Αποσύνδεση", + "navigation_bar.moderation": "Συντονισμός", "navigation_bar.mutes": "Αποσιωπημένοι χρήστες", "navigation_bar.opened_in_classic_interface": "Δημοσιεύσεις, λογαριασμοί και άλλες συγκεκριμένες σελίδες ανοίγονται από προεπιλογή στην κλασική διεπαφή ιστού.", "navigation_bar.personal": "Προσωπικά", @@ -768,6 +778,7 @@ "status.bookmark": "Σελιδοδείκτης", "status.cancel_reblog_private": "Ακύρωση ενίσχυσης", "status.cannot_reblog": "Αυτή η ανάρτηση δεν μπορεί να ενισχυθεί", + "status.continued_thread": "Συνεχιζόμενο νήματος", "status.copy": "Αντιγραφή συνδέσμου ανάρτησης", "status.delete": "Διαγραφή", "status.detailed_status": "Προβολή λεπτομερούς συζήτησης", @@ -776,6 +787,7 @@ "status.edit": "Επεξεργασία", "status.edited": "Τελευταία επεξεργασία {date}", "status.edited_x_times": "Επεξεργάστηκε {count, plural, one {{count} φορά} other {{count} φορές}}", + "status.embed": "Απόκτηση κώδικα ενσωμάτωσης", "status.favourite": "Αγαπημένα", "status.favourites": "{count, plural, one {# αγαπημένο} other {# αγαπημένα}}", "status.filter": "Φιλτράρισμα αυτής της ανάρτησης", @@ -800,6 +812,7 @@ "status.reblogs.empty": "Κανείς δεν ενίσχυσε αυτή την ανάρτηση ακόμα. Μόλις το κάνει κάποιος, θα εμφανιστεί εδώ.", "status.redraft": "Σβήσε & ξαναγράψε", "status.remove_bookmark": "Αφαίρεση σελιδοδείκτη", + "status.replied_in_thread": "Απαντήθηκε σε νήμα", "status.replied_to": "Απάντησε στον {name}", "status.reply": "Απάντησε", "status.replyAll": "Απάντησε στο νήμα συζήτησης", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 05f53fb731..fbf74e9efa 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -231,6 +231,8 @@ "domain_pill.their_username": "Ilia unika identigilo sur ilia servilo. Eblas trovi uzantojn kun la sama uzantnomo sur malsamaj serviloj.", "domain_pill.username": "Uzantnomo", "domain_pill.whats_in_a_handle": "Kio estas en identigo?", + "domain_pill.who_they_are": "Ĉar identigoj diras, kiu iu estas kaj kie ili estas, vi povas interagi kun homoj tra la socia reto de .", + "domain_pill.who_you_are": "Ĉar via identigo diras kiu vi estas kaj kie vi estas, homoj povas interagi kun vi tra la socia reto de .", "domain_pill.your_handle": "Via identigo:", "domain_pill.your_server": "Via cifereca hejmo, kie loĝas ĉiuj viaj afiŝoj. Ĉu vi ne ŝatas ĉi tiun? Transloku servilojn iam ajn kaj alportu ankaŭ viajn sekvantojn.", "domain_pill.your_username": "Via unika identigilo sur ĉi tiu servilo. Eblas trovi uzantojn kun la sama uzantnomo sur malsamaj serviloj.", @@ -301,6 +303,7 @@ "filter_modal.select_filter.subtitle": "Uzu ekzistantan kategorion aŭ kreu novan", "filter_modal.select_filter.title": "Filtri ĉi tiun afiŝon", "filter_modal.title.status": "Filtri mesaĝon", + "filter_warning.matches_filter": "Filtrilo de kongruoj “{title}”", "filtered_notifications_banner.pending_requests": "El {count, plural, =0 {neniu} one {unu persono} other {# homoj}} vi eble konas", "filtered_notifications_banner.title": "Filtritaj sciigoj", "firehose.all": "Ĉiuj", @@ -309,11 +312,16 @@ "follow_request.authorize": "Rajtigi", "follow_request.reject": "Rifuzi", "follow_requests.unlocked_explanation": "Kvankam via konto ne estas ŝlosita, la dungitaro de {domain} opinias, ke vi eble volas revizii petojn pri sekvado de ĉi tiuj kontoj permane.", + "follow_suggestions.curated_suggestion": "Elekto de stabo", "follow_suggestions.dismiss": "Ne montri denove", + "follow_suggestions.featured_longer": "Mane elektita de la teamo de {domain}", "follow_suggestions.friends_of_friends_longer": "Populara inter homoj, kiujn vi sekvas", + "follow_suggestions.hints.featured": "Ĉi tiu profilo estis mane elektita de la teamo de {domain}.", "follow_suggestions.hints.friends_of_friends": "Ĉi tiu profilo estas populara inter la homoj, kiujn vi sekvas.", "follow_suggestions.hints.most_followed": "Ĉi tiu profilo estas unu el la plej sekvataj en {domain}.", + "follow_suggestions.hints.most_interactions": "Ĉi tiu profilo lastatempe ricevis multe da atento sur {domain}.", "follow_suggestions.hints.similar_to_recently_followed": "Ĉi tiu profilo similas al la profiloj kiujn vi plej lastatempe sekvis.", + "follow_suggestions.personalized_suggestion": "Agordita propono", "follow_suggestions.popular_suggestion": "Popularaj proponoj", "follow_suggestions.popular_suggestion_longer": "Populara en {domain}", "follow_suggestions.similar_to_recently_followed_longer": "Simile al profiloj, kiujn vi lastatempe sekvis", @@ -346,9 +354,12 @@ "hashtag.unfollow": "Ne plu sekvi la kradvorton", "hashtags.and_other": "…kaj {count, plural,other {# pli}}", "hints.profiles.followers_may_be_missing": "Sekvantoj por ĉi tiu profilo eble mankas.", + "hints.profiles.follows_may_be_missing": "Sekvatoj de ĉi tiu profilo eble mankas.", "hints.profiles.posts_may_be_missing": "Iuj afiŝoj de ĉi tiu profilo eble mankas.", "hints.profiles.see_more_followers": "Vidi pli da sekvantoj sur {domain}", + "hints.profiles.see_more_follows": "Vidi pli da sekvatoj sur {domain}", "hints.profiles.see_more_posts": "Vidi pli da afiŝoj sur {domain}", + "hints.threads.replies_may_be_missing": "Respondoj de aliaj serviloj eble mankas.", "hints.threads.see_more": "Vidi pli da respondoj sur {domain}", "home.column_settings.show_reblogs": "Montri diskonigojn", "home.column_settings.show_replies": "Montri respondojn", @@ -358,6 +369,10 @@ "home.pending_critical_update.title": "Kritika sekurĝisdatigo estas disponebla!", "home.show_announcements": "Montri anoncojn", "ignore_notifications_modal.disclaimer": "Mastodon ne povas informi uzantojn, ke vi ignoris iliajn sciigojn. Ignori sciigojn ne malhelpos la mesaĝojn mem esti senditaj.", + "ignore_notifications_modal.filter_instead": "Filtri anstataŭe", + "ignore_notifications_modal.filter_to_act_users": "Vi ankoraŭ povos akcepti, malakcepti aŭ raporti uzantojn", + "ignore_notifications_modal.filter_to_avoid_confusion": "Filtrado helpas eviti eblan konfuzon", + "ignore_notifications_modal.filter_to_review_separately": "Vi povas revizii filtritajn sciigojn aparte", "ignore_notifications_modal.ignore": "Ignori sciigojn", "ignore_notifications_modal.limited_accounts_title": "Ĉu ignori sciigojn de moderigitaj kontoj?", "ignore_notifications_modal.new_accounts_title": "Ĉu ignori sciigojn de novaj kontoj?", @@ -471,6 +486,7 @@ "navigation_bar.follows_and_followers": "Sekvatoj kaj sekvantoj", "navigation_bar.lists": "Listoj", "navigation_bar.logout": "Adiaŭi", + "navigation_bar.moderation": "Modereco", "navigation_bar.mutes": "Silentigitaj uzantoj", "navigation_bar.opened_in_classic_interface": "Afiŝoj, kontoj, kaj aliaj specifaj paĝoj kiuj estas malfermititaj defaulta en la klasika reta interfaco.", "navigation_bar.personal": "Persone", @@ -486,7 +502,9 @@ "notification.admin.report_statuses": "{name} raportis {target} por {category}", "notification.admin.report_statuses_other": "{name} raportis {target}", "notification.admin.sign_up": "{name} kreis konton", + "notification.admin.sign_up.name_and_others": "{name} kaj {count, plural, one {# alia} other {# aliaj}} kreis konton", "notification.favourite": "{name} stelumis vian afiŝon", + "notification.favourite.name_and_others_with_link": "{name} kaj {count, plural, one {# alia} other {# aliaj}} ŝatis vian afiŝon", "notification.follow": "{name} eksekvis vin", "notification.follow.name_and_others": "{name} kaj {count, plural, one {# alia} other {# aliaj}} sekvis vin", "notification.follow_request": "{name} petis sekvi vin", @@ -506,22 +524,32 @@ "notification.moderation_warning.action_silence": "Via konto estis limigita.", "notification.moderation_warning.action_suspend": "Via konto estas malakceptita.", "notification.own_poll": "Via enketo finiĝis", + "notification.poll": "Balotenketo, en kiu vi voĉdonis, finiĝis", "notification.reblog": "{name} diskonigis vian afiŝon", "notification.reblog.name_and_others_with_link": "{name} kaj {count, plural, one {# alia} other {# aliaj}} diskonigis vian afiŝon", + "notification.relationships_severance_event": "Perditaj konektoj kun {name}", + "notification.relationships_severance_event.account_suspension": "Administranto de {from} malakceptis {target}, kio signifas, ke vi ne plu povas ricevi ĝisdatigojn de ili aŭ interagi kun ili.", + "notification.relationships_severance_event.domain_block": "Administranto de {from} blokis {target}, inkluzive de {followersCount} de viaj sekvantoj kaj {followingCount, plural, one {# konto} other {# kontoj}} kiujn vi sekvas.", "notification.relationships_severance_event.learn_more": "Lerni pli", + "notification.relationships_severance_event.user_domain_block": "Vi blokis {target}, forigante {followersCount} de viaj sekvantoj kaj {followingCount, plural, one {# konto} other {# kontoj}} kiujn vi sekvas.", "notification.status": "{name} ĵus afiŝis", "notification.update": "{name} redaktis afiŝon", "notification_requests.accept": "Akcepti", "notification_requests.accept_multiple": "{count, plural, one {Akcepti # peton…} other {Akcepti # petojn…}}", "notification_requests.confirm_accept_multiple.button": "{count, plural, one {Akcepti peton} other {Akcepti petojn}}", + "notification_requests.confirm_accept_multiple.message": "Vi estas akceptonta {count, plural, one {unu sciigan peton} other {# sciigajn petojn}}. Ĉu vi certas, ke vi volas daŭrigi?", "notification_requests.confirm_accept_multiple.title": "Ĉu akcepti sciigajn petojn?", "notification_requests.confirm_dismiss_multiple.button": "{count, plural, one {Malakcepti peton} other {Malakcepti petojn}}", + "notification_requests.confirm_dismiss_multiple.message": "Vi estas malakceptonta {count, plural, one {unu sciigan peton} other {# sciigajn petojn}}. Vi ne povos facile aliri {count, plural, one {ĝin} other {ilin}} denove. Ĉu vi certas, ke vi volas daŭrigi?", "notification_requests.confirm_dismiss_multiple.title": "Ĉu malakcepti sciigajn petojn?", "notification_requests.dismiss": "Forĵeti", + "notification_requests.dismiss_multiple": "{count, plural, one {Malakcepti # peton…} other {# Malakcepti # petojn…}}", "notification_requests.edit_selection": "Redakti", "notification_requests.exit_selection": "Farita", "notification_requests.explainer_for_limited_account": "Sciigoj de ĉi tiu konto estis filtritaj ĉar la konto estis limigita de moderanto.", "notification_requests.explainer_for_limited_remote_account": "Sciigoj de ĉi tiu konto estis filtritaj ĉar la konto aŭ ĝia servilo estis limigitaj de moderanto.", + "notification_requests.maximize": "Maksimumigi", + "notification_requests.minimize_banner": "Minimumigi filtritajn sciigojn-rubandon", "notification_requests.notifications_from": "Sciigoj de {name}", "notification_requests.title": "Filtritaj sciigoj", "notification_requests.view": "Vidi sciigojn", @@ -533,6 +561,7 @@ "notifications.column_settings.alert": "Sciigoj de la retumilo", "notifications.column_settings.favourite": "Stelumoj:", "notifications.column_settings.filter_bar.advanced": "Montri ĉiujn kategoriojn", + "notifications.column_settings.filter_bar.category": "Rapida filtrila breto", "notifications.column_settings.follow": "Novaj sekvantoj:", "notifications.column_settings.follow_request": "Novaj petoj de sekvado:", "notifications.column_settings.mention": "Mencioj:", @@ -548,7 +577,7 @@ "notifications.filter.all": "Ĉiuj", "notifications.filter.boosts": "Diskonigoj", "notifications.filter.favourites": "Stelumoj", - "notifications.filter.follows": "Sekvoj", + "notifications.filter.follows": "Sekvatoj", "notifications.filter.mentions": "Mencioj", "notifications.filter.polls": "Balotenketaj rezultoj", "notifications.filter.statuses": "Ĝisdatigoj de homoj, kiujn vi sekvas", @@ -563,12 +592,16 @@ "notifications.policy.drop": "Ignori", "notifications.policy.drop_hint": "Sendi al la malpleno, por neniam esti vidita denove", "notifications.policy.filter": "Filtri", + "notifications.policy.filter_hint": "Sendi al filtritaj sciigoj-enirkesto", + "notifications.policy.filter_limited_accounts_hint": "Limigita de servilaj moderigantoj", "notifications.policy.filter_limited_accounts_title": "Moderigitaj kontoj", "notifications.policy.filter_new_accounts.hint": "Kreite en la {days, plural, one {lasta tago} other {# lastaj tagoj}}", "notifications.policy.filter_new_accounts_title": "Novaj kontoj", + "notifications.policy.filter_not_followers_hint": "Inkluzive de homoj, kiuj sekvis vin malpli ol {days, plural, one {unu tago} other {# tagoj}}", "notifications.policy.filter_not_followers_title": "Homoj, kiuj ne sekvas vin", "notifications.policy.filter_not_following_hint": "Ĝis vi permane aprobas ilin", "notifications.policy.filter_not_following_title": "Homoj, kiujn vi ne sekvas", + "notifications.policy.filter_private_mentions_hint": "Filtrite krom se ĝi respondas al via propra mencio aŭ se vi sekvas la sendinton", "notifications.policy.filter_private_mentions_title": "Nepetitaj privataj mencioj", "notifications.policy.title": "Administri sciigojn de…", "notifications_permission_banner.enable": "Ŝalti retumilajn sciigojn", @@ -580,8 +613,8 @@ "onboarding.actions.go_to_home": "Go to your home feed", "onboarding.compose.template": "Saluton #Mastodon!", "onboarding.follows.empty": "Bedaŭrinde, neniu rezulto estas montrebla nuntempe. Vi povas provi serĉi aŭ foliumi la esploran paĝon por trovi kontojn por sekvi, aŭ retrovi baldaŭ.", - "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": "Via hejma fluo estas la ĉefa maniero sperti Mastodon. Ju pli da homoj vi sekvas, des pli aktiva kaj interesa ĝi estos. Por komenci, jen kelkaj sugestoj:", + "onboarding.follows.title": "Agordi vian hejman fluon", "onboarding.profile.discoverable": "Trovebligi mian profilon", "onboarding.profile.discoverable_hint": "Kiam vi aliĝi al trovebleco ĉe Mastodon, viaj afiŝoj eble aperos en serĉaj rezultoj kaj populariĝoj, kaj via profilo eble estas sugestota al personoj kun similaj intereseoj al vi.", "onboarding.profile.display_name": "Publika nomo", @@ -602,7 +635,7 @@ "onboarding.start.title": "Vi atingas ĝin!", "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.body": "Salutu la mondon per teksto, fotoj, filmetoj aŭ balotenketoj {emoji}", "onboarding.steps.publish_status.title": "Fari vian unuan afiŝon", "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", @@ -632,6 +665,7 @@ "privacy.private.short": "Sekvantoj", "privacy.public.long": "Ĉiujn ajn ĉe kaj ekster Mastodon", "privacy.public.short": "Publika", + "privacy.unlisted.additional": "Ĉi tio kondutas ekzakte kiel publika, krom ke la afiŝo ne aperos en vivaj fluoj aŭ kradvortoj, esploro aŭ Mastodon-serĉo, eĉ se vi estas enskribita en la tuta konto.", "privacy.unlisted.long": "Malpli algoritmaj fanfaroj", "privacy.unlisted.short": "Diskrete publika", "privacy_policy.last_updated": "Laste ĝisdatigita en {date}", @@ -651,7 +685,9 @@ "relative_time.minutes": "{number}m", "relative_time.seconds": "{number}s", "relative_time.today": "hodiaŭ", + "reply_indicator.attachments": "{count, plural, one {# aldonaĵo} other {# aldonaĵoj}}", "reply_indicator.cancel": "Nuligi", + "reply_indicator.poll": "Balotenketo", "report.block": "Bloki", "report.block_explanation": "Vi ne vidos iliajn afiŝojn. Ili ne povos vidi viajn afiŝojn, nek sekvi vin. Ili ne scios, ke vi blokas ilin.", "report.categories.legal": "Laŭleĝa", @@ -732,6 +768,7 @@ "server_banner.server_stats": "Statistikoj de la servilo:", "sign_in_banner.create_account": "Krei konton", "sign_in_banner.follow_anyone": "Sekvi iun ajn tra la fediverso kaj vidi ĉion en kronologia ordo. Neniuj algoritmoj, reklamoj aŭ klakbetoj videblas.", + "sign_in_banner.mastodon_is": "Mastodonto estas la plej bona maniero por resti flank-al-flanke kun kio okazas.", "sign_in_banner.sign_in": "Saluti", "sign_in_banner.sso_redirect": "Ensalutu aŭ Registriĝi", "status.admin_account": "Malfermi fasadon de moderigado por @{name}", @@ -741,6 +778,7 @@ "status.bookmark": "Aldoni al la legosignoj", "status.cancel_reblog_private": "Ne plu diskonigi", "status.cannot_reblog": "Ĉi tiun afiŝon ne eblas diskonigi", + "status.continued_thread": "Daŭrigis fadenon", "status.copy": "Kopii la ligilon al la mesaĝo", "status.delete": "Forigi", "status.detailed_status": "Detala konversacia vido", @@ -749,6 +787,7 @@ "status.edit": "Redakti", "status.edited": "Laste redaktita {date}", "status.edited_x_times": "Redactita {count, plural, one {{count} fojon} other {{count} fojojn}}", + "status.embed": "Akiri enkorpigan kodon", "status.favourite": "Ŝatata", "status.favourites": "{count, plural, one {plej ŝatata} other {plej ŝatataj}}", "status.filter": "Filtri ĉi tiun afiŝon", @@ -773,6 +812,7 @@ "status.reblogs.empty": "Ankoraŭ neniu diskonigis tiun afiŝon. Kiam iu faras tion, ri aperos ĉi tie.", "status.redraft": "Forigi kaj reskribi", "status.remove_bookmark": "Forigi legosignon", + "status.replied_in_thread": "Respondis en fadeno", "status.replied_to": "Respondis al {name}", "status.reply": "Respondi", "status.replyAll": "Respondi al la fadeno", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index 6ab3f0fdb9..8a37a33df2 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -435,7 +435,7 @@ "lightbox.next": "Siguiente", "lightbox.previous": "Anterior", "lightbox.zoom_in": "Ampliar al tamaño real", - "lightbox.zoom_out": "Ampliar para ajustar", + "lightbox.zoom_out": "Ampliar hasta ajustar", "limited_account_hint.action": "Mostrar perfil de todos modos", "limited_account_hint.title": "Este perfil fue ocultado por los moderadores de {domain}.", "link_preview.author": "Por {name}", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index df05e82b0b..5915c333c4 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -267,7 +267,7 @@ "empty_column.favourites": "Todavía nadie marcó esta publicación como favorita. Cuando alguien lo haga, se mostrarán aquí.", "empty_column.follow_requests": "No tienes ninguna petición de seguidor. Cuando recibas una, se mostrará aquí.", "empty_column.followed_tags": "No has seguido ninguna etiqueta todavía. Cuando lo hagas, se mostrarán aquí.", - "empty_column.hashtag": "No hay nada en este hashtag aún.", + "empty_column.hashtag": "No hay nada en esta etiqueta todavía.", "empty_column.home": "¡Tu línea temporal está vacía! Sigue a más personas para rellenarla.", "empty_column.list": "Aún no hay nada en esta lista. Cuando los miembros de esta lista publiquen nuevos estados, estos aparecerán aquí.", "empty_column.lists": "No tienes ninguna lista. Cuando crees una, se mostrará aquí.", @@ -342,7 +342,7 @@ "hashtag.column_header.tag_mode.any": "o {additional}", "hashtag.column_header.tag_mode.none": "sin {additional}", "hashtag.column_settings.select.no_options_message": "No se encontraron sugerencias", - "hashtag.column_settings.select.placeholder": "Introduzca hashtags…", + "hashtag.column_settings.select.placeholder": "Introduce etiquetas…", "hashtag.column_settings.tag_mode.all": "Todos estos", "hashtag.column_settings.tag_mode.any": "Cualquiera de estos", "hashtag.column_settings.tag_mode.none": "Ninguno de estos", @@ -637,7 +637,7 @@ "onboarding.steps.follow_people.title": "Personaliza tu línea de inicio", "onboarding.steps.publish_status.body": "Di hola al mundo con texto, fotos, vídeos o encuestas {emoji}", "onboarding.steps.publish_status.title": "Escribe tu primera publicación", - "onboarding.steps.setup_profile.body": "Aumenta tus interacciones tcompletando tu perfil.", + "onboarding.steps.setup_profile.body": "Aumenta tus interacciones con un perfil completo.", "onboarding.steps.setup_profile.title": "Personaliza tu perfil", "onboarding.steps.share_profile.body": "¡Dile a tus amigos cómo encontrarte en Mastodon!", "onboarding.steps.share_profile.title": "Comparte tu perfil de Mastodon", diff --git a/app/javascript/mastodon/locales/gd.json b/app/javascript/mastodon/locales/gd.json index 6cd3cbcc56..8f51d43ef8 100644 --- a/app/javascript/mastodon/locales/gd.json +++ b/app/javascript/mastodon/locales/gd.json @@ -434,6 +434,8 @@ "lightbox.close": "Dùin", "lightbox.next": "Air adhart", "lightbox.previous": "Air ais", + "lightbox.zoom_in": "Sùm dhan fhìor-mheud", + "lightbox.zoom_out": "Sùm fèin-obrachail", "limited_account_hint.action": "Seall a’ phròifil co-dhiù", "limited_account_hint.title": "Chaidh a’ phròifil seo fhalach le maoir {domain}.", "link_preview.author": "Le {name}", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 5e67cbce25..f2ea6ee900 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -368,13 +368,13 @@ "home.pending_critical_update.link": "Mira as actualizacións", "home.pending_critical_update.title": "Hai una actualización crítica de seguridade!", "home.show_announcements": "Amosar anuncios", - "ignore_notifications_modal.disclaimer": "Mastodon non pode informar ás usuarias se ignoraches as súas notificacións. Ao ignorar as notificacións non evitarás que as mensaxes sexan enviadas igualmente.", + "ignore_notifications_modal.disclaimer": "Mastodon non pode informar ás usuarias de que ignoraches as súas notificacións. Ao ignorar as notificacións non evitarás que as mensaxes sexan enviadas igualmente.", "ignore_notifications_modal.filter_instead": "Filtrar igualmente", "ignore_notifications_modal.filter_to_act_users": "Poderás seguir aceptando, rexeitando e denunciando usuarias", "ignore_notifications_modal.filter_to_avoid_confusion": "Ao filtrar axudas a evitar posibles confusións", - "ignore_notifications_modal.filter_to_review_separately": "Podes revisar as notificacións filtradas por separado", + "ignore_notifications_modal.filter_to_review_separately": "Podes revisar por separado as notificacións filtradas", "ignore_notifications_modal.ignore": "Ignorar notificacións", - "ignore_notifications_modal.limited_accounts_title": "Ignorar notificacións desde contas moderadas?", + "ignore_notifications_modal.limited_accounts_title": "Ignorar notificacións desde contas limitadas?", "ignore_notifications_modal.new_accounts_title": "Ignorar notificacións desde novas contas?", "ignore_notifications_modal.not_followers_title": "Ignorar notificacións de persoas que non te seguen?", "ignore_notifications_modal.not_following_title": "Ignorar notificacións de persoas que non segues?", @@ -463,7 +463,7 @@ "mute_modal.hide_options": "Opcións ao ocultar", "mute_modal.indefinite": "Ata que as reactive", "mute_modal.show_options": "Mostrar opcións", - "mute_modal.they_can_mention_and_follow": "Pódete mencionar e seguirte, pero non o verás.", + "mute_modal.they_can_mention_and_follow": "Pódete mencionar e seguirte, pero non a verás.", "mute_modal.they_wont_know": "Non saberá que a acalaches.", "mute_modal.title": "Acalar usuaria?", "mute_modal.you_wont_see_mentions": "Non verás as publicacións que a mencionen.", diff --git a/config/locales/activerecord.be.yml b/config/locales/activerecord.be.yml index db6b632dc4..b1495c2855 100644 --- a/config/locales/activerecord.be.yml +++ b/config/locales/activerecord.be.yml @@ -15,6 +15,8 @@ be: user/invite_request: text: Прычына errors: + messages: + too_many_lines: перавышана абмежаванне ў %{limit} радкоў models: account: attributes: diff --git a/config/locales/activerecord.el.yml b/config/locales/activerecord.el.yml index 1c3bcc6ebf..a476221616 100644 --- a/config/locales/activerecord.el.yml +++ b/config/locales/activerecord.el.yml @@ -15,6 +15,12 @@ el: user/invite_request: text: Αιτιολογία errors: + attributes: + domain: + invalid: δεν είναι έγκυρο όνομα τομέα + messages: + invalid_domain_on_line: το %{value} δεν είναι έγκυρο όνομα τομέα + too_many_lines: υπερβαίνει το όριο των %{limit} γραμμών models: account: attributes: diff --git a/config/locales/doorkeeper.af.yml b/config/locales/doorkeeper.af.yml index 9e05f403f6..d32730468a 100644 --- a/config/locales/doorkeeper.af.yml +++ b/config/locales/doorkeeper.af.yml @@ -60,7 +60,6 @@ af: error: title: "’n Fout het ingesluip" new: - prompt_html: "%{client_name} wil toegang hê tot jou rekening. Dit is ’n derdepartytoepassing. Moet dit nie toelaat as jy dit nie vertrou nie." review_permissions: Hersien toestemmings title: Benodig magtiging show: diff --git a/config/locales/doorkeeper.an.yml b/config/locales/doorkeeper.an.yml index 1096d8c8dc..bee55771de 100644 --- a/config/locales/doorkeeper.an.yml +++ b/config/locales/doorkeeper.an.yml @@ -60,7 +60,6 @@ an: error: title: Ha ocurriu una error new: - prompt_html: "%{client_name} deseya permiso pa acceder ta la tuya cuenta. Ye una aplicación de tercers. Si no confías en ella, no habrías d'autorizar-la." review_permissions: Revisar permisos title: Se requiere autorización show: diff --git a/config/locales/doorkeeper.ar.yml b/config/locales/doorkeeper.ar.yml index 79e6c60152..8dea8b4894 100644 --- a/config/locales/doorkeeper.ar.yml +++ b/config/locales/doorkeeper.ar.yml @@ -60,7 +60,6 @@ ar: error: title: حدث هناك خطأ new: - prompt_html: يريد %{client_name} الإذن للوصول إلى حسابك. إنه تطبيق طرف ثالث. إذا كنت لا تثق فيه، فلا ينبغي أن تأذن له بذلك. review_permissions: مراجعة الصلاحيات title: إذن بالتصريح show: diff --git a/config/locales/doorkeeper.ast.yml b/config/locales/doorkeeper.ast.yml index 0df15a07b6..c9c831f7f5 100644 --- a/config/locales/doorkeeper.ast.yml +++ b/config/locales/doorkeeper.ast.yml @@ -41,7 +41,6 @@ ast: error: title: Prodúxose un error new: - prompt_html: "%{client_name}, que ye una aplicación de terceros, quier tener accesu a la cuenta. Si nun t'enfotes nella, nun habríes autorizala." review_permissions: Revisión de los permisos show: title: Copia esti códigu d'autorización y apiégalu na aplicación. diff --git a/config/locales/doorkeeper.be.yml b/config/locales/doorkeeper.be.yml index 75f9930e1a..defe0ddf0d 100644 --- a/config/locales/doorkeeper.be.yml +++ b/config/locales/doorkeeper.be.yml @@ -60,7 +60,6 @@ be: error: title: Узнікла памылка new: - prompt_html: "%{client_name} хацеў бы атрымаць дазвол для доступу да вашага акаунта. Гэта вонкавае прыкладанне. Не давайце дазволу на гэта, калі вы не давяраеце гэтаму прыкладанню. " review_permissions: Прагледзець дазволы title: Патрабуецца аўтарызацыя show: diff --git a/config/locales/doorkeeper.bg.yml b/config/locales/doorkeeper.bg.yml index a1ef177854..25dce0ea08 100644 --- a/config/locales/doorkeeper.bg.yml +++ b/config/locales/doorkeeper.bg.yml @@ -60,7 +60,6 @@ bg: error: title: Възникна грешка new: - prompt_html: "%{client_name} иска разрешение да има достъп до акаунта ви. Приложение от трета страна е.Ако не му се доверявате, то може да не го упълномощявате." review_permissions: Преглед на разрешенията title: Изисква се упълномощаване show: diff --git a/config/locales/doorkeeper.ca.yml b/config/locales/doorkeeper.ca.yml index 85a09b5b43..590339fe88 100644 --- a/config/locales/doorkeeper.ca.yml +++ b/config/locales/doorkeeper.ca.yml @@ -60,7 +60,7 @@ ca: error: title: S'ha produït un error new: - prompt_html: "%{client_name} vol permís per accedir el teu compte. És una aplicació de tercers. Si no hi confies, no hauries d'autoritzar-la." + prompt_html: "%{client_name} demana accés al vostre compte. Només aproveu aquesta petició si reconeixeu i confieu en aquest origen." review_permissions: Revisa els permisos title: Cal autorizació show: diff --git a/config/locales/doorkeeper.ckb.yml b/config/locales/doorkeeper.ckb.yml index f952b6eca9..b318bce859 100644 --- a/config/locales/doorkeeper.ckb.yml +++ b/config/locales/doorkeeper.ckb.yml @@ -60,7 +60,6 @@ ckb: error: title: هەڵەیەک ڕوویدا new: - prompt_html: "%{client_name} حەز دەکات مۆڵەت بدرێت بۆ چوونە ناو ئەکاونتەکەت. ئەپڵیکەیشنێکی لایەنی سێیەمە. ئەگەر متمانەت پێی نییە، ئەوا نابێت ڕێگەی پێبدەیت." review_permissions: پێداچوونەوە بە مۆڵەتەکاندا بکە title: ڕێپێدان پێویستە show: diff --git a/config/locales/doorkeeper.cs.yml b/config/locales/doorkeeper.cs.yml index 1b98d0f88b..882be66ee0 100644 --- a/config/locales/doorkeeper.cs.yml +++ b/config/locales/doorkeeper.cs.yml @@ -60,7 +60,6 @@ cs: error: title: Vyskytla se chyba new: - prompt_html: "%{client_name} si přeje oprávnění pro přístup k vašemu účtu. Je to aplikace třetí strany. Pokud jí nedůvěřujete, pak byste ji neměli autorizovat." review_permissions: Zkontrolujte oprávnění title: Je vyžadována autorizace show: diff --git a/config/locales/doorkeeper.cy.yml b/config/locales/doorkeeper.cy.yml index f15d74d721..80cc022a0b 100644 --- a/config/locales/doorkeeper.cy.yml +++ b/config/locales/doorkeeper.cy.yml @@ -60,7 +60,6 @@ cy: error: title: Mae rhywbeth wedi mynd o'i le new: - prompt_html: Hoffai %{client_name} gael caniatâd i gael mynediad i'ch cyfrif. Mae'n gais trydydd parti. Os nad ydych yn ymddiried ynddo, yna peidiwch a'i awdurdodi. review_permissions: Adolygu caniatâd title: Angen awdurdodi show: diff --git a/config/locales/doorkeeper.da.yml b/config/locales/doorkeeper.da.yml index cd11dcf4e3..7ac16e0012 100644 --- a/config/locales/doorkeeper.da.yml +++ b/config/locales/doorkeeper.da.yml @@ -60,7 +60,7 @@ da: error: title: En fejl opstod new: - prompt_html: "%{client_name} ønsker tilladelse til at tilgå din konto. Den er en tredjepartsapplikation. Har du ikke tillid til den, bør den ikke godkendes." + prompt_html: "%{client_name} vil ønsker tilladelse til at tilgå din konto. Godkend kun denne anmodning, hvis kilden genkendes, og man stoler på den." review_permissions: Gennemgå tilladelser title: Godkendelse kræves show: diff --git a/config/locales/doorkeeper.de.yml b/config/locales/doorkeeper.de.yml index b29d668b2e..6d0e3010af 100644 --- a/config/locales/doorkeeper.de.yml +++ b/config/locales/doorkeeper.de.yml @@ -60,7 +60,7 @@ de: error: title: Ein Fehler ist aufgetreten new: - prompt_html: "%{client_name} möchte auf dein Konto zugreifen. Es handelt sich um eine Software von Dritten. Wenn du der Anwendung nicht vertraust, solltest du ihr keinen Zugriff auf dein Konto geben." + prompt_html: "%{client_name} möchte auf dein Konto zugreifen. Du solltest diese Anfrage nur genehmigen, wenn du diese Quelle kennst und ihr vertraust." review_permissions: Berechtigungen überprüfen title: Autorisierung erforderlich show: diff --git a/config/locales/doorkeeper.el.yml b/config/locales/doorkeeper.el.yml index 7fcd69c238..59877b6bd0 100644 --- a/config/locales/doorkeeper.el.yml +++ b/config/locales/doorkeeper.el.yml @@ -60,7 +60,6 @@ el: error: title: Εμφανίστηκε σφάλμα new: - prompt_html: Το %{client_name} θα ήθελε άδεια πρόσβασης στο λογαριασμό σου. Είναι μια εφαρμογή τρίτων. Αν δεν το εμπιστεύεσαι, τότε δεν πρέπει να το εγκρίνεις. review_permissions: Ανασκόπηση δικαιωμάτων title: Απαιτείται έγκριση show: diff --git a/config/locales/doorkeeper.en-GB.yml b/config/locales/doorkeeper.en-GB.yml index 999284e85f..63a4575e83 100644 --- a/config/locales/doorkeeper.en-GB.yml +++ b/config/locales/doorkeeper.en-GB.yml @@ -60,7 +60,7 @@ en-GB: error: title: An error has occurred new: - prompt_html: "%{client_name} would like permission to access your account. It is a third-party application. If you do not trust it, then you should not authorise it." + prompt_html: "%{client_name} would like permission to access your account. Only approve this request if you recognise and trust this source." review_permissions: Review permissions title: Authorisation required show: diff --git a/config/locales/doorkeeper.eo.yml b/config/locales/doorkeeper.eo.yml index 8bb61c7a49..12e120f8be 100644 --- a/config/locales/doorkeeper.eo.yml +++ b/config/locales/doorkeeper.eo.yml @@ -60,7 +60,6 @@ eo: error: title: Eraro okazis new: - prompt_html: "%{client_name} petas permeson por aliri vian konton. Se vi ne fidas ĝin, ne rajtigu ĝin." review_permissions: Revizu permesojn title: Rajtigo bezonata show: diff --git a/config/locales/doorkeeper.es-AR.yml b/config/locales/doorkeeper.es-AR.yml index 91f4191247..9f3c862272 100644 --- a/config/locales/doorkeeper.es-AR.yml +++ b/config/locales/doorkeeper.es-AR.yml @@ -60,7 +60,7 @@ es-AR: error: title: Ocurrió un error new: - prompt_html: "%{client_name} solicita permiso para acceder a tu cuenta. Es una aplicación de terceros. Si no confiás en ella, no deberías autorizarla." + prompt_html: "%{client_name} le gustaría obtener permiso para acceder a tu cuenta. Solo aprueba esta solicitud si reconoces y confías en esta fuente." review_permissions: Revisar permisos title: Autorización requerida show: diff --git a/config/locales/doorkeeper.es-MX.yml b/config/locales/doorkeeper.es-MX.yml index 49ff9a1e43..419bb58f95 100644 --- a/config/locales/doorkeeper.es-MX.yml +++ b/config/locales/doorkeeper.es-MX.yml @@ -60,7 +60,7 @@ es-MX: error: title: Ha ocurrido un error new: - prompt_html: "%{client_name} requiere permiso para acceder a tu cuenta. Es una aplicación de terceros. Si no confías en esta, no deberías autorizarla." + prompt_html: "%{client_name} le gustaría obtener permiso para acceder a tu cuenta. Solo aprueba esta solicitud si reconoces y confías en esta fuente." review_permissions: Revisar permisos title: Se requiere autorización show: diff --git a/config/locales/doorkeeper.es.yml b/config/locales/doorkeeper.es.yml index b3c7ccddd4..093d84397a 100644 --- a/config/locales/doorkeeper.es.yml +++ b/config/locales/doorkeeper.es.yml @@ -60,7 +60,7 @@ es: error: title: Ha ocurrido un error new: - prompt_html: "%{client_name} desea permiso para acceder a tu cuenta. Es una aplicación de terceros. Si no confías en ella, no deberías autorizarla." + prompt_html: "%{client_name} le gustaría obtener permiso para acceder a tu cuenta. Solo aprueba esta solicitud si reconoces y confías en esta fuente." review_permissions: Revisar permisos title: Se requiere autorización show: diff --git a/config/locales/doorkeeper.et.yml b/config/locales/doorkeeper.et.yml index ffc42239a9..ebfaf5c710 100644 --- a/config/locales/doorkeeper.et.yml +++ b/config/locales/doorkeeper.et.yml @@ -60,7 +60,6 @@ et: error: title: Ilmnes viga new: - prompt_html: "%{client_name} soovib luba kontole juurdepääsuks. See on kolmanda osapoole rakendus. Kui see pole usaldusväärne, siis ei tohiks seda lubada." review_permissions: Lubade ülevaade title: Autoriseerimine vajalik show: diff --git a/config/locales/doorkeeper.eu.yml b/config/locales/doorkeeper.eu.yml index e7963672fa..a09fe42f40 100644 --- a/config/locales/doorkeeper.eu.yml +++ b/config/locales/doorkeeper.eu.yml @@ -60,7 +60,6 @@ eu: error: title: Errore bat gertatu da new: - prompt_html: "%{client_name} bezeroak zure kontura sartzeko baimena nahi du. Hirugarrengoen aplikazio bat da. Ez bazara fidatzen, ez zenuke baimendu behar." review_permissions: Berrikusi baimenak title: Baimena behar da show: diff --git a/config/locales/doorkeeper.fa.yml b/config/locales/doorkeeper.fa.yml index 0ce7a9591d..4ff03950b5 100644 --- a/config/locales/doorkeeper.fa.yml +++ b/config/locales/doorkeeper.fa.yml @@ -60,7 +60,6 @@ fa: error: title: خطایی رخ داد new: - prompt_html: "%{client_name} خواهان اجازه دسترسی به حساب کاربری شماست. اگر به آن اعتماد ندارید، نباید تاییدش کنید." review_permissions: بازبینی اجازه‌ها title: نیاز به اجازه دادن show: diff --git a/config/locales/doorkeeper.fi.yml b/config/locales/doorkeeper.fi.yml index ce8aef4a9f..7d44a6a6b9 100644 --- a/config/locales/doorkeeper.fi.yml +++ b/config/locales/doorkeeper.fi.yml @@ -60,7 +60,7 @@ fi: error: title: Tapahtui virhe new: - prompt_html: "%{client_name} pyytää oikeutta käyttää tiliäsi. Se on kolmannen osapuolen sovellus. Jos et luota siihen, älä valtuuta sitä." + prompt_html: "%{client_name} haluaisi käyttöoikeuden tiliisi. Hyväksy tämä pyyntö vain, jos tunnistat lähteen ja luotat siihen." review_permissions: Tarkista käyttöoikeudet title: Valtuutus vaaditaan show: diff --git a/config/locales/doorkeeper.fo.yml b/config/locales/doorkeeper.fo.yml index 29f8cd3387..b6c1998b3a 100644 --- a/config/locales/doorkeeper.fo.yml +++ b/config/locales/doorkeeper.fo.yml @@ -60,7 +60,7 @@ fo: error: title: Ein feilur er íkomin new: - prompt_html: "%{client_name} kundi hugsa sær atgongd til tína kontu. Tað er ein triðjaparts-nýtsluskipan. Tú skal ikki geva henni hesa heimld, um tú ikki hevur álit á henni." + prompt_html: "%{client_name} kundi hugsað sær atgongd til tína kontu. Góðtak einans hesa umbøn, um tú kennir hesa keldu aftur og hevur álit á henni." review_permissions: Eftirkanna rættindi title: Váttan kravd show: diff --git a/config/locales/doorkeeper.fr-CA.yml b/config/locales/doorkeeper.fr-CA.yml index f06cc7804d..f3dad084b5 100644 --- a/config/locales/doorkeeper.fr-CA.yml +++ b/config/locales/doorkeeper.fr-CA.yml @@ -60,7 +60,6 @@ fr-CA: error: title: Une erreur est survenue new: - prompt_html: "%{client_name} souhaite accéder à votre compte. Il s'agit d'une application tierce. Si vous ne lui faites pas confiance, vous ne devriez pas l'y autoriser." review_permissions: Examiner les autorisations title: Autorisation requise show: diff --git a/config/locales/doorkeeper.fr.yml b/config/locales/doorkeeper.fr.yml index cf8d1f08b4..fac58460c6 100644 --- a/config/locales/doorkeeper.fr.yml +++ b/config/locales/doorkeeper.fr.yml @@ -60,7 +60,6 @@ fr: error: title: Une erreur est survenue new: - prompt_html: "%{client_name} souhaite accéder à votre compte. Il s'agit d'une application tierce. Vous ne devriez pas l'y autoriser si vous ne lui faites pas confiance." review_permissions: Examiner les autorisations title: Autorisation requise show: diff --git a/config/locales/doorkeeper.fy.yml b/config/locales/doorkeeper.fy.yml index f15389faf9..94e67d17a6 100644 --- a/config/locales/doorkeeper.fy.yml +++ b/config/locales/doorkeeper.fy.yml @@ -60,7 +60,6 @@ fy: error: title: Der is in flater bard new: - prompt_html: "%{client_name} hat tastimming nedich om tagong te krijen ta jo account. It giet om in tapassing fan in tredde partij.As jo dit net fertrouwe, moatte jo gjin tastimming jaan." review_permissions: Tastimmingen beoardiele title: Autorisaasje fereaske show: diff --git a/config/locales/doorkeeper.ga.yml b/config/locales/doorkeeper.ga.yml index 95c3c4d223..27082028c7 100644 --- a/config/locales/doorkeeper.ga.yml +++ b/config/locales/doorkeeper.ga.yml @@ -60,7 +60,6 @@ ga: 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: diff --git a/config/locales/doorkeeper.gd.yml b/config/locales/doorkeeper.gd.yml index 1db1a90f80..8157a4e5fd 100644 --- a/config/locales/doorkeeper.gd.yml +++ b/config/locales/doorkeeper.gd.yml @@ -60,7 +60,7 @@ gd: error: title: Thachair mearachd new: - prompt_html: Bu mhiann le %{client_name} cead gus an cunntas agad inntrigeadh. Seo aplacaid threas-phàrtaidh. Mur eil earbsa agad ann, na ùghdarraich e. + prompt_html: Bu toigh le %{client_name} cead fhaighinn airson an cunntas agad inntrigeadh. Na gabh ris an iarrtas seo ach mas aithne dhut an tùs seo agus earbsa agad ann. review_permissions: Thoir sùil air na ceadan title: Tha feum air ùghdarrachadh show: diff --git a/config/locales/doorkeeper.gl.yml b/config/locales/doorkeeper.gl.yml index 2103e50ea1..adee6bd3d3 100644 --- a/config/locales/doorkeeper.gl.yml +++ b/config/locales/doorkeeper.gl.yml @@ -60,7 +60,7 @@ gl: error: title: Algo fallou new: - prompt_html: "%{client_name} solicita permiso para acceder á túa conta. É unha aplicación de terceiros. Se non confías nela, non deberías autorizala." + prompt_html: "%{client_name} solicita permiso para acceder á túa conta. Aproba esta solicitude só se recoñeces e confías da súa orixe." review_permissions: Revisar permisos title: Autorización necesaria show: diff --git a/config/locales/doorkeeper.he.yml b/config/locales/doorkeeper.he.yml index 16a8fc94cf..a454c779f1 100644 --- a/config/locales/doorkeeper.he.yml +++ b/config/locales/doorkeeper.he.yml @@ -60,7 +60,7 @@ he: error: title: התרחשה שגיאה new: - prompt_html: "%{client_name} מעוניין בהרשאה לגשת לחשבונך. זוהי אפליקציית צד-שלישי. אם יש סיבה לא לבטוח בה, נא לא לאשר." + prompt_html: היישום %{client_name} מבקש גישה לחשבונך. אשרו רק אם אתם מזהים את הבקשה וסומכים על מקור הבקשה. review_permissions: עיון בהרשאות title: נדרשת הרשאה show: diff --git a/config/locales/doorkeeper.hu.yml b/config/locales/doorkeeper.hu.yml index b3cd00f4be..ff37786d28 100644 --- a/config/locales/doorkeeper.hu.yml +++ b/config/locales/doorkeeper.hu.yml @@ -60,7 +60,6 @@ hu: error: title: Hiba történt new: - prompt_html: "%{client_name} szeretné elérni a fiókodat. Ez egy harmadik féltől származó alkalmazás. Ha nem bízol meg benne, ne addj felhatalmazást neki." review_permissions: Jogosultságok áttekintése title: Engedélyezés szükséges show: diff --git a/config/locales/doorkeeper.ia.yml b/config/locales/doorkeeper.ia.yml index 985d073ab8..6bf5e38506 100644 --- a/config/locales/doorkeeper.ia.yml +++ b/config/locales/doorkeeper.ia.yml @@ -60,7 +60,6 @@ ia: error: title: Un error ha occurrite new: - prompt_html: "%{client_name} vole haber le permission de acceder a tu conto. Illo es un application tertie. Si tu non confide in illo, alora tu non deberea autorisar lo." review_permissions: Revider permissiones title: Autorisation necessari show: diff --git a/config/locales/doorkeeper.id.yml b/config/locales/doorkeeper.id.yml index 3f9a409c21..cccabc0701 100644 --- a/config/locales/doorkeeper.id.yml +++ b/config/locales/doorkeeper.id.yml @@ -60,7 +60,6 @@ id: error: title: Ada yang error new: - prompt_html: "%{client_name} meminta izin untuk mengakses akun Anda. Ini adalah aplikasi pihak ketiga. Jika Anda tidak mempercayainya, Anda boleh tidak mengizinkannya." review_permissions: Tinjau izin title: Izin diperlukan show: diff --git a/config/locales/doorkeeper.ie.yml b/config/locales/doorkeeper.ie.yml index 0119f3573f..6d8951b326 100644 --- a/config/locales/doorkeeper.ie.yml +++ b/config/locales/doorkeeper.ie.yml @@ -60,7 +60,6 @@ ie: error: title: Alquo ha errat new: - prompt_html: "%{client_name}, un aplication de triesim partise, vole permission por accesser tui conto. Si tu ne fide it, ne autorisa it." review_permissions: Inspecter permissiones title: Autorisation besonat show: diff --git a/config/locales/doorkeeper.io.yml b/config/locales/doorkeeper.io.yml index a71fa95841..0384d968be 100644 --- a/config/locales/doorkeeper.io.yml +++ b/config/locales/doorkeeper.io.yml @@ -60,7 +60,6 @@ io: error: title: Eroro eventis new: - prompt_html: "%{client_name} volas permiso por acesar vua konti. Ol esas externa softwaro. Se vu ne fidas, lore vu debas ne yurizar." review_permissions: Kontrolez permisi title: Yurizo bezonesas show: diff --git a/config/locales/doorkeeper.is.yml b/config/locales/doorkeeper.is.yml index 01093f4429..05f2415eea 100644 --- a/config/locales/doorkeeper.is.yml +++ b/config/locales/doorkeeper.is.yml @@ -60,7 +60,6 @@ is: error: title: Villa kom upp new: - prompt_html: "%{client_name} biður um heimild til að fara inn á notandaaðganginn þinn. Þetta er utanaðkomandi hugbúnaður. Ef þú treystir ekki viðkomandi, þá ættir þú ekki að heimila þetta." review_permissions: Yfirfara heimildir title: Auðkenning er nauðsynleg show: diff --git a/config/locales/doorkeeper.it.yml b/config/locales/doorkeeper.it.yml index 9654cb2a20..3a24465cdc 100644 --- a/config/locales/doorkeeper.it.yml +++ b/config/locales/doorkeeper.it.yml @@ -60,7 +60,7 @@ it: error: title: Si è verificato un errore new: - prompt_html: "%{client_name} vorrebbe l'autorizzazione ad accedere al tuo profilo. È un'applicazione di terze parti. Se non ti fidi, non dovresti autorizzarla." + prompt_html: "%{client_name} vorrebbe il permesso di accedere al tuo account. Approva questa richiesta solo se riconosci e ti fidi di questa fonte." review_permissions: Revisiona le autorizzazioni title: Autorizzazione necessaria show: diff --git a/config/locales/doorkeeper.ja.yml b/config/locales/doorkeeper.ja.yml index 38c4d2c1aa..7cfddf50a1 100644 --- a/config/locales/doorkeeper.ja.yml +++ b/config/locales/doorkeeper.ja.yml @@ -60,7 +60,6 @@ ja: error: title: エラーが発生しました new: - prompt_html: "%{client_name}があなたのアカウントにアクセスする許可を求めています。心当たりが無い場合はアクセス許可しないでください。" review_permissions: アクセス許可を確認 title: 認証が必要です show: diff --git a/config/locales/doorkeeper.ko.yml b/config/locales/doorkeeper.ko.yml index f35333f1df..4dabc19e43 100644 --- a/config/locales/doorkeeper.ko.yml +++ b/config/locales/doorkeeper.ko.yml @@ -60,7 +60,6 @@ ko: error: title: 오류가 발생하였습니다 new: - prompt_html: "%{client_name} 제3자 애플리케이션이 귀하의 계정에 접근하기 위한 권한을 요청하고 있습니다. 이 애플리케이션을 신뢰할 수 없다면 이 요청을 승인하지 마십시오." review_permissions: 권한 검토 title: 승인 필요 show: diff --git a/config/locales/doorkeeper.ku.yml b/config/locales/doorkeeper.ku.yml index e3438eb5de..be6f0587c9 100644 --- a/config/locales/doorkeeper.ku.yml +++ b/config/locales/doorkeeper.ku.yml @@ -60,7 +60,6 @@ ku: error: title: Xeletîyek çêbû new: - prompt_html: "%{client_name} mafê dixwaze ku bigihîje ajimêrê te. Ew sepanek aliyê sêyemîn e. Ku tu pê bawer nakî, wê demê divê tu mafê gihiştinê nedî. " review_permissions: Gihiştinan binirxînin title: Destûr kirin pêwîst e show: diff --git a/config/locales/doorkeeper.lad.yml b/config/locales/doorkeeper.lad.yml index c335d67fd6..678a53c91d 100644 --- a/config/locales/doorkeeper.lad.yml +++ b/config/locales/doorkeeper.lad.yml @@ -60,7 +60,6 @@ lad: error: title: Un yerro tiene afitado new: - prompt_html: "%{client_name} kere permiso para akseder tu kuento. Es una aplikasyon de terseros. Si no konfias en eya, no deverias autorizarla." review_permissions: Reviza permisos title: Autorizasyon rekerida show: diff --git a/config/locales/doorkeeper.lt.yml b/config/locales/doorkeeper.lt.yml index 9b3b6558a4..f957e4157c 100644 --- a/config/locales/doorkeeper.lt.yml +++ b/config/locales/doorkeeper.lt.yml @@ -60,7 +60,7 @@ lt: error: title: Įvyko klaida. new: - prompt_html: "%{client_name} norėtų gauti leidimą pasiekti tavo paskyrą. Tai – trečiosios šalies programa. Jei ja nepasitiki, tada neturėtum leisti." + prompt_html: "%{client_name} norėtų gauti leidimą pasiekti tavo paskyrą. Patvirtink šį prašymą tik tada, jei atpažįsti šį šaltinį ir juo pasitiki." review_permissions: Peržiūrėti leidimus title: Privalomas leidimas show: diff --git a/config/locales/doorkeeper.lv.yml b/config/locales/doorkeeper.lv.yml index 0f05adf148..55e288a9d6 100644 --- a/config/locales/doorkeeper.lv.yml +++ b/config/locales/doorkeeper.lv.yml @@ -60,7 +60,6 @@ lv: error: title: Radās kļūda new: - prompt_html: "%{client_name} vēlas saņemt atļauju piekļūt tavam kontam. Tā ir trešās puses lietojumprogramma. Ja tu tam neuzticies, tad nevajadzētu to autorizēt." review_permissions: Pārskatīt atļaujas title: Nepieciešama autorizācija show: diff --git a/config/locales/doorkeeper.ms.yml b/config/locales/doorkeeper.ms.yml index 32fb0044bb..b52824e8c3 100644 --- a/config/locales/doorkeeper.ms.yml +++ b/config/locales/doorkeeper.ms.yml @@ -60,7 +60,6 @@ ms: error: title: Ralat telah berlaku new: - prompt_html: "%{client_name} ingin mendapatkan kebenaran untuk mengakses akaun anda. Ia adalah aplikasi pihak ketiga. Jika anda tidak mempercayainya, maka anda tidak seharusnya membenarkannya." review_permissions: Semak kebenaran title: Kebenaran diperlukan show: diff --git a/config/locales/doorkeeper.my.yml b/config/locales/doorkeeper.my.yml index bce6039eae..0cfe505982 100644 --- a/config/locales/doorkeeper.my.yml +++ b/config/locales/doorkeeper.my.yml @@ -60,7 +60,6 @@ my: error: title: အမှားအယွင်းတစ်ခု ဖြစ်ပေါ်ခဲ့သည် new: - prompt_html: "%{client_name} က သင့်အကောင့်သို့ ဝင်ရောက်ရန် ခွင့်ပြုချက်ရယူလိုပါသည်။ ၎င်းမှာ ပြင်ပကြားခံအက်ပလီကေးရှင်းတစ်ခုဖြစ်သည်။ သင် မယုံကြည်ပါက ၎င်းကိုခွင့်မပြုသင့်ပါ။" review_permissions: ခွင့်ပြုချက်များကို ပြန်လည်သုံးသပ်ပါ title: ခွင့်ပြုချက် လိုအပ်သည် show: diff --git a/config/locales/doorkeeper.nl.yml b/config/locales/doorkeeper.nl.yml index 65ef826d38..c6453761f1 100644 --- a/config/locales/doorkeeper.nl.yml +++ b/config/locales/doorkeeper.nl.yml @@ -60,7 +60,7 @@ nl: error: title: Er is een fout opgetreden new: - prompt_html: "%{client_name} heeft toestemming nodig om toegang te krijgen tot jouw account. Het betreft een third-party-toepassing.Als je dit niet vertrouwt, moet je geen toestemming verlenen." + prompt_html: "%{client_name} vraagt om toegang tot je account. Keur dit verzoek alleen goed als je deze bron herkent en vertrouwt." review_permissions: Toestemmingen beoordelen title: Autorisatie vereist show: diff --git a/config/locales/doorkeeper.nn.yml b/config/locales/doorkeeper.nn.yml index 975ceb43c7..9b9ddba08d 100644 --- a/config/locales/doorkeeper.nn.yml +++ b/config/locales/doorkeeper.nn.yml @@ -60,7 +60,7 @@ nn: error: title: Ein feil har oppstått new: - prompt_html: "%{client_name} ønsker tilgang til kontoen din. Det er ein tredjepartsapplikasjon. Dersom du ikkje stolar på den, bør du ikkje autorisere det." + prompt_html: "%{client_name} ynskjer tilgang til kontoen din. Godkjenn dette berre dersom du kjenner att og stolar på %{client_name}." review_permissions: Sjå gjennom løyve title: Autorisasjon nødvendig show: diff --git a/config/locales/doorkeeper.no.yml b/config/locales/doorkeeper.no.yml index c432f6645c..7b7b9d6534 100644 --- a/config/locales/doorkeeper.no.yml +++ b/config/locales/doorkeeper.no.yml @@ -60,7 +60,6 @@ error: title: En feil oppstod new: - prompt_html: "%{client_name} ønsker tilgang til kontoen din. Det er en tredjeparts applikasjon. Hvis du ikke stoler på den, bør du ikke autorisere den." review_permissions: Gå gjennom tillatelser title: Autorisasjon påkrevd show: diff --git a/config/locales/doorkeeper.oc.yml b/config/locales/doorkeeper.oc.yml index 64bc3a43e2..7f1155a839 100644 --- a/config/locales/doorkeeper.oc.yml +++ b/config/locales/doorkeeper.oc.yml @@ -60,7 +60,6 @@ oc: error: title: I a agut un error new: - prompt_html: "%{client_name} volria l’autorizacion d’accedir a vòstre compte. Es una aplicacion tèrça.Se vos fisatz pas a ela, alara deuriatz pas l’autorizacion." review_permissions: Repassar las autorizacions title: Cal l’autorizacion show: diff --git a/config/locales/doorkeeper.pl.yml b/config/locales/doorkeeper.pl.yml index bf2da6f808..2ff1bb5f45 100644 --- a/config/locales/doorkeeper.pl.yml +++ b/config/locales/doorkeeper.pl.yml @@ -60,7 +60,7 @@ pl: error: title: Wystapił błąd new: - prompt_html: "%{client_name} chciałby uzyskać pozwolenie na dostęp do Twojego konta. Jest to aplikacja zewnętrzna. Jeśli jej nie ufasz, nie powinno się jej autoryzować." + prompt_html: "%{client_name} prosi o dostęp do twojego konta. Tylko zatwierdź tę prośbę, jeżeli ją rozpoznajesz i ufasz." review_permissions: Sprawdź uprawnienia title: Wymagana jest autoryzacja show: diff --git a/config/locales/doorkeeper.pt-BR.yml b/config/locales/doorkeeper.pt-BR.yml index c991850c68..f7f9dce7ea 100644 --- a/config/locales/doorkeeper.pt-BR.yml +++ b/config/locales/doorkeeper.pt-BR.yml @@ -60,7 +60,6 @@ pt-BR: error: title: Ocorreu um erro new: - prompt_html: O %{client_name} gostaria de ter permissão para acessar sua conta. Trata-se de uma aplicação de terceiros. Se você não confia nesta aplicação, então você não deve autorizá-la. review_permissions: Rever permissões title: Autorização necessária show: diff --git a/config/locales/doorkeeper.pt-PT.yml b/config/locales/doorkeeper.pt-PT.yml index 3b4439584b..e2ac275335 100644 --- a/config/locales/doorkeeper.pt-PT.yml +++ b/config/locales/doorkeeper.pt-PT.yml @@ -60,7 +60,6 @@ pt-PT: error: title: Ocorreu um erro new: - prompt_html: "%{client_name} pretende ter permissão para aceder à sua conta. É uma aplicação de terceiros. Se não confia nesta aplicação, então não deve autorizá-la." review_permissions: Rever permissões title: Autorização necessária show: diff --git a/config/locales/doorkeeper.ro.yml b/config/locales/doorkeeper.ro.yml index 7091bcaf7b..9c02501eff 100644 --- a/config/locales/doorkeeper.ro.yml +++ b/config/locales/doorkeeper.ro.yml @@ -60,7 +60,6 @@ ro: error: title: A apărut o eroare new: - prompt_html: "%{client_name} dorește să îți acceseze contul. Este o aplicație terță. Dacă nu aveți încredere în ea, atunci nu ar trebui să o autorizați." review_permissions: Revizuiți permisiunile title: Autorizare necesară show: diff --git a/config/locales/doorkeeper.ru.yml b/config/locales/doorkeeper.ru.yml index 12b7e7e87e..1dcb2093d5 100644 --- a/config/locales/doorkeeper.ru.yml +++ b/config/locales/doorkeeper.ru.yml @@ -60,7 +60,6 @@ ru: error: title: Произошла ошибка new: - prompt_html: "%{client_name} хочет получить доступ к вашему аккаунту. Это стороннее приложение. Если вы ему не доверяете, не разрешайте доступ." review_permissions: Просмотр разрешений title: Требуется авторизация show: diff --git a/config/locales/doorkeeper.sco.yml b/config/locales/doorkeeper.sco.yml index 70341c3c69..6e54c53ca5 100644 --- a/config/locales/doorkeeper.sco.yml +++ b/config/locales/doorkeeper.sco.yml @@ -60,7 +60,6 @@ sco: error: title: A error haes occurrt new: - prompt_html: "%{client_name} wad like permission fir tae access yer accoont. It is a third-party application. Gin ye dinnae trust it, then ye shuidnae authorize it." review_permissions: Luik ower permissions title: Authorization requirt show: diff --git a/config/locales/doorkeeper.si.yml b/config/locales/doorkeeper.si.yml index d3550cf598..43a109b194 100644 --- a/config/locales/doorkeeper.si.yml +++ b/config/locales/doorkeeper.si.yml @@ -60,7 +60,6 @@ si: error: title: දෝෂයක් සිදු වී ඇත new: - prompt_html: "%{client_name} ඔබගේ ගිණුමට ප්‍රවේශ වීමට අවසර ලබා ගැනීමට කැමති වේ. එය තෙවන පාර්ශවීය යෙදුමකි. ඔබ එය විශ්වාස නොකරන්නේ නම්, ඔබ එයට අවසර නොදිය යුතුය." review_permissions: අවසර සමාලෝචනය title: බලය පැවරීමේ අවශ්ය show: diff --git a/config/locales/doorkeeper.sk.yml b/config/locales/doorkeeper.sk.yml index face9db966..774a2648f9 100644 --- a/config/locales/doorkeeper.sk.yml +++ b/config/locales/doorkeeper.sk.yml @@ -60,7 +60,6 @@ sk: error: title: Nastala chyba new: - prompt_html: "%{client_name} žiada o povolenie na prístup k vášmu účtu. Ide o aplikáciu tretej strany. Ak jej nedôverujete, nemali by ste ju povoliť." review_permissions: Preskúmať povolenia title: Je potrebné schválenie show: diff --git a/config/locales/doorkeeper.sl.yml b/config/locales/doorkeeper.sl.yml index f6f64fc87f..3f36c73756 100644 --- a/config/locales/doorkeeper.sl.yml +++ b/config/locales/doorkeeper.sl.yml @@ -60,7 +60,6 @@ sl: error: title: Prišlo je do napake new: - prompt_html: "%{client_name} želi dovoljenje za dostop do vašega računa. Gre za zunanji program. Če mu ne zaupate, mu ne dodelite teh pravic." review_permissions: Preglej dovoljenja title: Potrebna je odobritev show: diff --git a/config/locales/doorkeeper.sq.yml b/config/locales/doorkeeper.sq.yml index 651f90b3b4..1fd9000aa2 100644 --- a/config/locales/doorkeeper.sq.yml +++ b/config/locales/doorkeeper.sq.yml @@ -60,7 +60,6 @@ sq: error: title: Ndodhi një gabim new: - prompt_html: "%{client_name} do të donte leje të hyjë në llogarinë tuaj. Është një aplikacion palësh të treta. Nëse s’i zini besë, atëherë s’duhet ta autorizoni." review_permissions: Shqyrtoni leje title: Lypset autorizim show: diff --git a/config/locales/doorkeeper.sr-Latn.yml b/config/locales/doorkeeper.sr-Latn.yml index 89e84344fd..bce2b1c083 100644 --- a/config/locales/doorkeeper.sr-Latn.yml +++ b/config/locales/doorkeeper.sr-Latn.yml @@ -60,7 +60,6 @@ sr-Latn: error: title: Dogodila se greška new: - prompt_html: "%{client_name} želi dozvolu za pristup tvom nalogu. U pitanju je aplikacija treće strane. Ako smatraš da nije pouzdana, ne bi trebalo da je ovlastiš." review_permissions: Pregledaj dozvole title: Potrebna autorizacija show: diff --git a/config/locales/doorkeeper.sr.yml b/config/locales/doorkeeper.sr.yml index 63b9404784..b6ab6e61c0 100644 --- a/config/locales/doorkeeper.sr.yml +++ b/config/locales/doorkeeper.sr.yml @@ -60,7 +60,6 @@ sr: error: title: Догодила се грешка new: - prompt_html: "%{client_name} жели дозволу за приступ твом налогу. У питању је апликација треће стране. Ако сматраш да није поуздана, не би требало да је овластиш." review_permissions: Прегледај дозволе title: Потребна ауторизација show: diff --git a/config/locales/doorkeeper.sv.yml b/config/locales/doorkeeper.sv.yml index 83927d1097..9f646fd3e4 100644 --- a/config/locales/doorkeeper.sv.yml +++ b/config/locales/doorkeeper.sv.yml @@ -60,7 +60,6 @@ sv: error: title: Ett fel har uppstått new: - prompt_html: "%{client_name} vill ha behörighet att komma åt ditt konto. Det är en applikation från tredje part. Du bör endast godkänna den om du litar på den." review_permissions: Granska behörigheter title: Godkännande krävs show: diff --git a/config/locales/doorkeeper.th.yml b/config/locales/doorkeeper.th.yml index 3735386ba3..e7ed0ba8a3 100644 --- a/config/locales/doorkeeper.th.yml +++ b/config/locales/doorkeeper.th.yml @@ -60,7 +60,6 @@ th: error: title: เกิดข้อผิดพลาด new: - prompt_html: "%{client_name} ต้องการสิทธิอนุญาตเพื่อเข้าถึงบัญชีของคุณ แอปพลิเคชันเป็นแอปพลิเคชันจากบุคคลที่สาม หากคุณไม่เชื่อถือแอปพลิเคชัน คุณไม่ควรอนุญาตแอปพลิเคชัน" review_permissions: ตรวจทานสิทธิอนุญาต title: ต้องการการอนุญาต show: diff --git a/config/locales/doorkeeper.tr.yml b/config/locales/doorkeeper.tr.yml index 41996d9603..f7f67564d2 100644 --- a/config/locales/doorkeeper.tr.yml +++ b/config/locales/doorkeeper.tr.yml @@ -60,7 +60,7 @@ tr: error: title: Bir hata oluştu new: - prompt_html: "%{client_name} hesabınıza erişme izni istiyor. Bu üçüncü taraf bir uygulamadır. Eğer güvenmiyorsanız, izin vermemelisiniz." + prompt_html: "%{client_name} hesabınıze erişmek için izin istiyor. Bu isteği sadece bu kaynağı tanıyor ve güveniyorsanız onaylayın." review_permissions: İzinleri incele title: İzin gerekli show: diff --git a/config/locales/doorkeeper.uk.yml b/config/locales/doorkeeper.uk.yml index 55b91fd69d..1e18eaef9a 100644 --- a/config/locales/doorkeeper.uk.yml +++ b/config/locales/doorkeeper.uk.yml @@ -60,7 +60,7 @@ uk: error: title: Сталася помилка new: - prompt_html: "%{client_name} хоче отримати доступ до вашого облікового запису. Це сторонній застосунок. Якщо ви йому не довіряєте, не варто авторизувати його." + prompt_html: "%{client_name} хоче отримати дозвіл на доступ до вашого облікового запису. Схвалюйте цей запит, якщо ви впізнаєте це джерело і довіряєте йому." review_permissions: Переглянути дозволи title: Необхідна авторизація show: diff --git a/config/locales/doorkeeper.vi.yml b/config/locales/doorkeeper.vi.yml index 5837961737..195e527f70 100644 --- a/config/locales/doorkeeper.vi.yml +++ b/config/locales/doorkeeper.vi.yml @@ -60,7 +60,7 @@ vi: error: title: Một lỗi đã xảy ra new: - prompt_html: "%{client_name} yêu cầu truy cập tài khoản của bạn. Đây là ứng dụng của bên thứ ba. Nếu không tin tưởng, đừng cho phép nó." + prompt_html: "%{client_name} cần được bạn cho phép truy cập vào tài khoản. Cho phép nếu bạn tin tưởng ứng dụng này." review_permissions: Quyền truy cập title: Yêu cầu truy cập show: diff --git a/config/locales/doorkeeper.zh-CN.yml b/config/locales/doorkeeper.zh-CN.yml index ee8c0cf13a..46253d01b3 100644 --- a/config/locales/doorkeeper.zh-CN.yml +++ b/config/locales/doorkeeper.zh-CN.yml @@ -60,7 +60,7 @@ zh-CN: error: title: 发生错误 new: - prompt_html: "%{client_name} 希望得到访问你账号的许可。这是一个第三方应用。如果你不信任它,那么你不应该授权它。" + prompt_html: "%{client_name} 请求获得访问您账户的权限。 请在确保自己了解并信任此来源后再批准该请求。" review_permissions: 检查权限 title: 需要授权 show: diff --git a/config/locales/doorkeeper.zh-HK.yml b/config/locales/doorkeeper.zh-HK.yml index 79629b12fe..4450cfc1a4 100644 --- a/config/locales/doorkeeper.zh-HK.yml +++ b/config/locales/doorkeeper.zh-HK.yml @@ -60,7 +60,6 @@ zh-HK: error: title: 發生錯誤 new: - prompt_html: "%{client_name} 想得到存取你帳號的權限。這是一個第三方應用程式。如果你不信任它,請勿授權。" review_permissions: 檢視權限 title: 需要用戶授權 show: diff --git a/config/locales/doorkeeper.zh-TW.yml b/config/locales/doorkeeper.zh-TW.yml index 2759f0a3d2..5af93e1e07 100644 --- a/config/locales/doorkeeper.zh-TW.yml +++ b/config/locales/doorkeeper.zh-TW.yml @@ -60,7 +60,7 @@ zh-TW: error: title: 發生錯誤 new: - prompt_html: "%{client_name} 欲請求存取您帳號之權限。這是一個第三方應用程式。若您不信任該應用程式,請不要授權。" + prompt_html: "%{client_name} 想要請求存取您帳號之權限。請僅於您所識別且信任此來源時允許請求。" review_permissions: 檢視權限 title: 需要授權 show: diff --git a/config/locales/el.yml b/config/locales/el.yml index 0da957cdb2..c119beec17 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -24,6 +24,8 @@ el: admin: account_actions: action: Εκτέλεση ενέργειας + already_silenced: Αυτός ο λογαριασμός έχει ήδη περιοριστεί. + already_suspended: Αυτός ο λογαριασμός έχει ήδη ανασταλεί. title: Εκτέλεση ενέργειας συντονισμού στον %{acct} account_moderation_notes: create: Άφησε σημείωση @@ -45,6 +47,7 @@ el: title: Αλλαγή email για %{username} change_role: changed_msg: Ο ρόλος άλλαξε επιτυχώς! + edit_roles: Διαχείριση ρόλων χρήστη label: Αλλαγή ρόλου no_role: Κανένας ρόλος title: Αλλαγή ρόλου για %{username} diff --git a/config/locales/eo.yml b/config/locales/eo.yml index 7ebf074819..2d03ce08a7 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -24,12 +24,15 @@ eo: admin: account_actions: action: Plenumi agon + already_silenced: Ĉi tiu konto jam estis limigita. + already_suspended: Ĉi tiu konto jam estis suspendita. title: Plenumi kontrolan agon al %{acct} account_moderation_notes: create: Lasi noton created_msg: Noto de moderigado sukcese kreita! destroyed_msg: Noto de moderigado sukcese detruita! accounts: + add_email_domain_block: Bloki retpoŝtan domajnon approve: Aprobi approved_msg: Sukcese aprobis aliĝ-peton de %{username} are_you_sure: Ĉu vi certas? @@ -44,6 +47,7 @@ eo: title: Ŝanĝi retadreson por %{username} change_role: changed_msg: Rolo sukcese ŝanĝita! + edit_roles: Administri uzantrolojn label: Ŝanĝi rolon no_role: Neniu rolo title: Ŝanĝi rolon por %{username} @@ -817,16 +821,27 @@ eo: action: Klaku ĉi tie por pliaj informoj message_html: "Via objektostokado estas misagordita. La privateco de viaj uzantoj estas en risko." tags: + moderation: + title: Stato + name: Nomo + newest: Plej novaj + oldest: Plej malnovaj review: La statuso de la recenzo + search: Serĉi + title: Kradvortoj updated_msg: Kradvorto agordoj ĝisdatigis sukcese title: Administrado trends: allow: Permesi approved: Aprobita + confirm_allow: Ĉu vi certas, ke vi volas permesi elektitajn etikedojn? + confirm_disallow: Ĉu vi certas, ke vi volas malpermesi elektitajn etikedojn? disallow: Malpermesi links: allow: Permesi ligilon allow_provider: Permesi publikiganto + confirm_allow: Ĉu vi certas, ke vi volas permesi elektitajn ligilojn? + confirm_disallow: Ĉu vi certas, ke vi volas malpermesi elektitajn ligilojn? description_html: Ĉioj estas ligiloj kiuj nun diskonitajs multe de kontoj kiujn via servilo vidas. Ligiloj ne montritas publike se vi ne aprobis la publikiganton. disallow: Malpermesi ligilon disallow_provider: Malpermesi publikiganton @@ -850,6 +865,8 @@ eo: statuses: allow: Permesi afiŝon allow_account: Permesi aŭtoron + confirm_allow_account: Ĉu vi certas, ke vi volas permesi elektitajn kontojn? + confirm_disallow_account: Ĉu vi certas, ke vi volas malpermesi elektitajn kontojn? description_html: Oni multe diskonigas kaj stelumas ĉi tiujn mesaĝojn nuntempe laŭ via servilo. Tio povas helpi novajn kaj revenantajn uzantojn trovi pli da homoj por sekvi. Mesaĝo estas montrita publike nur se vi aprobis la aŭtoron kaj se la aŭtoro aprobis ke ties konto estu proponita al aliaj. Vi ankaŭ povas permesi aŭ malakcepti specifajn mesaĝojn. disallow: Malpermesi afiŝon disallow_account: Malpermesi aŭtoron @@ -928,6 +945,8 @@ eo: body: "%{reporter} signalis %{target}" body_remote: Iu de %{domain} signalis %{target} subject: Nova signalo por %{instance} (#%{id}) + new_software_updates: + body: Novaj versioj de Mastodon estis publikigitaj, vi eble volas ĝisdatigi! new_trends: body: 'La eroj bezonas kontrolon antau ol ili povas montritas publike:' new_trending_links: @@ -1033,6 +1052,9 @@ eo: view_strikes: Vidi antauaj admonoj kontra via konto too_fast: Formularo sendita tro rapide, klopodu denove. use_security_key: Uzi sekurecan ŝlosilon + author_attribution: + more_from_html: Pli de %{name} + s_blog: Blogo de %{name} challenge: confirm: Daŭrigi hint_html: "Konsileto: Ni ne demandos pri via pasvorto ĝis 1 horo." @@ -1231,6 +1253,7 @@ eo: states: finished: Finita unconfirmed: Nekonfirmita + status: Stato success: Viaj datumoj estis sukcese alŝutitaj kaj estos traktitaj kiel planite titles: following: Importado de sekvaj kontoj @@ -1689,7 +1712,13 @@ eo: silence: Konto limigita suspend: Konto suspendita welcome: + apps_step: Elŝutu niajn oficialajn aplikaĵojn. + apps_title: Aplikaĵoj de Mastodon + edit_profile_action: Agordi + edit_profile_title: Agordi vian profilon explanation: Jen kelkaj konsiloj por helpi vin komenci + feature_action: Lerni pli + follow_action: Sekvi subject: Bonvenon en Mastodon title: Bonvenon, %{name}! users: diff --git a/config/locales/simple_form.el.yml b/config/locales/simple_form.el.yml index 88c1c0e184..d46e764a44 100644 --- a/config/locales/simple_form.el.yml +++ b/config/locales/simple_form.el.yml @@ -2,6 +2,15 @@ el: simple_form: hints: + account: + attribution_domains_as_text: Προστατεύει από ψευδείς ιδιότητες. + discoverable: Οι δημόσιες δημοσιεύσεις και το προφίλ σου μπορεί να εμφανίζονται ή να συνιστώνται σε διάφορους τομείς του Mastodon και το προφίλ σου μπορεί να προτείνεται σε άλλους χρήστες. + display_name: Το πλήρες ή το αστείο σου όνομα. + fields: Η αρχική σου σελίδα, αντωνυμίες, ηλικία, ό,τι θες. + indexable: Οι δημόσιες δημοσιεύσεις σου μπορεί να εμφανιστούν στα αποτελέσματα αναζήτησης στο Mastodon. Άτομα που έχουν αλληλεπιδράσει με τις δημοσιεύσεις σου μπορεί να είναι σε θέση να τις αναζητήσουν όπως και να 'χει. + note: 'Μπορείς να @επισημάνεις άλλα άτομα ή #ετικέτες.' + show_collections: Οι χρήστες θα είναι σε θέση να περιηγηθούν στα άτομα που ακολουθείς και στους ακόλουθούς σου. Άτομα που ακολουθείς θα βλέπουν ότι τους ακολουθείς όπως και να 'χει. + unlocked: Οι χρήστες θα είναι σε θέση να σε ακολουθήσουν χωρίς να ζητούν έγκριση. Κατάργησε την επιλογή αν θες να αξιολογείς τα αιτήματα ακολούθησης και να επιλέξεις αν θα αποδεχθείς ή απορρίψεις νέους ακόλουθους. account_alias: acct: Όρισε το username@domain του λογαριασμού από τον οποίο θέλεις να μετακινηθείς account_migration: @@ -31,12 +40,14 @@ el: text: Μπορείς να κάνετε έφεση σε ένα παράπτωμα μόνο μία φορά defaults: autofollow: Όσοι εγγραφούν μέσω της πρόσκλησης θα σε ακολουθούν αυτόματα + avatar: WEBP, PNG, GIF ή JPG. Το πολύ %{size}. Θα υποβαθμιστεί σε %{dimensions}px bot: Ο λογαριασμός αυτός εκτελεί κυρίως αυτοματοποιημένες ενέργειες και ίσως να μην παρακολουθείται context: Ένα ή περισσότερα πλαίσια στα οποία μπορεί να εφαρμόζεται αυτό το φίλτρο current_password: Για λόγους ασφαλείας παρακαλώ γράψε τον κωδικό του τρέχοντος λογαριασμού current_username: Για επιβεβαίωση, παρακαλώ γράψε το όνομα χρήστη του τρέχοντος λογαριασμού digest: Αποστέλλεται μόνο μετά από μακρά περίοδο αδράνειας και μόνο αν έχεις λάβει προσωπικά μηνύματα κατά την απουσία σου email: Θα σου σταλεί email επιβεβαίωσης + header: WEBP, PNG, GIF ή JPG. Το πολύ %{size}. Θα υποβαθμιστεί σε %{dimensions}px inbox_url: Αντέγραψε το URL της αρχικής σελίδας του ανταποκριτή που θέλεις να χρησιμοποιήσεις irreversible: Οι φιλτραρισμένες αναρτήσεις θα εξαφανιστούν αμετάκλητα, ακόμα και αν το φίλτρο αργότερα αφαιρεθεί locale: Η γλώσσα χρήσης, των email και των ειδοποιήσεων push @@ -67,10 +78,15 @@ el: warn: Απόκρυψη φιλτραρισμένου περιεχομένου πίσω από μια προειδοποίηση που αναφέρει τον τίτλο του φίλτρου form_admin_settings: activity_api_enabled: Καταμέτρηση τοπικά δημοσιευμένων δημοσιεύσεων, ενεργών χρηστών και νέων εγγραφών σε εβδομαδιαία πακέτα + app_icon: WEBP, PNG, GIF ή JPG. Παρακάμπτει το προεπιλεγμένο εικονίδιο εφαρμογής σε κινητές συσκευές με προσαρμοσμένο εικονίδιο. + backups_retention_period: Οι χρήστες έχουν τη δυνατότητα να δημιουργήσουν αρχεία των αναρτήσεων τους για να κατεβάσουν αργότερα. Όταν οριστεί μια θετική τιμή, αυτά τα αρχεία θα διαγράφονται αυτόματα από τον αποθηκευτικό σου χώρο μετά τον καθορισμένο αριθμό ημερών. bootstrap_timeline_accounts: Αυτοί οι λογαριασμοί θα καρφιτσωθούν στην κορυφή των νέων χρηστών που ακολουθούν τις συστάσεις. closed_registrations_message: Εμφανίζεται όταν κλείνουν οι εγγραφές + content_cache_retention_period: Όλες οι αναρτήσεις από άλλους διακομιστές (συμπεριλαμβανομένων των ενισχύσεων και απαντήσεων) θα διαγραφούν μετά τον καθορισμένο αριθμό ημερών, χωρίς να λαμβάνεται υπόψη οποιαδήποτε αλληλεπίδραση τοπικού χρήστη με αυτές τις αναρτήσεις. Αυτό περιλαμβάνει αναρτήσεις όπου ένας τοπικός χρήστης την έχει χαρακτηρίσει ως σελιδοδείκτη ή αγαπημένη. Θα χαθούν επίσης ιδιωτικές αναφορές μεταξύ χρηστών από διαφορετικές οντότητες και θα είναι αδύνατο να αποκατασταθούν. Η χρήση αυτής της ρύθμισης προορίζεται για οντότητες ειδικού σκοπού και χαλάει πολλές προσδοκίες του χρήστη όταν εφαρμόζεται για χρήση γενική σκοπού. custom_css: Μπορείς να εφαρμόσεις προσαρμοσμένα στυλ στην έκδοση ιστοσελίδας του Mastodon. + favicon: WEBP, PNG, GIF ή JPG. Παρακάμπτει το προεπιλεγμένο favicon του Mastodon με ένα προσαρμοσμένο εικονίδιο. mascot: Παρακάμπτει την εικονογραφία στην προηγμένη διεπαφή ιστού. + media_cache_retention_period: Τα αρχεία πολυμέσων από αναρτήσεις που γίνονται από απομακρυσμένους χρήστες αποθηκεύονται προσωρινά στο διακομιστή σου. Όταν οριστεί μια θετική τιμή, τα μέσα θα διαγραφούν μετά τον καθορισμένο αριθμό ημερών. Αν τα δεδομένα πολυμέσων ζητηθούν μετά τη διαγραφή τους, θα γίνει ε, αν το πηγαίο περιεχόμενο είναι ακόμα διαθέσιμο. Λόγω περιορισμών σχετικά με το πόσο συχνά οι κάρτες προεπισκόπησης συνδέσμων συνδέονται σε ιστοσελίδες τρίτων, συνιστάται να ορίσεις αυτή την τιμή σε τουλάχιστον 14 ημέρες ή οι κάρτες προεπισκόπησης συνδέσμων δεν θα ενημερώνονται κατ' απάιτηση πριν από εκείνη την ώρα. peers_api_enabled: Μια λίστα με ονόματα τομέα που συνάντησε αυτός ο διακομιστής στο fediverse. Δεν περιλαμβάνονται δεδομένα εδώ για το αν συναλλάσσετε με ένα συγκεκριμένο διακομιστή, μόνο ότι ο διακομιστής σας το ξέρει. Χρησιμοποιείται από υπηρεσίες που συλλέγουν στατιστικά στοιχεία για την συναλλαγή με γενική έννοια. profile_directory: Ο κατάλογος προφίλ παραθέτει όλους τους χρήστες που έχουν επιλέξει να είναι ανακαλύψιμοι. require_invite_text: 'Όταν η εγγραφή απαιτεί χειροκίνητη έγκριση, κάνε το πεδίο κειμένου: «Γιατί θέλετε να συμμετάσχετε;» υποχρεωτικό αντί για προαιρετικό' @@ -103,14 +119,19 @@ el: sign_up_requires_approval: Νέες εγγραφές θα απαιτούν την έγκριση σας severity: Επιλέξτε τι θα γίνεται με αιτήσεις από αυτήν την διεύθυνση IP rule: + hint: Προαιρετικό. Δώσε περισσότερες λεπτομέρειες σχετικά με τον κανόνα text: Περιγράψτε έναν κανόνα ή μια απαίτηση για τους χρήστες σε αυτόν τον διακομιστή. Προσπαθήστε να τον κρατήσετε σύντομο και απλό sessions: otp: 'Βάλε τον κωδικό δυο παραγόντων (2FA) από την εφαρμογή του τηλεφώνου σου ή χρησιμοποίησε κάποιον από τους κωδικούς ανάκτησης σου:' webauthn: Αν πρόκειται για ένα κλειδί USB βεβαιωθείτε ότι είναι συνδεδεμένο και αν απαιτείται πατήστε το ελαφρά. + settings: + indexable: Η σελίδα του προφίλ σου μπορεί να εμφανιστεί στα αποτελέσματα αναζήτησης στο Google, Bing και άλλες. + show_application: Θα είσαι πάντα σε θέση να δεις ποια εφαρμογή δημοσίευσε την ανάρτησή σου όπως και να 'χει. tag: name: Μπορείς να αλλάξεις μόνο το πλαίσιο των χαρακτήρων, για παράδειγμα για να γίνει περισσότερο ευανάγνωστο user: chosen_languages: Όταν ενεργοποιηθεί, στη δημόσια ροή θα εμφανίζονται τουτ μόνο από τις επιλεγμένες γλώσσες + role: Ο ρόλος ελέγχει ποια δικαιώματα έχει ο χρήστης. user_role: color: Το χρώμα που θα χρησιμοποιηθεί για το ρόλο σε ολόκληρη τη διεπαφή, ως RGB σε δεκαεξαδική μορφή highlighted: Αυτό καθιστά το ρόλο δημόσια ορατό @@ -119,12 +140,18 @@ el: position: Ανώτεροι ρόλοι αποφασίζει την επίλυση συγκρούσεων σε ορισμένες περιπτώσεις. Ορισμένες ενέργειες μπορούν να εκτελεστούν μόνο σε ρόλους με χαμηλότερη προτεραιότητα webhook: events: Επιλέξτε συμβάντα για αποστολή + template: Σύνθεσε το δικό σου JSON payload χρησιμοποιώντας μεταβλητή παρεμβολή. Άφησε κενό για προεπιλογή JSON. url: Πού θα σταλούν τα γεγονότα labels: account: + attribution_domains_as_text: Να επιτρέπονται μόνο συγκεκριμένες ιστοσελίδες + discoverable: Παροχή προφίλ και αναρτήσεων σε αλγορίθμους ανακάλυψης fields: name: Περιγραφή value: Περιεχόμενο + indexable: Συμπερίληψη δημόσιων αναρτήσεων στα αποτελέσματα αναζήτησης + show_collections: Εμφάνιση ακολούθων και ακολουθουμένων στο προφίλ + unlocked: Αυτόματη αποδοχή νέων ακολούθων account_alias: acct: Διακριτικό του παλιού λογαριασμού account_migration: @@ -186,6 +213,7 @@ el: setting_default_privacy: Ιδιωτικότητα δημοσιεύσεων setting_default_sensitive: Σημείωση όλων των πολυμέσων ως ευαίσθητου περιεχομένου setting_delete_modal: Επιβεβαίωση πριν τη διαγραφή ενός τουτ + setting_disable_hover_cards: Απενεργοποίηση προεπισκόπησης προφίλ κατά την αιώρηση setting_disable_swiping: Απενεργοποίηση κινήσεων συρσίματος setting_display_media: Εμφάνιση πολυμέσων setting_display_media_default: Προκαθορισμένο @@ -217,10 +245,13 @@ el: 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 @@ -268,15 +299,27 @@ el: pending_account: Αποστολή email όταν υπάρχει νέος λογαριασμός για επιθεώρηση reblog: Αποστολή email όταν κάποιος προωθεί τη δημοσίευση σου report: Υποβλήθηκε νέα αναφορά + software_updates: + all: Ειδοποίηση για όλες τις ενημερώσεις + critical: Ειδοποίηση μόνο για κρίσιμες ενημερώσεις + label: Μια νέα έκδοση του Mastodon είναι διαθέσιμη + none: Να μην ειδοποιούμαι ποτέ για ενημερώσεις (δεν συνιστάται) + patch: Ειδοποίηση για ενημερώσεις σφαλμάτων trending_tag: Νέο περιεχόμενο προς τάση απαιτεί αξιολόγηση rule: + hint: Επιπρόσθετες πληροφορίες text: Κανόνας + settings: + indexable: Συμπερίληψη σελίδας προφίλ στις μηχανές αναζήτησης + show_application: Εμφάνιση από ποια εφαρμογή έστειλες μία ανάρτηση tag: listable: Εμφάνιση αυτής της ετικέτας στο δημόσιο κατάλογο name: Ετικέτα trendable: Εμφάνιση της ετικέτας στις τάσεις + usable: Να επιτρέπεται η τοπική χρήση αυτής της ετικέτας από αναρτήσεις user: role: Ρόλος + time_zone: Ζώνη ώρας user_role: color: Χρώμα εμβλήματος highlighted: Εμφάνιση ρόλου ως σήμα στα προφίλ χρηστών @@ -285,9 +328,11 @@ el: position: Προτεραιότητα webhook: events: Ενεργοποιημένα συμβάντα + template: Πρότυπο payload url: Endpoint URL 'no': Όχι not_recommended: Δεν προτείνεται + overridden: Αντικαταστάθηκε recommended: Προτείνεται required: mark: "*" From 7c61533111a7046126a93580e744f99f1b72337e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 11:54:25 +0200 Subject: [PATCH 70/93] Update dependency aws-sdk-s3 to v1.165.0 (#32050) 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 8f0c0ed53c..d8a13e8ecf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -101,15 +101,15 @@ GEM awrence (1.2.1) aws-eventstream (1.3.0) aws-partitions (1.977.0) - aws-sdk-core (3.207.0) + aws-sdk-core (3.208.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.92.0) + aws-sdk-kms (1.93.0) aws-sdk-core (~> 3, >= 3.207.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.164.0) + aws-sdk-s3 (1.165.0) aws-sdk-core (~> 3, >= 3.207.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) From 780e2e9d660db329a375e6120d25be5b689acb66 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 24 Sep 2024 08:07:16 -0400 Subject: [PATCH 71/93] Convert notification mailer spec shared examples to matchers (#32047) --- spec/mailers/notification_mailer_spec.rb | 40 +++++------------------- spec/support/examples/mailers.rb | 29 +++++++++++++++++ 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/spec/mailers/notification_mailer_spec.rb b/spec/mailers/notification_mailer_spec.rb index eab196166d..056000b042 100644 --- a/spec/mailers/notification_mailer_spec.rb +++ b/spec/mailers/notification_mailer_spec.rb @@ -8,38 +8,12 @@ RSpec.describe NotificationMailer do let(:foreign_status) { Fabricate(:status, account: sender, text: 'The body of the foreign status') } let(:own_status) { Fabricate(:status, account: receiver.account, text: 'The body of the own status') } - shared_examples 'standard headers' do |type| - it 'renders the email' do - expect(mail) - .to be_present - .and(have_header('To', "#{receiver.account.username} <#{receiver.email}>")) - .and(have_header('List-ID', "<#{type}.alice.cb6e6126.ngrok.io>")) - .and(have_header('List-Unsubscribe', %r{})) - .and(have_header('List-Unsubscribe', /&type=#{type}/)) - .and(have_header('List-Unsubscribe-Post', 'List-Unsubscribe=One-Click')) - .and(deliver_to("#{receiver.account.username} <#{receiver.email}>")) - .and(deliver_from('notifications@localhost')) - end - end - - shared_examples 'thread headers' do - it 'renders the email with conversation thread headers' do - conversation_header_regex = // - expect(mail) - .to be_present - .and(have_header('In-Reply-To', conversation_header_regex)) - .and(have_header('References', conversation_header_regex)) - end - end - describe 'mention' do let(:mention) { Mention.create!(account: receiver.account, status: foreign_status) } let(:notification) { Notification.create!(account: receiver.account, activity: mention) } let(:mail) { prepared_mailer_for(receiver.account).mention } include_examples 'localized subject', 'notification_mailer.mention.subject', name: 'bob' - include_examples 'standard headers', 'mention' - include_examples 'thread headers' it 'renders the email' do expect(mail) @@ -47,6 +21,8 @@ RSpec.describe NotificationMailer do .and(have_subject('You were mentioned by bob')) .and(have_body_text('You were mentioned by bob')) .and(have_body_text('The body of the foreign status')) + .and have_thread_headers + .and have_standard_headers('mention').for(receiver) end end @@ -56,13 +32,13 @@ RSpec.describe NotificationMailer do let(:mail) { prepared_mailer_for(receiver.account).follow } include_examples 'localized subject', 'notification_mailer.follow.subject', name: 'bob' - include_examples 'standard headers', 'follow' it 'renders the email' do expect(mail) .to be_present .and(have_subject('bob is now following you')) .and(have_body_text('bob is now following you')) + .and have_standard_headers('follow').for(receiver) end end @@ -72,8 +48,6 @@ RSpec.describe NotificationMailer do let(:mail) { prepared_mailer_for(own_status.account).favourite } include_examples 'localized subject', 'notification_mailer.favourite.subject', name: 'bob' - include_examples 'standard headers', 'favourite' - include_examples 'thread headers' it 'renders the email' do expect(mail) @@ -81,6 +55,8 @@ RSpec.describe NotificationMailer do .and(have_subject('bob favorited your post')) .and(have_body_text('Your post was favorited by bob')) .and(have_body_text('The body of the own status')) + .and have_thread_headers + .and have_standard_headers('favourite').for(receiver) end end @@ -90,8 +66,6 @@ RSpec.describe NotificationMailer do let(:mail) { prepared_mailer_for(own_status.account).reblog } include_examples 'localized subject', 'notification_mailer.reblog.subject', name: 'bob' - include_examples 'standard headers', 'reblog' - include_examples 'thread headers' it 'renders the email' do expect(mail) @@ -99,6 +73,8 @@ RSpec.describe NotificationMailer do .and(have_subject('bob boosted your post')) .and(have_body_text('Your post was boosted by bob')) .and(have_body_text('The body of the own status')) + .and have_thread_headers + .and have_standard_headers('reblog').for(receiver) end end @@ -108,13 +84,13 @@ RSpec.describe NotificationMailer do let(:mail) { prepared_mailer_for(receiver.account).follow_request } include_examples 'localized subject', 'notification_mailer.follow_request.subject', name: 'bob' - include_examples 'standard headers', 'follow_request' it 'renders the email' do expect(mail) .to be_present .and(have_subject('Pending follower: bob')) .and(have_body_text('bob has requested to follow you')) + .and have_standard_headers('follow_request').for(receiver) end end diff --git a/spec/support/examples/mailers.rb b/spec/support/examples/mailers.rb index a8469f1964..c1767dc419 100644 --- a/spec/support/examples/mailers.rb +++ b/spec/support/examples/mailers.rb @@ -12,3 +12,32 @@ RSpec.shared_examples 'localized subject' do |*args, **kwrest| expect(mail.subject).to eq I18n.t(*args, **kwrest.merge(locale: I18n.default_locale)) end end + +RSpec::Matchers.define :have_thread_headers do + match(notify_expectation_failures: true) do |mail| + expect(mail) + .to be_present + .and(have_header('In-Reply-To', conversation_header_regex)) + .and(have_header('References', conversation_header_regex)) + end + + def conversation_header_regex = // +end + +RSpec::Matchers.define :have_standard_headers do |type| + chain :for do |user| + @user = user + end + + match(notify_expectation_failures: true) do |mail| + expect(mail) + .to be_present + .and(have_header('To', "#{@user.account.username} <#{@user.email}>")) + .and(have_header('List-ID', "<#{type}.#{@user.account.username}.#{Rails.configuration.x.local_domain}>")) + .and(have_header('List-Unsubscribe', %r{})) + .and(have_header('List-Unsubscribe', /&type=#{type}/)) + .and(have_header('List-Unsubscribe-Post', 'List-Unsubscribe=One-Click')) + .and(deliver_to("#{@user.account.username} <#{@user.email}>")) + .and(deliver_from(Rails.configuration.action_mailer.default_options[:from])) + end +end From 19dedd7cfd9a6d61e136519ede4447d7ec0e6e60 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 24 Sep 2024 09:16:31 -0400 Subject: [PATCH 72/93] Set important mailer headers with `after_action` callback (#32057) --- app/mailers/admin_mailer.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/mailers/admin_mailer.rb b/app/mailers/admin_mailer.rb index 72a2c2e64e..fffbbb3c6d 100644 --- a/app/mailers/admin_mailer.rb +++ b/app/mailers/admin_mailer.rb @@ -9,6 +9,8 @@ class AdminMailer < ApplicationMailer before_action :process_params before_action :set_instance + after_action :set_important_headers!, only: :new_critical_software_updates + default to: -> { @me.user_email } def new_report(report) @@ -56,12 +58,6 @@ class AdminMailer < ApplicationMailer def new_critical_software_updates @software_updates = SoftwareUpdate.where(urgent: true).to_a.sort_by(&:gem_version) - headers( - 'Importance' => 'high', - 'Priority' => 'urgent', - 'X-Priority' => '1' - ) - locale_for_account(@me) do mail subject: default_i18n_subject(instance: @instance) end @@ -82,4 +78,12 @@ class AdminMailer < ApplicationMailer def set_instance @instance = Rails.configuration.x.local_domain end + + def set_important_headers! + headers( + 'Importance' => 'high', + 'Priority' => 'urgent', + 'X-Priority' => '1' + ) + end end From cfb8fc6222f451b16ccabfc4bb1448a84afb26ee Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Tue, 24 Sep 2024 15:16:58 +0200 Subject: [PATCH 73/93] Increase regexp timeout and allow override (#32056) --- config/initializers/regexp.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/initializers/regexp.rb b/config/initializers/regexp.rb index a820d2b5d3..4e79dc478e 100644 --- a/config/initializers/regexp.rb +++ b/config/initializers/regexp.rb @@ -1,4 +1,4 @@ # frozen_string_literal: true -# 0.5s is a fairly high timeout, but that should account for slow servers under load -Regexp.timeout = 0.5 if Regexp.respond_to?(:timeout=) +# 2s is a fairly high default, but that should account for slow servers under load +Regexp.timeout = ENV.fetch('REGEXP_TIMEOUT', 2).to_f if Regexp.respond_to?(:timeout=) From 0a6b75b71eae0dd9eb9d5d8c73e1d210820eeb51 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 24 Sep 2024 17:03:38 +0200 Subject: [PATCH 74/93] Fix multiple bugs in notification requests and notification policies (#32062) --- .../mastodon/actions/notification_policies.ts | 4 +- .../mastodon/actions/notification_requests.ts | 40 +++++-------------- app/javascript/mastodon/api/notifications.ts | 2 +- .../filtered_notifications_banner.tsx | 4 +- .../mastodon/reducers/notification_policy.ts | 7 ++-- 5 files changed, 18 insertions(+), 39 deletions(-) diff --git a/app/javascript/mastodon/actions/notification_policies.ts b/app/javascript/mastodon/actions/notification_policies.ts index b182bcf699..fd798eaad7 100644 --- a/app/javascript/mastodon/actions/notification_policies.ts +++ b/app/javascript/mastodon/actions/notification_policies.ts @@ -17,6 +17,6 @@ export const updateNotificationsPolicy = createDataLoadingThunk( (policy: Partial) => apiUpdateNotificationsPolicy(policy), ); -export const decreasePendingNotificationsCount = createAction( - 'notificationPolicy/decreasePendingNotificationCount', +export const decreasePendingRequestsCount = createAction( + 'notificationPolicy/decreasePendingRequestsCount', ); diff --git a/app/javascript/mastodon/actions/notification_requests.ts b/app/javascript/mastodon/actions/notification_requests.ts index ef9cbef03e..8352ff2aad 100644 --- a/app/javascript/mastodon/actions/notification_requests.ts +++ b/app/javascript/mastodon/actions/notification_requests.ts @@ -13,11 +13,11 @@ import type { ApiNotificationJSON, } from 'mastodon/api_types/notifications'; import type { ApiStatusJSON } from 'mastodon/api_types/statuses'; -import type { AppDispatch, RootState } from 'mastodon/store'; +import type { AppDispatch } from 'mastodon/store'; import { createDataLoadingThunk } from 'mastodon/store/typed_functions'; import { importFetchedAccounts, importFetchedStatuses } from './importer'; -import { decreasePendingNotificationsCount } from './notification_policies'; +import { decreasePendingRequestsCount } from './notification_policies'; // TODO: refactor with notification_groups function dispatchAssociatedRecords( @@ -169,19 +169,11 @@ export const expandNotificationsForRequest = createDataLoadingThunk( }, ); -const selectNotificationCountForRequest = (state: RootState, id: string) => { - const requests = state.notificationRequests.items; - const thisRequest = requests.find((request) => request.id === id); - return thisRequest ? thisRequest.notifications_count : 0; -}; - export const acceptNotificationRequest = createDataLoadingThunk( 'notificationRequest/accept', ({ id }: { id: string }) => apiAcceptNotificationRequest(id), - (_data, { dispatch, getState, discardLoadData, actionArg: { id } }) => { - const count = selectNotificationCountForRequest(getState(), id); - - dispatch(decreasePendingNotificationsCount(count)); + (_data, { dispatch, discardLoadData }) => { + dispatch(decreasePendingRequestsCount(1)); // The payload is not used in any functions return discardLoadData; @@ -191,10 +183,8 @@ export const acceptNotificationRequest = createDataLoadingThunk( export const dismissNotificationRequest = createDataLoadingThunk( 'notificationRequest/dismiss', ({ id }: { id: string }) => apiDismissNotificationRequest(id), - (_data, { dispatch, getState, discardLoadData, actionArg: { id } }) => { - const count = selectNotificationCountForRequest(getState(), id); - - dispatch(decreasePendingNotificationsCount(count)); + (_data, { dispatch, discardLoadData }) => { + dispatch(decreasePendingRequestsCount(1)); // The payload is not used in any functions return discardLoadData; @@ -204,13 +194,8 @@ export const dismissNotificationRequest = createDataLoadingThunk( export const acceptNotificationRequests = createDataLoadingThunk( 'notificationRequests/acceptBulk', ({ ids }: { ids: string[] }) => apiAcceptNotificationRequests(ids), - (_data, { dispatch, getState, discardLoadData, actionArg: { ids } }) => { - const count = ids.reduce( - (count, id) => count + selectNotificationCountForRequest(getState(), id), - 0, - ); - - dispatch(decreasePendingNotificationsCount(count)); + (_data, { dispatch, discardLoadData, actionArg: { ids } }) => { + dispatch(decreasePendingRequestsCount(ids.length)); // The payload is not used in any functions return discardLoadData; @@ -220,13 +205,8 @@ export const acceptNotificationRequests = createDataLoadingThunk( export const dismissNotificationRequests = createDataLoadingThunk( 'notificationRequests/dismissBulk', ({ ids }: { ids: string[] }) => apiDismissNotificationRequests(ids), - (_data, { dispatch, getState, discardLoadData, actionArg: { ids } }) => { - const count = ids.reduce( - (count, id) => count + selectNotificationCountForRequest(getState(), id), - 0, - ); - - dispatch(decreasePendingNotificationsCount(count)); + (_data, { dispatch, discardLoadData, actionArg: { ids } }) => { + dispatch(decreasePendingRequestsCount(ids.length)); // The payload is not used in any functions return discardLoadData; diff --git a/app/javascript/mastodon/api/notifications.ts b/app/javascript/mastodon/api/notifications.ts index 24d526655c..92863ac5ca 100644 --- a/app/javascript/mastodon/api/notifications.ts +++ b/app/javascript/mastodon/api/notifications.ts @@ -91,5 +91,5 @@ export const apiAcceptNotificationRequests = async (id: string[]) => { }; export const apiDismissNotificationRequests = async (id: string[]) => { - return apiRequestPost('v1/notifications/dismiss/dismiss', { id }); + return apiRequestPost('v1/notifications/requests/dismiss', { id }); }; diff --git a/app/javascript/mastodon/features/notifications/components/filtered_notifications_banner.tsx b/app/javascript/mastodon/features/notifications/components/filtered_notifications_banner.tsx index d9e60d48cd..d2241af389 100644 --- a/app/javascript/mastodon/features/notifications/components/filtered_notifications_banner.tsx +++ b/app/javascript/mastodon/features/notifications/components/filtered_notifications_banner.tsx @@ -31,7 +31,7 @@ export const FilteredNotificationsIconButton: React.FC<{ history.push('/notifications/requests'); }, [history]); - if (policy === null || policy.summary.pending_notifications_count === 0) { + if (policy === null || policy.summary.pending_requests_count <= 0) { return null; } @@ -70,7 +70,7 @@ export const FilteredNotificationsBanner: React.FC = () => { }; }, [dispatch]); - if (policy === null || policy.summary.pending_notifications_count === 0) { + if (policy === null || policy.summary.pending_requests_count <= 0) { return null; } diff --git a/app/javascript/mastodon/reducers/notification_policy.ts b/app/javascript/mastodon/reducers/notification_policy.ts index ed912dde5d..a883f9c1ee 100644 --- a/app/javascript/mastodon/reducers/notification_policy.ts +++ b/app/javascript/mastodon/reducers/notification_policy.ts @@ -2,7 +2,7 @@ import { createReducer, isAnyOf } from '@reduxjs/toolkit'; import { fetchNotificationPolicy, - decreasePendingNotificationsCount, + decreasePendingRequestsCount, updateNotificationsPolicy, } from 'mastodon/actions/notification_policies'; import type { NotificationPolicy } from 'mastodon/models/notification_policy'; @@ -10,10 +10,9 @@ import type { NotificationPolicy } from 'mastodon/models/notification_policy'; export const notificationPolicyReducer = createReducer(null, (builder) => { builder - .addCase(decreasePendingNotificationsCount, (state, action) => { + .addCase(decreasePendingRequestsCount, (state, action) => { if (state) { - state.summary.pending_notifications_count -= action.payload; - state.summary.pending_requests_count -= 1; + state.summary.pending_requests_count -= action.payload; } }) .addMatcher( From c36a76b9eb2e5e6809956e012028d726e113dd50 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 24 Sep 2024 17:19:55 +0200 Subject: [PATCH 75/93] Fix error when accepting appeal for sensitive posts deleted in the meantime (#32037) Co-authored-by: David Roetzel --- app/services/approve_appeal_service.rb | 2 +- spec/services/approve_appeal_service_spec.rb | 60 ++++++++++++++------ 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/app/services/approve_appeal_service.rb b/app/services/approve_appeal_service.rb index b8a522b2a1..3f8d1e2a3b 100644 --- a/app/services/approve_appeal_service.rb +++ b/app/services/approve_appeal_service.rb @@ -53,7 +53,7 @@ class ApproveAppealService < BaseService def undo_mark_statuses_as_sensitive! representative_account = Account.representative - @strike.statuses.includes(:media_attachments).find_each do |status| + @strike.statuses.kept.includes(:media_attachments).reorder(nil).find_each do |status| UpdateStatusService.new.call(status, representative_account.id, sensitive: false) if status.with_media? end end diff --git a/spec/services/approve_appeal_service_spec.rb b/spec/services/approve_appeal_service_spec.rb index 5707c5d7f4..52e073df0c 100644 --- a/spec/services/approve_appeal_service_spec.rb +++ b/spec/services/approve_appeal_service_spec.rb @@ -4,27 +4,55 @@ require 'rails_helper' RSpec.describe ApproveAppealService do describe '#call' do - context 'with an existing appeal' do - let(:appeal) { Fabricate(:appeal) } - let(:account) { Fabricate(:account) } + let(:appeal) { Fabricate(:appeal) } + let(:account) { Fabricate(:account) } - it 'processes the appeal approval' do - expect { subject.call(appeal, account) } - .to mark_overruled - .and record_approver + it 'processes the appeal approval' do + expect { subject.call(appeal, account) } + .to mark_overruled + .and record_approver + end + + context 'with an appeal about then-deleted posts marked as sensitive by moderators' do + let(:target_account) { Fabricate(:account) } + let(:appeal) { Fabricate(:appeal, strike: strike, account: target_account) } + let(:deleted_media) { Fabricate(:media_attachment, type: :video, status: Fabricate(:status, account: target_account), account: target_account) } + let(:kept_media) { Fabricate(:media_attachment, type: :video, status: Fabricate(:status, account: target_account), account: target_account) } + let(:strike) { Fabricate(:account_warning, target_account: target_account, action: :mark_statuses_as_sensitive, status_ids: [deleted_media.status.id, kept_media.status.id]) } + + before do + target_account.unsuspend! + deleted_media.status.discard! end - def mark_overruled - change(appeal.strike, :overruled_at) - .from(nil) - .to(be > 1.minute.ago) - end + it 'approves the appeal, marks the statuses as not sensitive and notifies target account about the approval', :inline_jobs do + emails = capture_emails { subject.call(appeal, account) } - def record_approver - change(appeal, :approved_by_account) - .from(nil) - .to(account) + expect(appeal.reload).to be_approved + expect(strike.reload).to be_overruled + + expect(kept_media.status.reload).to_not be_sensitive + + expect(emails.size) + .to eq(1) + expect(emails.first) + .to have_attributes( + to: contain_exactly(target_account.user.email), + subject: eq(I18n.t('user_mailer.appeal_approved.subject', date: I18n.l(appeal.created_at))) + ) end end + + def mark_overruled + change(appeal.strike, :overruled_at) + .from(nil) + .to(be > 1.minute.ago) + end + + def record_approver + change(appeal, :approved_by_account) + .from(nil) + .to(account) + end end end From 556837f1561bb8b806030cb39d7ed658a1a1eaca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Menrath?= <99024746+Menrath@users.noreply.github.com> Date: Tue, 24 Sep 2024 17:57:53 +0200 Subject: [PATCH 76/93] Fix the summary of converted object types to be treated as HTML (#28629) --- app/lib/activitypub/activity/create.rb | 10 +++- .../fetch_remote_status_service_spec.rb | 57 ++++++++++++++++++- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index aae73e01e0..09a8caf1fc 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -342,7 +342,15 @@ class ActivityPub::Activity::Create < ActivityPub::Activity end def converted_text - linkify([@status_parser.title.presence, @status_parser.spoiler_text.presence, @status_parser.url || @status_parser.uri].compact.join("\n\n")) + [formatted_title, @status_parser.spoiler_text.presence, formatted_url].compact.join("\n\n") + end + + def formatted_title + "

#{@status_parser.title}

" if @status_parser.title.present? + end + + def formatted_url + linkify(@status_parser.url || @status_parser.uri) end def unsupported_media_type?(mime_type) diff --git a/spec/services/activitypub/fetch_remote_status_service_spec.rb b/spec/services/activitypub/fetch_remote_status_service_spec.rb index 635fcb7976..9d8c6e0e0a 100644 --- a/spec/services/activitypub/fetch_remote_status_service_spec.rb +++ b/spec/services/activitypub/fetch_remote_status_service_spec.rb @@ -72,7 +72,7 @@ RSpec.describe ActivityPub::FetchRemoteStatusService do expect(status).to_not be_nil expect(status.url).to eq 'https://foo.bar/watch?v=12345' - expect(strip_tags(status.text)).to eq 'Nyan Cat 10 hours remixhttps://foo.bar/watch?v=12345' + expect(strip_tags(status.text)).to eq "Nyan Cat 10 hours remix\n\nhttps://foo.bar/watch?v=12345" end end @@ -105,7 +105,7 @@ RSpec.describe ActivityPub::FetchRemoteStatusService do expect(status).to_not be_nil expect(status.url).to eq 'https://foo.bar/watch?v=12345' - expect(strip_tags(status.text)).to eq 'Nyan Cat 10 hours remixhttps://foo.bar/watch?v=12345' + expect(strip_tags(status.text)).to eq "Nyan Cat 10 hours remix\n\nhttps://foo.bar/watch?v=12345" end end @@ -125,7 +125,58 @@ RSpec.describe ActivityPub::FetchRemoteStatusService do expect(status).to_not be_nil expect(status.url).to eq 'https://foo.bar/@foo/1234' - expect(strip_tags(status.text)).to eq "Let's change the worldhttps://foo.bar/@foo/1234" + expect(strip_tags(status.text)).to eq "Let's change the world\n\nhttps://foo.bar/@foo/1234" + end + end + + context 'with Event object that contains a HTML summary' do + let(:object) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'https://foo.bar/@foo/1234', + type: 'Event', + name: 'Fediverse Birthday Party', + startTime: '2024-01-31T20:00:00.000+01:00', + location: { + type: 'Place', + name: 'FooBar – The not converted location', + }, + content: 'The not converted detailed description of the event object.', + summary: '

See you at the FooBar!

  • Doors: 8pm
  • Music: 10pm
', + attributedTo: ActivityPub::TagManager.instance.uri_for(sender), + } + end + + it 'creates status' do + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.url).to eq 'https://foo.bar/@foo/1234' + expect(status.text).to start_with "

#{object[:name]}

\n\n#{object[:summary]}\n\n" + expect(status.text).to include "href=\"#{object[:id]}\"" + end + end + + context 'with Article object that contains a HTML summary' do + let(:object) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'https://foo.bar/blog/future-of-the-fediverse', + type: 'Article', + name: 'Future of the Fediverse', + content: 'Lorem Ipsum', + summary: '

Guest article by John Mastodon

The fediverse is great reading this you will find out why!

', + attributedTo: ActivityPub::TagManager.instance.uri_for(sender), + } + end + + it 'creates status' do + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.url).to eq object[:id] + expect(status.text).to start_with "

#{object[:name]}

\n\n#{object[:summary]}\n\n" + expect(status.text).to include "href=\"#{object[:id]}\"" end end From f1b6a611aab351d9f3658fc8c255694597602d81 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Tue, 24 Sep 2024 18:47:45 +0200 Subject: [PATCH 77/93] Fix wrapping in dashboard quick access buttons (#32043) --- app/javascript/styles/mastodon/dashboard.scss | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/javascript/styles/mastodon/dashboard.scss b/app/javascript/styles/mastodon/dashboard.scss index 1621220ccb..d049b2456c 100644 --- a/app/javascript/styles/mastodon/dashboard.scss +++ b/app/javascript/styles/mastodon/dashboard.scss @@ -86,9 +86,7 @@ color: $primary-text-color; transition: all 100ms ease-in; font-size: 14px; - padding: 0 16px; - line-height: 36px; - height: 36px; + padding: 8px 16px; text-decoration: none; margin-bottom: 4px; From 70988519df66f0b8edeb6ca95140f1d3e436fea8 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 24 Sep 2024 19:02:36 +0200 Subject: [PATCH 78/93] Fix too many requests caused by relationship look-ups in web UI (#32042) Co-authored-by: Claire --- app/javascript/mastodon/actions/accounts.js | 23 +++++++++++++------ .../mastodon/actions/notifications.js | 13 +---------- app/javascript/mastodon/utils/debounce.ts | 23 +++++++++++++++++++ 3 files changed, 40 insertions(+), 19 deletions(-) create mode 100644 app/javascript/mastodon/utils/debounce.ts diff --git a/app/javascript/mastodon/actions/accounts.js b/app/javascript/mastodon/actions/accounts.js index 9144235195..3d0e8b8c90 100644 --- a/app/javascript/mastodon/actions/accounts.js +++ b/app/javascript/mastodon/actions/accounts.js @@ -1,4 +1,5 @@ import { browserHistory } from 'mastodon/components/router'; +import { debounceWithDispatchAndArguments } from 'mastodon/utils/debounce'; import api, { getLinks } from '../api'; @@ -449,6 +450,20 @@ export function expandFollowingFail(id, error) { }; } +const debouncedFetchRelationships = debounceWithDispatchAndArguments((dispatch, ...newAccountIds) => { + if (newAccountIds.length === 0) { + return; + } + + dispatch(fetchRelationshipsRequest(newAccountIds)); + + api().get(`/api/v1/accounts/relationships?with_suspended=true&${newAccountIds.map(id => `id[]=${id}`).join('&')}`).then(response => { + dispatch(fetchRelationshipsSuccess({ relationships: response.data })); + }).catch(error => { + dispatch(fetchRelationshipsFail(error)); + }); +}, { delay: 500 }); + export function fetchRelationships(accountIds) { return (dispatch, getState) => { const state = getState(); @@ -460,13 +475,7 @@ export function fetchRelationships(accountIds) { return; } - dispatch(fetchRelationshipsRequest(newAccountIds)); - - api().get(`/api/v1/accounts/relationships?with_suspended=true&${newAccountIds.map(id => `id[]=${id}`).join('&')}`).then(response => { - dispatch(fetchRelationshipsSuccess({ relationships: response.data })); - }).catch(error => { - dispatch(fetchRelationshipsFail(error)); - }); + debouncedFetchRelationships(dispatch, ...newAccountIds); }; } diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js index 14072562d4..4c6e27cd5f 100644 --- a/app/javascript/mastodon/actions/notifications.js +++ b/app/javascript/mastodon/actions/notifications.js @@ -10,7 +10,7 @@ import api, { getLinks } from '../api'; import { unescapeHTML } from '../utils/html'; import { requestNotificationPermission } from '../utils/notifications'; -import { fetchFollowRequests, fetchRelationships } from './accounts'; +import { fetchFollowRequests } from './accounts'; import { importFetchedAccount, importFetchedAccounts, @@ -56,14 +56,6 @@ defineMessages({ group: { id: 'notifications.group', defaultMessage: '{count} notifications' }, }); -const fetchRelatedRelationships = (dispatch, notifications) => { - const accountIds = notifications.filter(item => ['follow', 'follow_request', 'admin.sign_up'].indexOf(item.type) !== -1).map(item => item.account.id); - - if (accountIds.length > 0) { - dispatch(fetchRelationships(accountIds)); - } -}; - export const loadPending = () => ({ type: NOTIFICATIONS_LOAD_PENDING, }); @@ -106,8 +98,6 @@ export function updateNotifications(notification, intlMessages, intlLocale) { dispatch(notificationsUpdate({ notification, preferPendingItems, playSound: playSound && !filtered})); - - fetchRelatedRelationships(dispatch, [notification]); } else if (playSound && !filtered) { dispatch({ type: NOTIFICATIONS_UPDATE_NOOP, @@ -199,7 +189,6 @@ export function expandNotifications({ maxId = undefined, forceLoad = false }) { dispatch(importFetchedAccounts(response.data.filter(item => item.report).map(item => item.report.target_account))); dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore, isLoadingRecent, isLoadingRecent && preferPendingItems)); - fetchRelatedRelationships(dispatch, response.data); dispatch(submitMarkers()); } catch(error) { dispatch(expandNotificationsFail(error, isLoadingMore)); diff --git a/app/javascript/mastodon/utils/debounce.ts b/app/javascript/mastodon/utils/debounce.ts new file mode 100644 index 0000000000..224a538984 --- /dev/null +++ b/app/javascript/mastodon/utils/debounce.ts @@ -0,0 +1,23 @@ +import { debounce } from 'lodash'; + +import type { AppDispatch } from 'mastodon/store'; + +export const debounceWithDispatchAndArguments = ( + fn: (dispatch: AppDispatch, ...args: T[]) => void, + { delay = 100 }, +) => { + let argumentBuffer: T[] = []; + let dispatchBuffer: AppDispatch; + + const wrapped = debounce(() => { + const tmpBuffer = argumentBuffer; + argumentBuffer = []; + fn(dispatchBuffer, ...tmpBuffer); + }, delay); + + return (dispatch: AppDispatch, ...args: T[]) => { + dispatchBuffer = dispatch; + argumentBuffer.push(...args); + wrapped(); + }; +}; From a773c239c3b4da4801e0438ec88ccaf0ca90e8fd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 09:33:37 +0200 Subject: [PATCH 79/93] Update dependency aws-sdk-s3 to v1.166.0 (#32079) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index d8a13e8ecf..ed217ca146 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -100,16 +100,16 @@ GEM attr_required (1.0.2) awrence (1.2.1) aws-eventstream (1.3.0) - aws-partitions (1.977.0) - aws-sdk-core (3.208.0) + aws-partitions (1.978.0) + aws-sdk-core (3.209.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.93.0) + aws-sdk-kms (1.94.0) aws-sdk-core (~> 3, >= 3.207.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.165.0) + aws-sdk-s3 (1.166.0) aws-sdk-core (~> 3, >= 3.207.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) From 4e6fc3a62f053084916b4bdfbcb20d4b5de2eb08 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 07:40:14 +0000 Subject: [PATCH 80/93] New Crowdin Translations (automated) (#32083) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/fr-CA.json | 2 +- app/javascript/mastodon/locales/fr.json | 2 +- app/javascript/mastodon/locales/vi.json | 28 +++--- config/locales/activerecord.eo.yml | 6 ++ config/locales/devise.eo.yml | 3 + config/locales/doorkeeper.es-AR.yml | 2 +- config/locales/doorkeeper.es-MX.yml | 2 +- config/locales/doorkeeper.es.yml | 2 +- config/locales/doorkeeper.hu.yml | 1 + config/locales/doorkeeper.vi.yml | 4 +- config/locales/simple_form.vi.yml | 12 +-- config/locales/vi.yml | 108 ++++++++++----------- 12 files changed, 91 insertions(+), 81 deletions(-) diff --git a/app/javascript/mastodon/locales/fr-CA.json b/app/javascript/mastodon/locales/fr-CA.json index 27e776024f..ead016dec2 100644 --- a/app/javascript/mastodon/locales/fr-CA.json +++ b/app/javascript/mastodon/locales/fr-CA.json @@ -164,7 +164,7 @@ "compose_form.publish": "Publier", "compose_form.publish_form": "Publier", "compose_form.reply": "Répondre", - "compose_form.save_changes": "Mis à jour", + "compose_form.save_changes": "Mettre à jour", "compose_form.spoiler.marked": "Enlever l'avertissement de contenu", "compose_form.spoiler.unmarked": "Ajouter un avertissement de contenu", "compose_form.spoiler_placeholder": "Avertissement de contenu (optionnel)", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index f787216c46..9375eccc6f 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -164,7 +164,7 @@ "compose_form.publish": "Publier", "compose_form.publish_form": "Nouvelle publication", "compose_form.reply": "Répondre", - "compose_form.save_changes": "Mis à jour", + "compose_form.save_changes": "Mettre à jour", "compose_form.spoiler.marked": "Enlever l’avertissement de contenu", "compose_form.spoiler.unmarked": "Ajouter un avertissement de contenu", "compose_form.spoiler_placeholder": "Avertissement de contenu (optionnel)", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index 74c46c7b33..04b73e78fa 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -76,7 +76,7 @@ "admin.dashboard.monthly_retention": "Tỉ lệ người dùng ở lại sau khi đăng ký", "admin.dashboard.retention.average": "Trung bình", "admin.dashboard.retention.cohort": "Tháng đăng ký", - "admin.dashboard.retention.cohort_size": "Người mới", + "admin.dashboard.retention.cohort_size": "Số người", "admin.impact_report.instance_accounts": "Hồ sơ tài khoản này sẽ xóa", "admin.impact_report.instance_followers": "Người theo dõi của thành viên máy chủ sẽ mất", "admin.impact_report.instance_follows": "Người theo dõi người dùng của họ sẽ mất", @@ -154,7 +154,7 @@ "compose_form.lock_disclaimer": "Tài khoản của bạn không {locked}. Bất cứ ai cũng có thể theo dõi và xem tút riêng tư của bạn.", "compose_form.lock_disclaimer.lock": "khóa", "compose_form.placeholder": "Bạn đang nghĩ gì?", - "compose_form.poll.duration": "Hết hạn", + "compose_form.poll.duration": "Hết hạn sau", "compose_form.poll.multiple": "Chọn nhiều", "compose_form.poll.option_placeholder": "Lựa chọn {number}", "compose_form.poll.single": "Chọn một", @@ -180,7 +180,7 @@ "confirmations.discard_edit_media.message": "Bạn chưa lưu thay đổi đối với phần mô tả hoặc bản xem trước của media, vẫn bỏ luôn?", "confirmations.edit.confirm": "Sửa", "confirmations.edit.message": "Nội dung tút cũ sẽ bị ghi đè, bạn có tiếp tục?", - "confirmations.edit.title": "Viết đè lên tút cũ", + "confirmations.edit.title": "Ghi đè lên tút cũ", "confirmations.logout.confirm": "Đăng xuất", "confirmations.logout.message": "Bạn có chắc muốn thoát?", "confirmations.logout.title": "Đăng xuất", @@ -190,11 +190,11 @@ "confirmations.redraft.title": "Xóa & viết lại", "confirmations.reply.confirm": "Trả lời", "confirmations.reply.message": "Nội dung bạn đang soạn thảo sẽ bị ghi đè, bạn có tiếp tục?", - "confirmations.reply.title": "Viết đè lên tút cũ", + "confirmations.reply.title": "Ghi đè lên tút cũ", "confirmations.unfollow.confirm": "Bỏ theo dõi", "confirmations.unfollow.message": "Bạn có chắc muốn bỏ theo dõi {name}?", "confirmations.unfollow.title": "Bỏ theo dõi", - "content_warning.hide": "Ẩn tút", + "content_warning.hide": "Ẩn lại", "content_warning.show": "Nhấn để xem", "conversation.delete": "Xóa tin nhắn này", "conversation.mark_as_read": "Đánh dấu là đã đọc", @@ -322,7 +322,7 @@ "follow_suggestions.hints.most_interactions": "Người này đang thu hút sự chú ý trên {domain}.", "follow_suggestions.hints.similar_to_recently_followed": "Người này có nét giống những người mà bạn theo dõi gần đây.", "follow_suggestions.personalized_suggestion": "Gợi ý cá nhân hóa", - "follow_suggestions.popular_suggestion": "Những người nổi tiếng", + "follow_suggestions.popular_suggestion": "Người nổi tiếng", "follow_suggestions.popular_suggestion_longer": "Nổi tiếng trên {domain}", "follow_suggestions.similar_to_recently_followed_longer": "Tương tự những người mà bạn theo dõi gần đây", "follow_suggestions.view_all": "Xem tất cả", @@ -480,7 +480,7 @@ "navigation_bar.domain_blocks": "Máy chủ đã ẩn", "navigation_bar.explore": "Xu hướng", "navigation_bar.favourites": "Tút thích", - "navigation_bar.filters": "Bộ lọc từ ngữ", + "navigation_bar.filters": "Từ khóa đã lọc", "navigation_bar.follow_requests": "Yêu cầu theo dõi", "navigation_bar.followed_tags": "Hashtag theo dõi", "navigation_bar.follows_and_followers": "Quan hệ", @@ -555,7 +555,7 @@ "notification_requests.view": "Hiện thông báo", "notifications.clear": "Xóa hết thông báo", "notifications.clear_confirmation": "Bạn có chắc muốn xóa vĩnh viễn tất cả thông báo của mình?", - "notifications.clear_title": "Xóa hết thông báo?", + "notifications.clear_title": "Xóa toàn bộ thông báo", "notifications.column_settings.admin.report": "Báo cáo mới:", "notifications.column_settings.admin.sign_up": "Người mới tham gia:", "notifications.column_settings.alert": "Báo trên máy tính", @@ -601,8 +601,8 @@ "notifications.policy.filter_not_followers_title": "Những người không theo dõi bạn", "notifications.policy.filter_not_following_hint": "Cho tới khi bạn duyệt họ", "notifications.policy.filter_not_following_title": "Những người bạn không theo dõi", - "notifications.policy.filter_private_mentions_hint": "Được lọc trừ khi nó trả lời lượt nhắc từ bạn hoặc nếu bạn theo dõi người gửi", - "notifications.policy.filter_private_mentions_title": "Lượt nhắc riêng tư không được yêu cầu", + "notifications.policy.filter_private_mentions_hint": "Trừ khi nó trả lời lượt nhắc từ bạn hoặc nếu bạn có theo dõi người gửi", + "notifications.policy.filter_private_mentions_title": "Lượt nhắn riêng không mong muốn", "notifications.policy.title": "Quản lý thông báo từ…", "notifications_permission_banner.enable": "Cho phép thông báo trên màn hình", "notifications_permission_banner.how_to_control": "Hãy bật thông báo trên màn hình để không bỏ lỡ những thông báo từ Mastodon. Một khi đã bật, bạn có thể lựa chọn từng loại thông báo khác nhau thông qua {icon} nút bên dưới.", @@ -713,7 +713,7 @@ "report.reasons.other": "Một lý do khác", "report.reasons.other_description": "Vấn đề không nằm trong những mục trên", "report.reasons.spam": "Đây là spam", - "report.reasons.spam_description": "Liên kết độc hại, tạo tương tác giả hoặc trả lời lặp đi lặp lại", + "report.reasons.spam_description": "Liên kết độc hại, giả tương tác hoặc trả lời lặp đi lặp lại", "report.reasons.violation": "Vi phạm nội quy máy chủ", "report.reasons.violation_description": "Bạn nhận thấy nó vi phạm nội quy máy chủ", "report.rules.subtitle": "Chọn tất cả những gì phù hợp", @@ -787,9 +787,9 @@ "status.edit": "Sửa", "status.edited": "Sửa lần cuối {date}", "status.edited_x_times": "Đã sửa {count, plural, other {{count} lần}}", - "status.embed": "Lấy mã nhúng", + "status.embed": "Nhúng", "status.favourite": "Thích", - "status.favourites": "{count, plural, other {Thích}}", + "status.favourites": "{count, plural, other {thích}}", "status.filter": "Lọc tút này", "status.history.created": "{name} đăng {date}", "status.history.edited": "{name} đã sửa {date}", @@ -808,7 +808,7 @@ "status.reblog": "Đăng lại", "status.reblog_private": "Đăng lại (Riêng tư)", "status.reblogged_by": "{name} đăng lại", - "status.reblogs": "{count, plural, other {Đăng lại}}", + "status.reblogs": "{count, plural, other {đăng lại}}", "status.reblogs.empty": "Tút này chưa có ai đăng lại. Nếu có, nó sẽ hiển thị ở đây.", "status.redraft": "Xóa và viết lại", "status.remove_bookmark": "Bỏ lưu", diff --git a/config/locales/activerecord.eo.yml b/config/locales/activerecord.eo.yml index 99059e3856..f99f726e23 100644 --- a/config/locales/activerecord.eo.yml +++ b/config/locales/activerecord.eo.yml @@ -15,6 +15,12 @@ eo: user/invite_request: text: Kialo errors: + attributes: + domain: + invalid: ne estas valida domajna nomo + messages: + invalid_domain_on_line: "%{value} ne estas valida domajna nomo" + too_many_lines: superas la limon de %{limit} linioj models: account: attributes: diff --git a/config/locales/devise.eo.yml b/config/locales/devise.eo.yml index 193fecc757..f0322a60a8 100644 --- a/config/locales/devise.eo.yml +++ b/config/locales/devise.eo.yml @@ -48,10 +48,13 @@ eo: subject: 'Mastodon: Instrukcioj por ŝanĝi pasvorton' title: Pasvorto restarigita two_factor_disabled: + explanation: Ensalutu nun eblas uzante nur retadreson kaj pasvorton. subject: 'Mastodon: dufaktora aŭtentigo malebligita' + subtitle: Dupaŝa aŭtentigo por via konto estas malŝaltita. title: 2FA estas malŝaltita two_factor_enabled: subject: 'Mastodon: Dufaktora aŭtentigo ebligita' + subtitle: Dupaŝa aŭtentigo por via konto estas ŝaltita. title: 2FA aktivigita two_factor_recovery_codes_changed: explanation: La antaŭaj reakiraj kodoj estis nuligitaj kaj novaj estis generitaj. diff --git a/config/locales/doorkeeper.es-AR.yml b/config/locales/doorkeeper.es-AR.yml index 9f3c862272..804e4a51ed 100644 --- a/config/locales/doorkeeper.es-AR.yml +++ b/config/locales/doorkeeper.es-AR.yml @@ -60,7 +60,7 @@ es-AR: error: title: Ocurrió un error new: - prompt_html: "%{client_name} le gustaría obtener permiso para acceder a tu cuenta. Solo aprueba esta solicitud si reconoces y confías en esta fuente." + prompt_html: A %{client_name} le gustaría obtener permiso para acceder a tu cuenta. Solo aprueba esta solicitud si reconoces y confías en esta fuente. review_permissions: Revisar permisos title: Autorización requerida show: diff --git a/config/locales/doorkeeper.es-MX.yml b/config/locales/doorkeeper.es-MX.yml index 419bb58f95..c095777954 100644 --- a/config/locales/doorkeeper.es-MX.yml +++ b/config/locales/doorkeeper.es-MX.yml @@ -60,7 +60,7 @@ es-MX: error: title: Ha ocurrido un error new: - prompt_html: "%{client_name} le gustaría obtener permiso para acceder a tu cuenta. Solo aprueba esta solicitud si reconoces y confías en esta fuente." + prompt_html: A %{client_name} le gustaría obtener permiso para acceder a tu cuenta. Solo aprueba esta solicitud si reconoces y confías en esta fuente. review_permissions: Revisar permisos title: Se requiere autorización show: diff --git a/config/locales/doorkeeper.es.yml b/config/locales/doorkeeper.es.yml index 093d84397a..c26f11a7a1 100644 --- a/config/locales/doorkeeper.es.yml +++ b/config/locales/doorkeeper.es.yml @@ -60,7 +60,7 @@ es: error: title: Ha ocurrido un error new: - prompt_html: "%{client_name} le gustaría obtener permiso para acceder a tu cuenta. Solo aprueba esta solicitud si reconoces y confías en esta fuente." + prompt_html: A %{client_name} le gustaría obtener permiso para acceder a tu cuenta. Solo aprueba esta solicitud si reconoces y confías en esta fuente. review_permissions: Revisar permisos title: Se requiere autorización show: diff --git a/config/locales/doorkeeper.hu.yml b/config/locales/doorkeeper.hu.yml index ff37786d28..a13c362173 100644 --- a/config/locales/doorkeeper.hu.yml +++ b/config/locales/doorkeeper.hu.yml @@ -60,6 +60,7 @@ hu: error: title: Hiba történt new: + prompt_html: A(z) %{client_name} engedélyt kér hogy hozzáférjen a fiókodhoz. Csak akkor engedélyezd ezt a kérést, ha felismered és megbízol ebben a forrásban. review_permissions: Jogosultságok áttekintése title: Engedélyezés szükséges show: diff --git a/config/locales/doorkeeper.vi.yml b/config/locales/doorkeeper.vi.yml index 195e527f70..6687c0339d 100644 --- a/config/locales/doorkeeper.vi.yml +++ b/config/locales/doorkeeper.vi.yml @@ -71,7 +71,7 @@ vi: confirmations: revoke: Bạn có chắc không? index: - authorized_at: Cho phép %{date} + authorized_at: Cho phép vào %{date} description_html: Đây là những ứng dụng có thể truy cập tài khoản của bạn bằng API. Nếu có ứng dụng bạn không nhận ra ở đây hoặc ứng dụng hoạt động sai, bạn có thể thu hồi quyền truy cập của ứng dụng đó. last_used_at: Dùng lần cuối %{date} never_used: Chưa dùng @@ -151,7 +151,7 @@ vi: scopes: admin:read: đọc mọi dữ liệu trên máy chủ admin:read:accounts: đọc thông tin nhạy cảm của tất cả các tài khoản - admin:read:canonical_email_blocks: đọc thông tin nhạy cảm của tất cả các khối email chuẩn + admin:read:canonical_email_blocks: đọc thông tin nhạy cảm của tất cả khối email chuẩn admin:read:domain_allows: đọc thông tin nhạy cảm của tất cả các tên miền cho phép admin:read:domain_blocks: đọc thông tin nhạy cảm của tất cả các tên miền chặn admin:read:email_domain_blocks: đọc thông tin nhạy cảm của tất cả các miền email chặn diff --git a/config/locales/simple_form.vi.yml b/config/locales/simple_form.vi.yml index e675209017..010bb262ad 100644 --- a/config/locales/simple_form.vi.yml +++ b/config/locales/simple_form.vi.yml @@ -42,7 +42,7 @@ vi: autofollow: Những người đăng ký sẽ tự động theo dõi bạn avatar: WEBP, PNG, GIF hoặc JPG, tối đa %{size}. Sẽ bị nén xuống %{dimensions}px bot: Tài khoản này tự động thực hiện các hành động và không được quản lý bởi người thật - context: Chọn một hoặc nhiều nơi mà bộ lọc sẽ áp dụng + context: Chọn những nơi mà bộ lọc sẽ áp dụng current_password: Vì mục đích bảo mật, vui lòng nhập mật khẩu của tài khoản hiện tại current_username: Để xác nhận, vui lòng nhập tên người dùng của tài khoản hiện tại digest: Chỉ gửi sau một thời gian dài không hoạt động hoặc khi bạn nhận được tin nhắn (trong thời gian vắng mặt) @@ -51,7 +51,7 @@ vi: inbox_url: Sao chép URL của máy chủ mà bạn muốn dùng irreversible: Các tút đã lọc sẽ không thể phục hồi, kể cả sau khi xóa bộ lọc locale: Ngôn ngữ của giao diện, email và thông báo đẩy - password: Dùng ít nhất 8 ký tự + password: Tối thiểu 8 ký tự phrase: Sẽ được hiện thị trong văn bản hoặc cảnh báo nội dung của một tút scopes: Ứng dụng sẽ được phép truy cập những API nào. Nếu bạn chọn quyền cấp cao nhất, không cần chọn quyền nhỏ. setting_aggregate_reblogs: Nếu một tút đã được đăng lại thì những lượt đăng lại sau sẽ không hiện trên bảng tin nữa @@ -74,8 +74,8 @@ vi: filters: action: Chọn hành động sẽ thực hiện khi một tút khớp với bộ lọc actions: - hide: Ẩn hoàn toàn nội dung đã lọc, như thể nó không tồn tại - warn: Ẩn nội dung đã lọc đằng sau một cảnh báo đề cập đến tiêu đề của bộ lọc + hide: Ẩn hoàn toàn, như thể nó không tồn tại + warn: Hiện cảnh báo và bộ lọc form_admin_settings: activity_api_enabled: Số lượng tút được đăng trong máy chủ, người dùng đang hoạt động và đăng ký mới hàng tuần app_icon: WEBP, PNG, GIF hoặc JPG. Dùng biểu tượng tùy chỉnh trên thiết bị di động. @@ -226,7 +226,7 @@ vi: setting_theme: Giao diện setting_trends: Hiển thị xu hướng trong ngày setting_unfollow_modal: Hỏi trước khi bỏ theo dõi ai đó - setting_use_blurhash: Phủ màu media nhạy cảm + setting_use_blurhash: Làm mờ media nhạy cảm setting_use_pending_items: Không tự động cập nhật bảng tin severity: Mức độ nghiêm trọng sign_in_token_attempt: Mã an toàn @@ -305,7 +305,7 @@ vi: label: Đã có phiên bản Mastodon mới none: Không bao giờ thông báo (không đề xuất) patch: Thông báo bản cập sửa lỗi - trending_tag: Phê duyệt nội dung nổi bật mới + trending_tag: Phê duyệt xu hướng mới rule: hint: Thông tin thêm text: Nội quy diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 83531c6561..d969ad7d4f 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -44,7 +44,7 @@ vi: submit: Thay đổi email title: Thay đổi email cho %{username} change_role: - changed_msg: Vai trò đã thay đổi thành công! + changed_msg: Đã cập nhật vai trò! edit_roles: Quản lý vai trò người dùng label: Đổi vai trò no_role: Chưa có vai trò @@ -55,7 +55,7 @@ vi: custom: Tùy chỉnh delete: Xóa dữ liệu deleted: Đã xóa - demote: Xóa vai trò + demote: Hạ vai trò destroyed_msg: Dữ liệu %{username} sẽ được lên lịch xóa ngay bây giờ disable: Khóa disable_sign_in_token_auth: Tắt xác minh bằng email @@ -108,7 +108,7 @@ vi: previous_strikes: Lịch sử kiểm duyệt previous_strikes_description_html: other: Người này bị cảnh cáo %{count} lần. - promote: Chỉ định vai trò + promote: Nâng vai trò protocol: Giao thức public: Công khai push_subscription_expires: Đăng ký PuSH hết hạn @@ -153,8 +153,8 @@ vi: suspension_irreversible: Toàn bộ dữ liệu của người này sẽ bị xóa hết. Bạn vẫn có thể ngừng vô hiệu hóa nhưng dữ liệu sẽ không thể phục hồi. suspension_reversible_hint_html: Mọi dữ liệu của người này sẽ bị xóa sạch vào %{date}. Trước thời hạn này, dữ liệu vẫn có thể phục hồi. Nếu bạn muốn xóa dữ liệu của người này ngay lập tức, hãy tiếp tục. title: Tài khoản - unblock_email: Mở khóa địa chỉ email - unblocked_email_msg: Mở khóa thành công địa chỉ email của %{username} + unblock_email: Bỏ chặn địa chỉ email + unblocked_email_msg: Đã bỏ chặn địa chỉ email của %{username} unconfirmed_email: Email chưa được xác minh undo_sensitized: Đánh dấu bình thường undo_silenced: Bỏ hạn chế @@ -170,42 +170,42 @@ vi: action_logs: action_types: approve_appeal: Chấp nhận kháng cáo - approve_user: Chấp nhận đăng ký + approve_user: Duyệt đăng ký assigned_to_self_report: Tự xử lý báo cáo change_email_user: Đổi email người dùng change_role_user: Đổi vai trò confirm_user: Xác minh create_account_warning: Cảnh cáo create_announcement: Tạo thông báo mới - create_canonical_email_block: Tạo chặn email + create_canonical_email_block: Chặn địa chỉ email create_custom_emoji: Tạo emoji create_domain_allow: Cho phép máy chủ create_domain_block: Chặn máy chủ create_email_domain_block: Tạo chặn tên miền email - create_ip_block: Tạo chặn IP mới - create_unavailable_domain: Máy chủ không khả dụng + create_ip_block: Chặn IP + create_unavailable_domain: Ngừng liên hợp create_user_role: Tạo vai trò - demote_user: Xóa vai trò + demote_user: Hạ vai trò destroy_announcement: Xóa thông báo - destroy_canonical_email_block: Bỏ chặn email + destroy_canonical_email_block: Bỏ chặn địa chỉ email destroy_custom_emoji: Xóa emoji destroy_domain_allow: Bỏ thanh trừng máy chủ destroy_domain_block: Bỏ chặn máy chủ destroy_email_domain_block: Bỏ chặn tên miền email destroy_instance: Thanh trừng máy chủ - destroy_ip_block: Xóa IP đã chặn + destroy_ip_block: Bỏ chặn IP destroy_status: Xóa tút - destroy_unavailable_domain: Xóa máy chủ không khả dụng + destroy_unavailable_domain: Tái liên hợp destroy_user_role: Xóa vai trò disable_2fa_user: Vô hiệu hóa 2FA disable_custom_emoji: Vô hiệu hóa emoji disable_sign_in_token_auth_user: Tắt xác minh bằng email cho người dùng disable_user: Vô hiệu hóa đăng nhập - enable_custom_emoji: Cho phép emoji + enable_custom_emoji: Duyệt emoji enable_sign_in_token_auth_user: Bật xác minh bằng email cho người dùng - enable_user: Bỏ vô hiệu hóa đăng nhập + enable_user: Cho phép đăng nhập memorialize_account: Đánh dấu tưởng niệm - promote_user: Chỉ định vai trò + promote_user: Nâng vai trò reject_appeal: Từ chối kháng cáo reject_user: Từ chối đăng ký remove_avatar_user: Xóa ảnh đại diện @@ -213,11 +213,11 @@ vi: resend_user: Gửi lại email xác minh reset_password_user: Đặt lại mật khẩu resolve_report: Xử lý báo cáo - sensitive_account: Áp đặt nhạy cảm - silence_account: Áp đặt ẩn - suspend_account: Áp đặt vô hiệu hóa + sensitive_account: Gán nhạy cảm + silence_account: Gán ẩn + suspend_account: Gán vô hiệu hóa unassigned_report: Báo cáo chưa xử lý - unblock_email_account: Mở khóa địa chỉ email + unblock_email_account: Bỏ chặn địa chỉ email unsensitive_account: Bỏ nhạy cảm unsilence_account: Bỏ ẩn unsuspend_account: Bỏ vô hiệu hóa @@ -229,7 +229,7 @@ vi: update_status: Cập nhật tút update_user_role: Cập nhật vai trò actions: - approve_appeal_html: "%{name} đã chấp nhận kháng cáo của %{target}" + approve_appeal_html: "%{name} đã duyệt kháng cáo của %{target}" approve_user_html: "%{name} đã chấp nhận đăng ký từ %{target}" assigned_to_self_report_html: "%{name} tự xử lý báo cáo %{target}" change_email_user_html: "%{name} đã thay đổi địa chỉ email của %{target}" @@ -237,7 +237,7 @@ vi: confirm_user_html: "%{name} đã xác minh địa chỉ email của %{target}" create_account_warning_html: "%{name} đã cảnh cáo %{target}" create_announcement_html: "%{name} tạo thông báo mới %{target}" - create_canonical_email_block_html: "%{name} đã chặn email với hash %{target}" + create_canonical_email_block_html: "%{name} đã chặn địa chỉ email với hash %{target}" create_custom_emoji_html: "%{name} đã tải lên biểu tượng cảm xúc mới %{target}" create_domain_allow_html: "%{name} kích hoạt liên hợp với %{target}" create_domain_block_html: "%{name} chặn máy chủ %{target}" @@ -245,9 +245,9 @@ vi: create_ip_block_html: "%{name} đã chặn IP %{target}" create_unavailable_domain_html: "%{name} ngưng phân phối với máy chủ %{target}" create_user_role_html: "%{name} đã tạo vai trò %{target}" - demote_user_html: "%{name} đã xóa vai trò của %{target}" + demote_user_html: "%{name} đã hạ vai trò của %{target}" destroy_announcement_html: "%{name} xóa thông báo %{target}" - destroy_canonical_email_block_html: "%{name} đã bỏ chặn email với hash %{target}" + destroy_canonical_email_block_html: "%{name} đã bỏ chặn địa chỉ email với hash %{target}" destroy_custom_emoji_html: "%{name} đã xóa emoji %{target}" destroy_domain_allow_html: "%{name} đã ngừng liên hợp với %{target}" destroy_domain_block_html: "%{name} bỏ chặn máy chủ %{target}" @@ -261,11 +261,11 @@ vi: disable_custom_emoji_html: "%{name} đã ẩn emoji %{target}" disable_sign_in_token_auth_user_html: "%{name} đã tắt xác minh email của %{target}" disable_user_html: "%{name} vô hiệu hóa đăng nhập %{target}" - enable_custom_emoji_html: "%{name} cho phép Emoji %{target}" + enable_custom_emoji_html: "%{name} cho phép emoji %{target}" enable_sign_in_token_auth_user_html: "%{name} đã bật xác minh email của %{target}" enable_user_html: "%{name} bỏ vô hiệu hóa đăng nhập %{target}" memorialize_account_html: "%{name} đã biến tài khoản %{target} thành một trang tưởng niệm" - promote_user_html: "%{name} chỉ định vai trò cho %{target}" + promote_user_html: "%{name} đã nâng vai trò của %{target}" reject_appeal_html: "%{name} đã từ chối kháng cáo của %{target}" reject_user_html: "%{name} đã từ chối đăng ký từ %{target}" remove_avatar_user_html: "%{name} đã xóa ảnh đại diện của %{target}" @@ -277,7 +277,7 @@ vi: silence_account_html: "%{name} đã ẩn %{target}" suspend_account_html: "%{name} đã vô hiệu hóa %{target}" unassigned_report_html: "%{name} đã xử lý báo cáo %{target} chưa xử lí" - unblock_email_account_html: "%{name} mở khóa địa chỉ email của %{target}" + unblock_email_account_html: "%{name} bỏ chặn địa chỉ email của %{target}" unsensitive_account_html: "%{name} đánh dấu nội dung của %{target} là bình thường" unsilence_account_html: "%{name} đã bỏ ẩn %{target}" unsuspend_account_html: "%{name} đã bỏ vô hiệu hóa %{target}" @@ -287,7 +287,7 @@ vi: update_ip_block_html: "%{name} cập nhật chặn IP %{target}" update_report_html: "%{name} cập nhật báo cáo %{target}" update_status_html: "%{name} cập nhật tút của %{target}" - update_user_role_html: "%{name} đã thay đổi vai trò %{target}" + update_user_role_html: "%{name} đã cập nhật vai trò %{target}" deleted_account: tài khoản đã xóa empty: Không tìm thấy bản ghi. filter_by_action: Theo hành động @@ -328,7 +328,7 @@ vi: emoji: Emoji enable: Cho phép enabled: Đã cho phép - enabled_msg: Đã cho phép thành công Emoji này + enabled_msg: Đã cho phép emoji này xong image_hint: PNG hoặc GIF tối đa %{size} list: Danh sách listed: Liệt kê @@ -692,7 +692,7 @@ vi: manage_announcements: Quản lý thông báo manage_announcements_description: Cho phép quản lý thông báo trên máy chủ manage_appeals: Quản lý kháng cáo - manage_appeals_description: Cho phép xem xét kháng cáo đối với các hành động kiểm duyệt + manage_appeals_description: Cho phép thành viên kháng cáo đối với các hành động kiểm duyệt manage_blocks: Quản lý chặn manage_blocks_description: Cho phép người dùng tự chặn các nhà cung cấp email và địa chỉ IP manage_custom_emojis: Quản lý emoji @@ -704,7 +704,7 @@ vi: manage_reports: Quản lý báo cáo manage_reports_description: Cho phép xem xét các báo cáo và thực hiện hành động kiểm duyệt đối với chúng manage_roles: Quản lý vai trò - manage_roles_description: Cho phép quản lý và chỉ định các vai trò nhỏ hơn họ + manage_roles_description: Cho phép quản lý và nâng cấp các vai trò nhỏ hơn họ manage_rules: Quản lý nội quy máy chủ manage_rules_description: Cho phép thay đổi nội quy máy chủ manage_settings: Quản lý thiết lập @@ -798,7 +798,7 @@ vi: patch: Bản vá - sửa lỗi và dễ dàng áp dụng các thay đổi version: Phiên bản statuses: - account: Tác giả + account: Người đăng application: Ứng dụng back_to_account: Quay lại trang tài khoản back_to_report: Quay lại trang báo cáo @@ -817,7 +817,7 @@ vi: open: Mở tút original_status: Tút gốc reblogs: Lượt đăng lại - status_changed: Tút đã thay đổi + status_changed: Tút đã sửa title: Toàn bộ tút trending: Xu hướng visibility: Hiển thị @@ -896,7 +896,7 @@ vi: title: Quản trị trends: allow: Cho phép - approved: Đã cho phép + approved: Đã duyệt confirm_allow: Bạn có chắc muốn cho phép những hashtag đã chọn? confirm_disallow: Bạn có chắc muốn cấm những hashtag đã chọn? disallow: Cấm @@ -915,17 +915,17 @@ vi: no_publisher_selected: Không có nguồn đăng nào thay đổi vì không có nguồn đăng nào được chọn shared_by_over_week: other: "%{count} người chia sẻ tuần rồi" - title: Tin tức nổi bật + title: Xu hướng tin tức usage_comparison: Chia sẻ %{today} lần hôm nay, so với %{yesterday} lần hôm qua not_allowed_to_trend: Không được phép thành xu hướng - only_allowed: Chỉ cho phép + only_allowed: Đã cho phép pending_review: Đang chờ preview_card_providers: allowed: Tin tức từ nguồn này có thể lên xu hướng description_html: Đây là những nguồn mà từ đó các liên kết thường được chia sẻ trên máy chủ của bạn. Các liên kết sẽ không thể lên xu hướng trừ khi bạn cho phép nguồn. Sự cho phép (hoặc cấm) của bạn áp dụng luôn cho các tên miền phụ. rejected: Tin tức từ nguồn này không thể lên xu hướng title: Nguồn đăng - rejected: Đã cấm + rejected: Từ chối statuses: allow: Cho phép tút allow_account: Cho phép người đăng @@ -936,11 +936,11 @@ vi: description_html: Đây là những tút đang được chia sẻ và yêu thích rất nhiều trên máy chủ của bạn. Nó có thể giúp người mới và người cũ tìm thấy nhiều người hơn để theo dõi. Không có tút nào được hiển thị công khai cho đến khi bạn cho phép người đăng và người cho phép đề xuất tài khoản của họ cho người khác. Bạn cũng có thể cho phép hoặc từ chối từng tút riêng. disallow: Cấm tút disallow_account: Cấm người đăng - no_status_selected: Không có tút xu hướng nào thay đổi vì không có tút nào được chọn - not_discoverable: Tác giả đã chọn không tham gia mục khám phá + no_status_selected: Bạn chưa chọn mục nào + not_discoverable: Người đăng đã chọn không tham gia mục khám phá shared_by: other: Được thích và đăng lại %{friendly_count} lần - title: Tút xu hướng + title: Xu hướng tút tags: current_score: Chỉ số gần đây %{score} dashboard: @@ -956,9 +956,9 @@ vi: not_trendable: Không cho lên xu hướng not_usable: Không được phép dùng peaked_on_and_decaying: Đỉnh điểm %{date}, giờ đang giảm - title: Hashtag nổi bật + title: Xu hướng hashtag trendable: Cho phép lên xu hướng - trending_rank: 'Nổi bật #%{rank}' + trending_rank: 'Xu hướng #%{rank}' usable: Có thể dùng usage_comparison: Dùng %{today} lần hôm nay, so với %{yesterday} hôm qua used_by_over_week: @@ -1004,7 +1004,7 @@ vi: silence: hạn chế tài khoản của họ suspend: vô hiệu hóa tài khoản của họ body: "%{target} đã khiếu nại vì bị %{action_taken_by} %{type} vào %{date}. Họ cho biết:" - next_steps: Bạn có thể chấp nhận kháng cáo để hủy kiểm duyệt hoặc bỏ qua. + next_steps: Bạn có thể duyệt kháng cáo để hủy kiểm duyệt hoặc bỏ qua. subject: "%{username} đang khiếu nại quyết định kiểm duyệt trên %{instance}" new_critical_software_updates: body: Các phiên bản quan trọng mới của Mastodon đã được phát hành, bạn nên cập nhật càng sớm càng tốt! @@ -1022,12 +1022,12 @@ vi: new_trends: body: 'Các mục sau đây cần được xem xét trước khi chúng hiển thị công khai:' new_trending_links: - title: Tin tức nổi bật + title: Xu hướng tin tức new_trending_statuses: - title: Tút nổi bật + title: Xu hướng tút new_trending_tags: - title: Hashtag nổi bật - subject: Nội dung nổi bật chờ duyệt trên %{instance} + title: Xu hướng hashtag + subject: Xu hướng chờ duyệt trên %{instance} aliases: add_new: Kết nối tài khoản created_msg: Tạo thành công một tên hiển thị mới. Bây giờ bạn có thể bắt đầu di chuyển từ tài khoản cũ. @@ -1147,7 +1147,7 @@ vi: hint_html: Kiểm soát cách bạn được ghi nhận khi chia sẻ liên kết trên Mastodon. more_from_html: Thêm từ %{name} s_blog: "%{name}'s Blog" - title: Ghi nhận tác giả + title: Ghi nhận người đăng challenge: confirm: Tiếp tục hint_html: "Mẹo: Chúng tôi sẽ không hỏi lại mật khẩu của bạn sau này." @@ -1201,7 +1201,7 @@ vi: appealed_msg: Khiếu nại của bạn đã được gửi đi. Nếu nó được chấp nhận, bạn sẽ nhận được thông báo. appeals: submit: Gửi khiếu nại - approve_appeal: Chấp nhận kháng cáo + approve_appeal: Duyệt kháng cáo associated_report: Báo cáo đính kèm created_at: Ngày description_html: Đây là những cảnh cáo và áp đặt kiểm duyệt đối với bạn bởi đội ngũ %{instance}. @@ -1280,7 +1280,7 @@ vi: deprecated_api_multiple_keywords: Không thể thay đổi các tham số này từ ứng dụng này vì chúng áp dụng cho nhiều hơn một từ khóa bộ lọc. Sử dụng ứng dụng mới hơn hoặc giao diện web. invalid_context: Bối cảnh không hợp lệ hoặc không có index: - contexts: Bộ lọc %{contexts} + contexts: Lọc ở %{contexts} delete: Xóa bỏ empty: Chưa có bộ lọc nào. expires_in: Hết hạn trong %{distance} @@ -1336,7 +1336,7 @@ vi: merge: Hợp nhất merge_long: Giữ hồ sơ hiện có và thêm hồ sơ mới overwrite: Ghi đè - overwrite_long: Thay thế các bản ghi hiện tại bằng những cái mới + overwrite_long: Thay thế các bản ghi hiện tại bằng các bản ghi mới overwrite_preambles: blocking_html: Bạn sắp thay thế danh sách chặn với %{total_items} tài khoản từ %{filename}. bookmarks_html: Bạn sắp thay thế lượt lưu với %{total_items} tút từ %{filename}. @@ -1414,7 +1414,7 @@ vi: description_html: Nếu có lần đăng nhập đáng ngờ, hãy đổi ngay mật khẩu và bật xác minh 2 bước. empty: Không có lịch sử đăng nhập failed_sign_in_html: Đăng nhập thất bại bằng %{method} từ %{ip} (%{browser}) - successful_sign_in_html: Đăng nhập thành công bằng %{method} từ %{ip} (%{browser}) + successful_sign_in_html: Đăng nhập bằng %{method} từ %{ip} (%{browser}) title: Lịch sử đăng nhập mail_subscriptions: unsubscribe: @@ -1832,14 +1832,14 @@ vi: spam: Spam violation: Nội dung vi phạm quy tắc cộng đồng explanation: - delete_statuses: Vài tút của bạn đã vi phạm nội quy máy chủ và tạm thời bị ẩn bởi kiểm duyệt viên của %{instance}. + delete_statuses: Tút của bạn đã vi phạm nội quy máy chủ và tạm thời bị ẩn bởi kiểm duyệt viên của %{instance}. disable: Bạn không còn có thể sử dụng tài khoản của mình, nhưng hồ sơ của bạn và dữ liệu khác vẫn còn nguyên. Bạn có thể yêu cầu sao lưu dữ liệu của mình, thay đổi cài đặt tài khoản hoặc xóa tài khoản của bạn. mark_statuses_as_sensitive: Vài tút của bạn đã bị kiểm duyệt viên %{instance} đánh dấu nhạy cảm. Mọi người cần nhấn vào media để xem nó. Bạn có thể tự đánh dấu tài khoản của bạn là nhạy cảm. sensitive: Từ giờ trở đi, tất cả các media của bạn bạn tải lên sẽ được đánh dấu là nhạy cảm và ẩn đằng sau cảnh báo nhấp chuột. silence: Bạn vẫn có thể sử dụng tài khoản của mình, nhưng chỉ những người đang theo dõi bạn mới thấy bài đăng của bạn. Bạn cũng bị loại khỏi các tính năng khám phá khác. Tuy nhiên, những người khác vẫn có thể theo dõi bạn. suspend: Bạn không còn có thể sử dụng tài khoản của bạn, hồ sơ và các dữ liệu khác không còn có thể truy cập được. Trong vòng 30 ngày, bạn vẫn có thể đăng nhập để yêu cầu bản sao dữ liệu của mình cho đến khi dữ liệu bị xóa hoàn toàn, nhưng chúng tôi sẽ giữ lại một số dữ liệu cơ bản để ngăn bạn thoát khỏi việc vô hiệu hóa. reason: 'Lý do:' - statuses: 'Tút lưu ý:' + statuses: 'Tút vi phạm:' subject: delete_statuses: Những tút %{acct} của bạn đã bị xóa bỏ disable: Tài khoản %{acct} của bạn đã bị vô hiệu hóa From 69aa5699ce8fc1076d87e8f0a8954ea0caf3d464 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 25 Sep 2024 03:43:12 -0400 Subject: [PATCH 81/93] Use `not-allowed` for cursor on disabled buttons (#32076) --- 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 43ff4f28ef..25b3003b9f 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -93,7 +93,7 @@ &:disabled, &.disabled { background-color: $ui-primary-color; - cursor: default; + cursor: not-allowed; } &.copyable { From 06ecf9008b9f98a54eb4c2c0b57cdea0fdd96526 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 25 Sep 2024 03:50:15 -0400 Subject: [PATCH 82/93] Remove single-use shared examples in controller specs (#32044) --- .../settings/featured_tags_controller_spec.rb | 8 +------- .../controllers/settings/migrations_controller_spec.rb | 10 ++-------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/spec/controllers/settings/featured_tags_controller_spec.rb b/spec/controllers/settings/featured_tags_controller_spec.rb index a56ae1c498..f414e818f5 100644 --- a/spec/controllers/settings/featured_tags_controller_spec.rb +++ b/spec/controllers/settings/featured_tags_controller_spec.rb @@ -5,16 +5,10 @@ require 'rails_helper' RSpec.describe Settings::FeaturedTagsController do render_views - shared_examples 'authenticate user' do - it 'redirects to sign_in page' do - expect(subject).to redirect_to new_user_session_path - end - end - context 'when user is not signed in' do subject { post :create } - it_behaves_like 'authenticate user' + it { is_expected.to redirect_to new_user_session_path } end context 'when user is signed in' do diff --git a/spec/controllers/settings/migrations_controller_spec.rb b/spec/controllers/settings/migrations_controller_spec.rb index 93c5de0899..dca4c925fd 100644 --- a/spec/controllers/settings/migrations_controller_spec.rb +++ b/spec/controllers/settings/migrations_controller_spec.rb @@ -5,17 +5,11 @@ require 'rails_helper' RSpec.describe Settings::MigrationsController do render_views - shared_examples 'authenticate user' do - it 'redirects to sign_in page' do - expect(subject).to redirect_to new_user_session_path - end - end - describe 'GET #show' do context 'when user is not sign in' do subject { get :show } - it_behaves_like 'authenticate user' + it { is_expected.to redirect_to new_user_session_path } end context 'when user is sign in' do @@ -49,7 +43,7 @@ RSpec.describe Settings::MigrationsController do context 'when user is not sign in' do subject { post :create } - it_behaves_like 'authenticate user' + it { is_expected.to redirect_to new_user_session_path } end context 'when user is signed in' do From c3b6a7a29792ff55706a0741f88bdc43b741c46b Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 25 Sep 2024 03:56:08 -0400 Subject: [PATCH 83/93] Reduce factory creation (36 -> 12) in `spec/controllers/oauth/*` area (#32045) --- .../oauth/authorizations_controller_spec.rb | 42 +++++++------- ...authorized_applications_controller_spec.rb | 57 ++++++++----------- .../oauth/tokens_controller_spec.rb | 19 +++---- 3 files changed, 51 insertions(+), 67 deletions(-) diff --git a/spec/controllers/oauth/authorizations_controller_spec.rb b/spec/controllers/oauth/authorizations_controller_spec.rb index 9bb520211c..cfc80b8650 100644 --- a/spec/controllers/oauth/authorizations_controller_spec.rb +++ b/spec/controllers/oauth/authorizations_controller_spec.rb @@ -10,13 +10,6 @@ RSpec.describe Oauth::AuthorizationsController do get :new, params: { client_id: app.uid, response_type: 'code', redirect_uri: 'http://localhost/', scope: 'read' } end - shared_examples 'stores location for user' do - it 'stores location for user' do - subject - expect(controller.stored_location_for(:user)).to eq "/oauth/authorize?client_id=#{app.uid}&redirect_uri=http%3A%2F%2Flocalhost%2F&response_type=code&scope=read" - end - end - context 'when signed in' do let!(:user) { Fabricate(:user) } @@ -24,18 +17,17 @@ RSpec.describe Oauth::AuthorizationsController do sign_in user, scope: :user end - it 'returns http success' do + it 'returns http success and private cache control headers' do subject - expect(response).to have_http_status(200) - end - it 'returns private cache control headers' do - subject - expect(response.headers['Cache-Control']).to include('private, no-store') + expect(response) + .to have_http_status(200) + expect(response.headers['Cache-Control']) + .to include('private, no-store') + expect(controller.stored_location_for(:user)) + .to eq authorize_path_for(app) end - include_examples 'stores location for user' - context 'when app is already authorized' do before do Doorkeeper::AccessToken.find_or_create_for( @@ -52,10 +44,12 @@ RSpec.describe Oauth::AuthorizationsController do expect(response).to redirect_to(/\A#{app.redirect_uri}/) end - it 'does not redirect to callback with force_login=true' do - get :new, params: { client_id: app.uid, response_type: 'code', redirect_uri: 'http://localhost/', scope: 'read', force_login: 'true' } + context 'with `force_login` param true' do + subject do + get :new, params: { client_id: app.uid, response_type: 'code', redirect_uri: 'http://localhost/', scope: 'read', force_login: 'true' } + end - expect(response).to have_http_status(:success) + it { is_expected.to have_http_status(:success) } end end end @@ -63,10 +57,16 @@ RSpec.describe Oauth::AuthorizationsController do context 'when not signed in' do it 'redirects' do subject - expect(response).to redirect_to '/auth/sign_in' - end - include_examples 'stores location for user' + expect(response) + .to redirect_to '/auth/sign_in' + expect(controller.stored_location_for(:user)) + .to eq authorize_path_for(app) + end + end + + def authorize_path_for(app) + "/oauth/authorize?client_id=#{app.uid}&redirect_uri=http%3A%2F%2Flocalhost%2F&response_type=code&scope=read" end end end diff --git a/spec/controllers/oauth/authorized_applications_controller_spec.rb b/spec/controllers/oauth/authorized_applications_controller_spec.rb index 52d3dbde83..1cf0984abe 100644 --- a/spec/controllers/oauth/authorized_applications_controller_spec.rb +++ b/spec/controllers/oauth/authorized_applications_controller_spec.rb @@ -10,38 +10,31 @@ RSpec.describe Oauth::AuthorizedApplicationsController do get :index end - shared_examples 'stores location for user' do - it 'stores location for user' do - subject - expect(controller.stored_location_for(:user)).to eq '/oauth/authorized_applications' - end - end - context 'when signed in' do before do sign_in Fabricate(:user), scope: :user end - it 'returns http success' do + it 'returns http success with private cache control headers' do subject - expect(response).to have_http_status(200) + expect(response) + .to have_http_status(200) + expect(response.headers['Cache-Control']) + .to include('private, no-store') + expect(controller.stored_location_for(:user)) + .to eq '/oauth/authorized_applications' end - - it 'returns private cache control headers' do - subject - expect(response.headers['Cache-Control']).to include('private, no-store') - end - - include_examples 'stores location for user' end context 'when not signed in' do it 'redirects' do subject - expect(response).to redirect_to '/auth/sign_in' - end - include_examples 'stores location for user' + expect(response) + .to redirect_to '/auth/sign_in' + expect(controller.stored_location_for(:user)) + .to eq '/oauth/authorized_applications' + end end end @@ -55,23 +48,19 @@ RSpec.describe Oauth::AuthorizedApplicationsController do before do sign_in user, scope: :user allow(redis).to receive(:pipelined).and_yield(redis_pipeline_stub) + end + + it 'revokes access tokens for the application and removes subscriptions and sends kill payload to streaming' do post :destroy, params: { id: application.id } - end - it 'revokes access tokens for the application' do - expect(Doorkeeper::AccessToken.where(application: application).first.revoked_at).to_not be_nil - end - - it 'removes subscriptions for the application\'s access tokens' do - expect(Web::PushSubscription.where(user: user).count).to eq 0 - end - - it 'removes the web_push_subscription' do - expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound) - end - - it 'sends a session kill payload to the streaming server' do - expect(redis_pipeline_stub).to have_received(:publish).with("timeline:access_token:#{access_token.id}", '{"event":"kill"}') + expect(Doorkeeper::AccessToken.where(application: application).first.revoked_at) + .to_not be_nil + expect(Web::PushSubscription.where(user: user).count) + .to eq(0) + expect { web_push_subscription.reload } + .to raise_error(ActiveRecord::RecordNotFound) + expect(redis_pipeline_stub) + .to have_received(:publish).with("timeline:access_token:#{access_token.id}", '{"event":"kill"}') end end end diff --git a/spec/controllers/oauth/tokens_controller_spec.rb b/spec/controllers/oauth/tokens_controller_spec.rb index dd2d8ca70c..a2eed797e0 100644 --- a/spec/controllers/oauth/tokens_controller_spec.rb +++ b/spec/controllers/oauth/tokens_controller_spec.rb @@ -9,20 +9,15 @@ RSpec.describe Oauth::TokensController do let!(:access_token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: application) } let!(:web_push_subscription) { Fabricate(:web_push_subscription, user: user, access_token: access_token) } - before do + it 'revokes the token and removes subscriptions' do post :revoke, params: { client_id: application.uid, token: access_token.token } - end - it 'revokes the token' do - expect(access_token.reload.revoked_at).to_not be_nil - end - - it 'removes web push subscription for token' do - expect(Web::PushSubscription.where(access_token: access_token).count).to eq 0 - end - - it 'removes the web_push_subscription' do - expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect(access_token.reload.revoked_at) + .to_not be_nil + expect(Web::PushSubscription.where(access_token: access_token).count) + .to eq(0) + expect { web_push_subscription.reload } + .to raise_error(ActiveRecord::RecordNotFound) end end end From c2ef83ea4c045eefe9930b66ea6abe6ddeb31740 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 25 Sep 2024 03:56:42 -0400 Subject: [PATCH 84/93] Consolidate shared `a scope` example parts into one attributes check (#32046) --- spec/lib/scope_transformer_spec.rb | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/spec/lib/scope_transformer_spec.rb b/spec/lib/scope_transformer_spec.rb index 09a31e04c5..f4003352e4 100644 --- a/spec/lib/scope_transformer_spec.rb +++ b/spec/lib/scope_transformer_spec.rb @@ -7,16 +7,13 @@ RSpec.describe ScopeTransformer do subject { described_class.new.apply(ScopeParser.new.parse(input)) } shared_examples 'a scope' do |namespace, term, access| - it 'parses the term' do - expect(subject.term).to eq term - end - - it 'parses the namespace' do - expect(subject.namespace).to eq namespace - end - - it 'parses the access' do - expect(subject.access).to eq access + it 'parses the attributes' do + expect(subject) + .to have_attributes( + term: term, + namespace: namespace, + access: access + ) end end From 83574f641aba9278bbc9c0d940717dad00522634 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 25 Sep 2024 04:07:48 -0400 Subject: [PATCH 85/93] Add coverage and use mailer callback to check functional user in notification mailer (#32055) --- app/mailers/notification_mailer.rb | 16 +++++++++------- spec/mailers/notification_mailer_spec.rb | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb index 6b21b4bedd..4c374f5d57 100644 --- a/app/mailers/notification_mailer.rb +++ b/app/mailers/notification_mailer.rb @@ -13,12 +13,14 @@ class NotificationMailer < ApplicationMailer before_action :set_account, only: [:follow, :favourite, :reblog, :follow_request] after_action :set_list_headers! + before_deliver :verify_functional_user + default to: -> { email_address_with_name(@user.email, @me.username) } layout 'mailer' def mention - return unless @user.functional? && @status.present? + return if @status.blank? locale_for_account(@me) do mail subject: default_i18n_subject(name: @status.account.acct) @@ -26,15 +28,13 @@ class NotificationMailer < ApplicationMailer end def follow - return unless @user.functional? - locale_for_account(@me) do mail subject: default_i18n_subject(name: @account.acct) end end def favourite - return unless @user.functional? && @status.present? + return if @status.blank? locale_for_account(@me) do mail subject: default_i18n_subject(name: @account.acct) @@ -42,7 +42,7 @@ class NotificationMailer < ApplicationMailer end def reblog - return unless @user.functional? && @status.present? + return if @status.blank? locale_for_account(@me) do mail subject: default_i18n_subject(name: @account.acct) @@ -50,8 +50,6 @@ class NotificationMailer < ApplicationMailer end def follow_request - return unless @user.functional? - locale_for_account(@me) do mail subject: default_i18n_subject(name: @account.acct) end @@ -75,6 +73,10 @@ class NotificationMailer < ApplicationMailer @account = @notification.from_account end + def verify_functional_user + throw(:abort) unless @user.functional? + end + def set_list_headers! headers( 'List-ID' => "<#{@type}.#{@me.username}.#{Rails.configuration.x.local_domain}>", diff --git a/spec/mailers/notification_mailer_spec.rb b/spec/mailers/notification_mailer_spec.rb index 056000b042..4c6107d9f7 100644 --- a/spec/mailers/notification_mailer_spec.rb +++ b/spec/mailers/notification_mailer_spec.rb @@ -3,6 +3,17 @@ require 'rails_helper' RSpec.describe NotificationMailer do + shared_examples 'delivery to non functional user' do + context 'when user is not functional' do + before { receiver.update(confirmed_at: nil) } + + it 'does not deliver mail' do + emails = capture_emails { mail.deliver_now } + expect(emails).to be_empty + end + end + end + let(:receiver) { Fabricate(:user, account_attributes: { username: 'alice' }) } let(:sender) { Fabricate(:account, username: 'bob') } let(:foreign_status) { Fabricate(:status, account: sender, text: 'The body of the foreign status') } @@ -24,6 +35,8 @@ RSpec.describe NotificationMailer do .and have_thread_headers .and have_standard_headers('mention').for(receiver) end + + include_examples 'delivery to non functional user' end describe 'follow' do @@ -40,6 +53,8 @@ RSpec.describe NotificationMailer do .and(have_body_text('bob is now following you')) .and have_standard_headers('follow').for(receiver) end + + include_examples 'delivery to non functional user' end describe 'favourite' do @@ -58,6 +73,8 @@ RSpec.describe NotificationMailer do .and have_thread_headers .and have_standard_headers('favourite').for(receiver) end + + include_examples 'delivery to non functional user' end describe 'reblog' do @@ -76,6 +93,8 @@ RSpec.describe NotificationMailer do .and have_thread_headers .and have_standard_headers('reblog').for(receiver) end + + include_examples 'delivery to non functional user' end describe 'follow_request' do @@ -92,6 +111,8 @@ RSpec.describe NotificationMailer do .and(have_body_text('bob has requested to follow you')) .and have_standard_headers('follow_request').for(receiver) end + + include_examples 'delivery to non functional user' end private From 3dc4ddc6631585813c1c5bce9e8679f101a03a56 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Wed, 25 Sep 2024 15:35:37 +0200 Subject: [PATCH 86/93] Fix search params being dropped when redirected to non-deck path (#31984) --- .../concerns/web_app_controller_concern.rb | 2 +- app/javascript/mastodon/features/ui/index.jsx | 2 +- app/lib/permalink_redirector.rb | 2 +- spec/lib/permalink_redirector_spec.rb | 15 +++++++++++++++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/app/controllers/concerns/web_app_controller_concern.rb b/app/controllers/concerns/web_app_controller_concern.rb index e1f599dcb0..ebbdba59af 100644 --- a/app/controllers/concerns/web_app_controller_concern.rb +++ b/app/controllers/concerns/web_app_controller_concern.rb @@ -31,7 +31,7 @@ module WebAppControllerConcern def redirect_unauthenticated_to_permalinks! return if user_signed_in? && current_account.moved_to_account_id.nil? - permalink_redirector = PermalinkRedirector.new(request.path) + permalink_redirector = PermalinkRedirector.new(request.original_fullpath) return if permalink_redirector.redirect_path.blank? expires_in(15.seconds, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day) unless user_signed_in? diff --git a/app/javascript/mastodon/features/ui/index.jsx b/app/javascript/mastodon/features/ui/index.jsx index a6e3640478..2f9f962b81 100644 --- a/app/javascript/mastodon/features/ui/index.jsx +++ b/app/javascript/mastodon/features/ui/index.jsx @@ -186,7 +186,7 @@ class SwitchingColumnsArea extends PureComponent { {redirect} {singleColumn ? : null} - {singleColumn && pathName.startsWith('/deck/') ? : null} + {singleColumn && pathName.startsWith('/deck/') ? : null} {/* Redirect old bookmarks (without /deck) with home-like routes to the advanced interface */} {!singleColumn && pathName === '/getting-started' ? : null} {!singleColumn && pathName === '/home' ? : null} diff --git a/app/lib/permalink_redirector.rb b/app/lib/permalink_redirector.rb index f551f69db8..142a05d10d 100644 --- a/app/lib/permalink_redirector.rb +++ b/app/lib/permalink_redirector.rb @@ -83,6 +83,6 @@ class PermalinkRedirector end def path_segments - @path_segments ||= @path.delete_prefix('/deck').delete_prefix('/').split('/') + @path_segments ||= @path.split('?')[0].delete_prefix('/deck').delete_prefix('/').split('/') end end diff --git a/spec/lib/permalink_redirector_spec.rb b/spec/lib/permalink_redirector_spec.rb index 3f77d7665a..5a544c3d38 100644 --- a/spec/lib/permalink_redirector_spec.rb +++ b/spec/lib/permalink_redirector_spec.rb @@ -29,5 +29,20 @@ RSpec.describe PermalinkRedirector do redirector = described_class.new('@alice/123') expect(redirector.redirect_path).to eq 'https://example.com/status-123' end + + it 'returns path for legacy status links with a query param' do + redirector = described_class.new('statuses/123?foo=bar') + expect(redirector.redirect_path).to eq 'https://example.com/status-123' + end + + it 'returns path for pretty status links with a query param' do + redirector = described_class.new('@alice/123?foo=bar') + expect(redirector.redirect_path).to eq 'https://example.com/status-123' + end + + it 'returns path for deck URLs with query params' do + redirector = described_class.new('/deck/directory?local=true') + expect(redirector.redirect_path).to eq '/directory?local=true' + end end end From d6f5ee75ab91eba676e4e200d4e6a98a5aed91ef Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Wed, 25 Sep 2024 15:36:19 +0200 Subject: [PATCH 87/93] Add notification grouping for follow notifications (#32085) --- .../mastodon/actions/notification_groups.ts | 9 +++++- app/javascript/mastodon/api/notifications.ts | 1 + app/models/notification.rb | 29 ++++++++++++++++++- app/services/notify_service.rb | 21 +------------- 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/app/javascript/mastodon/actions/notification_groups.ts b/app/javascript/mastodon/actions/notification_groups.ts index 9d3fc0d425..b40b04f8cc 100644 --- a/app/javascript/mastodon/actions/notification_groups.ts +++ b/app/javascript/mastodon/actions/notification_groups.ts @@ -68,10 +68,15 @@ function dispatchAssociatedRecords( dispatch(importFetchedStatuses(fetchedStatuses)); } +const supportedGroupedNotificationTypes = ['favourite', 'reblog']; + export const fetchNotifications = createDataLoadingThunk( 'notificationGroups/fetch', async (_params, { getState }) => - apiFetchNotificationGroups({ exclude_types: getExcludedTypes(getState()) }), + apiFetchNotificationGroups({ + grouped_types: supportedGroupedNotificationTypes, + exclude_types: getExcludedTypes(getState()), + }), ({ notifications, accounts, statuses }, { dispatch }) => { dispatch(importFetchedAccounts(accounts)); dispatch(importFetchedStatuses(statuses)); @@ -93,6 +98,7 @@ export const fetchNotificationsGap = createDataLoadingThunk( 'notificationGroups/fetchGap', async (params: { gap: NotificationGap }, { getState }) => apiFetchNotificationGroups({ + grouped_types: supportedGroupedNotificationTypes, max_id: params.gap.maxId, exclude_types: getExcludedTypes(getState()), }), @@ -109,6 +115,7 @@ export const pollRecentNotifications = createDataLoadingThunk( 'notificationGroups/pollRecentNotifications', async (_params, { getState }) => { return apiFetchNotificationGroups({ + grouped_types: supportedGroupedNotificationTypes, max_id: undefined, exclude_types: getExcludedTypes(getState()), // In slow mode, we don't want to include notifications that duplicate the already-displayed ones diff --git a/app/javascript/mastodon/api/notifications.ts b/app/javascript/mastodon/api/notifications.ts index 92863ac5ca..813e2f3a17 100644 --- a/app/javascript/mastodon/api/notifications.ts +++ b/app/javascript/mastodon/api/notifications.ts @@ -31,6 +31,7 @@ export const apiFetchNotifications = async ( export const apiFetchNotificationGroups = async (params?: { url?: string; + grouped_types?: string[]; exclude_types?: string[]; max_id?: string; since_id?: string; diff --git a/app/models/notification.rb b/app/models/notification.rb index 44a43d2ece..695f39a316 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -20,6 +20,7 @@ class Notification < ApplicationRecord self.inheritance_column = nil include Paginable + include Redisable LEGACY_TYPE_CLASS_MAP = { 'Mention' => :mention, @@ -30,7 +31,9 @@ class Notification < ApplicationRecord 'Poll' => :poll, }.freeze - GROUPABLE_NOTIFICATION_TYPES = %i(favourite reblog).freeze + # `set_group_key!` needs to be updated if this list changes + GROUPABLE_NOTIFICATION_TYPES = %i(favourite reblog follow).freeze + MAXIMUM_GROUP_SPAN_HOURS = 12 # Please update app/javascript/api_types/notification.ts if you change this PROPERTIES = { @@ -123,6 +126,30 @@ class Notification < ApplicationRecord end end + def set_group_key! + return if filtered? || Notification::GROUPABLE_NOTIFICATION_TYPES.exclude?(type) + + type_prefix = case type + when :favourite, :reblog + [type, target_status&.id].join('-') + when :follow + type + else + raise NotImplementedError + end + redis_key = "notif-group/#{account.id}/#{type_prefix}" + hour_bucket = activity.created_at.utc.to_i / 1.hour.to_i + + # Reuse previous group if it does not span too large an amount of time + previous_bucket = redis.get(redis_key).to_i + 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.hours.to_i) + + self.group_key = "#{type_prefix}-#{hour_bucket}" + end + class << self def browserable(types: [], exclude_types: [], from_account_id: nil, include_filtered: false) requested_types = if types.empty? diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb index 97eee05487..9aebab787e 100644 --- a/app/services/notify_service.rb +++ b/app/services/notify_service.rb @@ -3,8 +3,6 @@ class NotifyService < BaseService include Redisable - MAXIMUM_GROUP_SPAN_HOURS = 12 - # TODO: the severed_relationships type probably warrants email notifications NON_EMAIL_TYPES = %i( admin.report @@ -216,7 +214,7 @@ class NotifyService < BaseService return if drop? @notification.filtered = filter? - @notification.group_key = notification_group_key + @notification.set_group_key! @notification.save! # It's possible the underlying activity has been deleted @@ -236,23 +234,6 @@ class NotifyService < BaseService private - def notification_group_key - return nil if @notification.filtered || Notification::GROUPABLE_NOTIFICATION_TYPES.exclude?(@notification.type) - - type_prefix = "#{@notification.type}-#{@notification.target_status.id}" - redis_key = "notif-group/#{@recipient.id}/#{type_prefix}" - hour_bucket = @notification.activity.created_at.utc.to_i / 1.hour.to_i - - # Reuse previous group if it does not span too large an amount of time - previous_bucket = redis.get(redis_key).to_i - 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.hours.to_i) - - "#{type_prefix}-#{hour_bucket}" - end - def drop? DropCondition.new(@notification).drop? end From 51777fe3e2ec1646fece2b99ade0b802afe2088d Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 25 Sep 2024 09:54:22 -0400 Subject: [PATCH 88/93] Prefer structure checks over multi-line size/parts checks in `parsed_body` (#32063) --- .../api/v1/accounts/follower_accounts_spec.rb | 20 ++++++++---- .../v1/accounts/following_accounts_spec.rb | 20 ++++++++---- spec/requests/api/v1/directories_spec.rb | 31 ++++++++++++------- spec/requests/api/v1/peers/search_spec.rb | 8 ++--- .../statuses/favourited_by_accounts_spec.rb | 9 +++--- .../v1/statuses/reblogged_by_accounts_spec.rb | 9 +++--- 6 files changed, 60 insertions(+), 37 deletions(-) diff --git a/spec/requests/api/v1/accounts/follower_accounts_spec.rb b/spec/requests/api/v1/accounts/follower_accounts_spec.rb index 7db9884a57..61987fac1c 100644 --- a/spec/requests/api/v1/accounts/follower_accounts_spec.rb +++ b/spec/requests/api/v1/accounts/follower_accounts_spec.rb @@ -23,8 +23,11 @@ RSpec.describe 'API V1 Accounts FollowerAccounts' do expect(response).to have_http_status(200) expect(response.content_type) .to start_with('application/json') - expect(response.parsed_body.size).to eq 2 - expect([response.parsed_body[0][:id], response.parsed_body[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s) + expect(response.parsed_body) + .to contain_exactly( + hash_including(id: alice.id.to_s), + hash_including(id: bob.id.to_s) + ) end it 'does not return blocked users', :aggregate_failures do @@ -34,8 +37,10 @@ RSpec.describe 'API V1 Accounts FollowerAccounts' do expect(response).to have_http_status(200) expect(response.content_type) .to start_with('application/json') - expect(response.parsed_body.size).to eq 1 - expect(response.parsed_body[0][:id]).to eq alice.id.to_s + expect(response.parsed_body) + .to contain_exactly( + hash_including(id: alice.id.to_s) + ) end context 'when requesting user is blocked' do @@ -56,8 +61,11 @@ RSpec.describe 'API V1 Accounts FollowerAccounts' do account.mute!(bob) get "/api/v1/accounts/#{account.id}/followers", params: { limit: 2 }, headers: headers - expect(response.parsed_body.size).to eq 2 - expect([response.parsed_body[0][:id], response.parsed_body[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s) + expect(response.parsed_body) + .to contain_exactly( + hash_including(id: alice.id.to_s), + hash_including(id: bob.id.to_s) + ) end end end diff --git a/spec/requests/api/v1/accounts/following_accounts_spec.rb b/spec/requests/api/v1/accounts/following_accounts_spec.rb index ffb7332c4e..aae811467d 100644 --- a/spec/requests/api/v1/accounts/following_accounts_spec.rb +++ b/spec/requests/api/v1/accounts/following_accounts_spec.rb @@ -23,8 +23,11 @@ RSpec.describe 'API V1 Accounts FollowingAccounts' do expect(response).to have_http_status(200) expect(response.content_type) .to start_with('application/json') - expect(response.parsed_body.size).to eq 2 - expect([response.parsed_body[0][:id], response.parsed_body[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s) + expect(response.parsed_body) + .to contain_exactly( + hash_including(id: alice.id.to_s), + hash_including(id: bob.id.to_s) + ) end it 'does not return blocked users', :aggregate_failures do @@ -34,8 +37,10 @@ RSpec.describe 'API V1 Accounts FollowingAccounts' do expect(response).to have_http_status(200) expect(response.content_type) .to start_with('application/json') - expect(response.parsed_body.size).to eq 1 - expect(response.parsed_body[0][:id]).to eq alice.id.to_s + expect(response.parsed_body) + .to contain_exactly( + hash_including(id: alice.id.to_s) + ) end context 'when requesting user is blocked' do @@ -56,8 +61,11 @@ RSpec.describe 'API V1 Accounts FollowingAccounts' do account.mute!(bob) get "/api/v1/accounts/#{account.id}/following", params: { limit: 2 }, headers: headers - expect(response.parsed_body.size).to eq 2 - expect([response.parsed_body[0][:id], response.parsed_body[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s) + expect(response.parsed_body) + .to contain_exactly( + hash_including(id: alice.id.to_s), + hash_including(id: bob.id.to_s) + ) end end end diff --git a/spec/requests/api/v1/directories_spec.rb b/spec/requests/api/v1/directories_spec.rb index 282be9a582..07e65f49b7 100644 --- a/spec/requests/api/v1/directories_spec.rb +++ b/spec/requests/api/v1/directories_spec.rb @@ -84,8 +84,11 @@ RSpec.describe 'Directories API' do expect(response).to have_http_status(200) expect(response.content_type) .to start_with('application/json') - expect(response.parsed_body.size).to eq(2) - expect(response.parsed_body.pluck(:id)).to contain_exactly(eligible_remote_account.id.to_s, local_discoverable_account.id.to_s) + expect(response.parsed_body) + .to contain_exactly( + hash_including(id: eligible_remote_account.id.to_s), + hash_including(id: local_discoverable_account.id.to_s) + ) end end @@ -105,9 +108,11 @@ RSpec.describe 'Directories API' do expect(response).to have_http_status(200) expect(response.content_type) .to start_with('application/json') - expect(response.parsed_body.size).to eq(1) - expect(response.parsed_body.first[:id]).to include(local_account.id.to_s) - expect(response.body).to_not include(remote_account.id.to_s) + expect(response.parsed_body) + .to contain_exactly( + hash_including(id: local_account.id.to_s) + ) + .and not_include(remote_account.id.to_s) end end @@ -121,9 +126,11 @@ RSpec.describe 'Directories API' do expect(response).to have_http_status(200) expect(response.content_type) .to start_with('application/json') - expect(response.parsed_body.size).to eq(2) - expect(response.parsed_body.first[:id]).to include(new_stat.account_id.to_s) - expect(response.parsed_body.second[:id]).to include(old_stat.account_id.to_s) + expect(response.parsed_body) + .to contain_exactly( + hash_including(id: new_stat.account_id.to_s), + hash_including(id: old_stat.account_id.to_s) + ) end end @@ -138,9 +145,11 @@ RSpec.describe 'Directories API' do expect(response).to have_http_status(200) expect(response.content_type) .to start_with('application/json') - expect(response.parsed_body.size).to eq(2) - expect(response.parsed_body.first[:id]).to include(account_new.id.to_s) - expect(response.parsed_body.second[:id]).to include(account_old.id.to_s) + expect(response.parsed_body) + .to contain_exactly( + hash_including(id: account_new.id.to_s), + hash_including(id: account_old.id.to_s) + ) end end end diff --git a/spec/requests/api/v1/peers/search_spec.rb b/spec/requests/api/v1/peers/search_spec.rb index d00a2437f2..afcc141902 100644 --- a/spec/requests/api/v1/peers/search_spec.rb +++ b/spec/requests/api/v1/peers/search_spec.rb @@ -55,10 +55,10 @@ RSpec.describe 'API Peers Search' do .to have_http_status(200) expect(response.content_type) .to start_with('application/json') - expect(response.parsed_body.size) - .to eq(1) - expect(response.parsed_body.first) - .to eq(account.domain) + expect(response.parsed_body) + .to contain_exactly( + eq(account.domain) + ) end end end 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 6471697154..441664d099 100644 --- a/spec/requests/api/v1/statuses/favourited_by_accounts_spec.rb +++ b/spec/requests/api/v1/statuses/favourited_by_accounts_spec.rb @@ -36,8 +36,6 @@ RSpec.describe 'API V1 Statuses Favourited by Accounts' do expect(response.content_type) .to start_with('application/json') - expect(response.parsed_body.size) - .to eq(2) expect(response.parsed_body) .to contain_exactly( include(id: alice.id.to_s), @@ -50,9 +48,10 @@ RSpec.describe 'API V1 Statuses Favourited by Accounts' do subject - expect(response.parsed_body.size) - .to eq 1 - expect(response.parsed_body.first[:id]).to eq(alice.id.to_s) + expect(response.parsed_body) + .to contain_exactly( + hash_including(id: alice.id.to_s) + ) end end end 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 40457f6e89..824b5aa275 100644 --- a/spec/requests/api/v1/statuses/reblogged_by_accounts_spec.rb +++ b/spec/requests/api/v1/statuses/reblogged_by_accounts_spec.rb @@ -35,8 +35,6 @@ RSpec.describe 'API V1 Statuses Reblogged by Accounts' do expect(response.content_type) .to start_with('application/json') - expect(response.parsed_body.size) - .to eq(2) expect(response.parsed_body) .to contain_exactly( include(id: alice.id.to_s), @@ -49,9 +47,10 @@ RSpec.describe 'API V1 Statuses Reblogged by Accounts' do subject - expect(response.parsed_body.size) - .to eq 1 - expect(response.parsed_body.first[:id]).to eq(alice.id.to_s) + expect(response.parsed_body) + .to contain_exactly( + hash_including(id: alice.id.to_s) + ) end end end From 739ad0eed2f537bc49f53fb556beab52fd6e66da Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Wed, 25 Sep 2024 16:33:58 +0200 Subject: [PATCH 89/93] Keep the status action buttons at their position regardless of the counter size (#32084) Co-authored-by: Claire --- .../mastodon/components/status_action_bar.jsx | 37 ++++++++++++------- .../styles/mastodon/components.scss | 13 +++++++ 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/app/javascript/mastodon/components/status_action_bar.jsx b/app/javascript/mastodon/components/status_action_bar.jsx index 165e81c7d8..75531abf56 100644 --- a/app/javascript/mastodon/components/status_action_bar.jsx +++ b/app/javascript/mastodon/components/status_action_bar.jsx @@ -375,20 +375,29 @@ class StatusActionBar extends ImmutablePureComponent { return (
- - - - - - +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
); } diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 25b3003b9f..ea163ff9bf 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -299,6 +299,10 @@ } } + &--with-counter { + padding-inline-end: 4px; + } + &__counter { display: block; width: auto; @@ -1465,6 +1469,15 @@ body > [data-popper-placement] { } } + &__action-bar__button-wrapper { + flex-basis: 0; + flex-grow: 1; + + &:last-child { + flex-grow: 0; + } + } + &--first-in-thread { border-top: 1px solid var(--background-border-color); } From 28966fa0a6d7b98ee94696acdc79e45449ce8349 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Wed, 25 Sep 2024 17:21:11 +0200 Subject: [PATCH 90/93] Remove deprecated `v2_alpha` endpoint for grouped notifications (#32089) --- config/routes/api.rb | 30 +- .../api/v2_alpha/notifications_spec.rb | 345 ------------------ 2 files changed, 11 insertions(+), 364 deletions(-) delete mode 100644 spec/requests/api/v2_alpha/notifications_spec.rb diff --git a/config/routes/api.rb b/config/routes/api.rb index 39c699b9a3..93698381cb 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -299,21 +299,6 @@ namespace :api, format: false do end end - concern :grouped_notifications do - resources :notifications, param: :group_key, only: [:index, :show] do - collection do - post :clear - get :unread_count - end - - member do - post :dismiss - end - - resources :accounts, only: [:index], module: :notifications - end - end - namespace :v2 do get '/search', to: 'search#index', as: :search @@ -340,11 +325,18 @@ namespace :api, format: false do resource :policy, only: [:show, :update] end - concerns :grouped_notifications - end + resources :notifications, param: :group_key, only: [:index, :show] do + collection do + post :clear + get :unread_count + end - namespace :v2_alpha, module: 'v2' do - concerns :grouped_notifications + member do + post :dismiss + end + + resources :accounts, only: [:index], module: :notifications + end end namespace :web do diff --git a/spec/requests/api/v2_alpha/notifications_spec.rb b/spec/requests/api/v2_alpha/notifications_spec.rb deleted file mode 100644 index 6d7df45b65..0000000000 --- a/spec/requests/api/v2_alpha/notifications_spec.rb +++ /dev/null @@ -1,345 +0,0 @@ -# frozen_string_literal: true - -# TODO: remove this before 4.3.0-rc1 - -require 'rails_helper' - -RSpec.describe 'Notifications' do - let(:user) { Fabricate(:user, account_attributes: { username: 'alice' }) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:notifications write:notifications' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } - - describe 'GET /api/v2_alpha/notifications/unread_count', :inline_jobs do - subject do - get '/api/v2_alpha/notifications/unread_count', headers: headers, params: params - end - - let(:params) { {} } - - before do - first_status = PostStatusService.new.call(user.account, text: 'Test') - ReblogService.new.call(Fabricate(:account), first_status) - PostStatusService.new.call(Fabricate(:account), text: 'Hello @alice') - FavouriteService.new.call(Fabricate(:account), first_status) - FavouriteService.new.call(Fabricate(:account), first_status) - FollowService.new.call(Fabricate(:account), user.account) - end - - it_behaves_like 'forbidden for wrong scope', 'write write:notifications' - - context 'with no options' do - it 'returns expected notifications count' do - subject - - expect(response).to have_http_status(200) - expect(response.parsed_body[:count]).to eq 4 - end - end - - context 'with grouped_types parameter' do - let(:params) { { grouped_types: %w(reblog) } } - - it 'returns expected notifications count' do - subject - - expect(response).to have_http_status(200) - expect(response.parsed_body[:count]).to eq 5 - end - end - - context 'with a read marker' do - before do - id = user.account.notifications.browserable.order(id: :desc).offset(2).first.id - user.markers.create!(timeline: 'notifications', last_read_id: id) - end - - it 'returns expected notifications count' do - subject - - expect(response).to have_http_status(200) - expect(response.parsed_body[:count]).to eq 2 - end - end - - context 'with exclude_types param' do - let(:params) { { exclude_types: %w(mention) } } - - it 'returns expected notifications count' do - subject - - expect(response).to have_http_status(200) - expect(response.parsed_body[:count]).to eq 3 - end - end - - context 'with a user-provided limit' do - let(:params) { { limit: 2 } } - - it 'returns a capped value' do - subject - - expect(response).to have_http_status(200) - expect(response.parsed_body[:count]).to eq 2 - end - end - - context 'when there are more notifications than the limit' do - before do - stub_const('Api::V2::NotificationsController::DEFAULT_NOTIFICATIONS_COUNT_LIMIT', 2) - end - - it 'returns a capped value' do - subject - - expect(response).to have_http_status(200) - expect(response.parsed_body[:count]).to eq Api::V2::NotificationsController::DEFAULT_NOTIFICATIONS_COUNT_LIMIT - end - end - end - - describe 'GET /api/v2_alpha/notifications', :inline_jobs do - subject do - get '/api/v2_alpha/notifications', headers: headers, params: params - end - - let(:bob) { Fabricate(:user) } - let(:tom) { Fabricate(:user) } - let(:params) { {} } - - before do - first_status = PostStatusService.new.call(user.account, text: 'Test') - ReblogService.new.call(bob.account, first_status) - PostStatusService.new.call(bob.account, text: 'Hello @alice') - FavouriteService.new.call(bob.account, first_status) - FavouriteService.new.call(tom.account, first_status) - FollowService.new.call(bob.account, user.account) - end - - it_behaves_like 'forbidden for wrong scope', 'write write:notifications' - - context 'when there are no notifications' do - before do - user.account.notifications.destroy_all - end - - it 'returns 0 notifications' do - subject - - expect(response).to have_http_status(200) - expect(response.parsed_body[:notification_groups]).to eq [] - end - end - - context 'with no options' do - it 'returns expected notification types', :aggregate_failures do - subject - - expect(response).to have_http_status(200) - expect(body_json_types).to include('reblog', 'mention', 'favourite', 'follow') - end - end - - context 'with grouped_types param' do - let(:params) { { grouped_types: %w(reblog) } } - - it 'returns everything, but does not group favourites' do - subject - - expect(response).to have_http_status(200) - expect(response.parsed_body[:notification_groups]).to contain_exactly( - a_hash_including( - type: 'reblog', - sample_account_ids: [bob.account_id.to_s] - ), - a_hash_including( - type: 'mention', - sample_account_ids: [bob.account_id.to_s] - ), - a_hash_including( - type: 'favourite', - sample_account_ids: [bob.account_id.to_s] - ), - a_hash_including( - type: 'favourite', - sample_account_ids: [tom.account_id.to_s] - ), - a_hash_including( - type: 'follow', - sample_account_ids: [bob.account_id.to_s] - ) - ) - end - end - - context 'with exclude_types param' do - let(:params) { { exclude_types: %w(mention) } } - - it 'returns everything but excluded type', :aggregate_failures do - subject - - expect(response).to have_http_status(200) - expect(response.parsed_body.size).to_not eq 0 - expect(body_json_types.uniq).to_not include 'mention' - end - end - - context 'with types param' do - let(:params) { { types: %w(mention) } } - - it 'returns only requested type', :aggregate_failures do - subject - - expect(response).to have_http_status(200) - expect(body_json_types.uniq).to eq ['mention'] - expect(response.parsed_body.dig(:notification_groups, 0, :page_min_id)).to_not be_nil - end - end - - context 'with limit param' do - let(:params) { { limit: 3 } } - let(:notifications) { user.account.notifications.reorder(id: :desc) } - - it 'returns the requested number of notifications paginated', :aggregate_failures do - subject - - expect(response.parsed_body[:notification_groups].size) - .to eq(params[:limit]) - - expect(response) - .to include_pagination_headers( - prev: api_v2_notifications_url(limit: params[:limit], min_id: notifications.first.id), - # TODO: one downside of the current approach is that we return the first ID matching the group, - # not the last that has been skipped, so pagination is very likely to give overlap - next: api_v2_notifications_url(limit: params[:limit], max_id: notifications[3].id) - ) - end - end - - context 'with since_id param' do - let(:params) { { since_id: notifications[2].id } } - let(:notifications) { user.account.notifications.reorder(id: :desc) } - - it 'returns the requested number of notifications paginated', :aggregate_failures do - subject - - expect(response.parsed_body[:notification_groups].size) - .to eq(2) - - expect(response) - .to include_pagination_headers( - prev: api_v2_notifications_url(limit: params[:limit], min_id: notifications.first.id), - # TODO: one downside of the current approach is that we return the first ID matching the group, - # not the last that has been skipped, so pagination is very likely to give overlap - next: api_v2_notifications_url(limit: params[:limit], max_id: notifications[1].id) - ) - end - end - - context 'when requesting stripped-down accounts' do - let(:params) { { expand_accounts: 'partial_avatars' } } - - let(:recent_account) { Fabricate(:account) } - - before do - FavouriteService.new.call(recent_account, user.account.statuses.first) - end - - it 'returns an account in "partial_accounts", with the expected keys', :aggregate_failures do - subject - - expect(response).to have_http_status(200) - expect(response.parsed_body[:partial_accounts].size).to be > 0 - expect(response.parsed_body[:partial_accounts][0].keys.map(&:to_sym)).to contain_exactly(:acct, :avatar, :avatar_static, :bot, :id, :locked, :url) - expect(response.parsed_body[:partial_accounts].pluck(:id)).to_not include(recent_account.id.to_s) - expect(response.parsed_body[:accounts].pluck(:id)).to include(recent_account.id.to_s) - end - end - - context 'when passing an invalid value for "expand_accounts"' do - let(:params) { { expand_accounts: 'unknown_foobar' } } - - it 'returns http bad request' do - subject - - expect(response).to have_http_status(400) - end - end - - def body_json_types - response.parsed_body[:notification_groups].pluck(:type) - end - end - - describe 'GET /api/v2_alpha/notifications/:id' do - subject do - get "/api/v2_alpha/notifications/#{notification.group_key}", headers: headers - end - - let(:notification) { Fabricate(:notification, account: user.account, group_key: 'foobar') } - - it_behaves_like 'forbidden for wrong scope', 'write write:notifications' - - it 'returns http success' do - subject - - expect(response).to have_http_status(200) - end - - context 'when notification belongs to someone else' do - let(:notification) { Fabricate(:notification, group_key: 'foobar') } - - it 'returns http not found' do - subject - - expect(response).to have_http_status(404) - end - end - end - - describe 'POST /api/v2_alpha/notifications/:id/dismiss' do - subject do - post "/api/v2_alpha/notifications/#{notification.group_key}/dismiss", headers: headers - end - - let!(:notification) { Fabricate(:notification, account: user.account, group_key: 'foobar') } - - it_behaves_like 'forbidden for wrong scope', 'read read:notifications' - - it 'destroys the notification' do - subject - - expect(response).to have_http_status(200) - expect { notification.reload }.to raise_error(ActiveRecord::RecordNotFound) - end - - context 'when notification belongs to someone else' do - let(:notification) { Fabricate(:notification) } - - it 'returns http not found' do - subject - - expect(response).to have_http_status(404) - end - end - end - - describe 'POST /api/v2_alpha/notifications/clear' do - subject do - post '/api/v2_alpha/notifications/clear', headers: headers - end - - before do - Fabricate(:notification, account: user.account) - end - - it_behaves_like 'forbidden for wrong scope', 'read read:notifications' - - it 'clears notifications for the account' do - subject - - expect(user.account.reload.notifications).to be_empty - expect(response).to have_http_status(200) - end - end -end From 28c4eca0af696ff039b90b8af57aa818df442edf Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 25 Sep 2024 18:36:19 +0200 Subject: [PATCH 91/93] Change responsive break points on navigation panel in web UI (#32034) --- .../mastodon/components/navigation_portal.tsx | 32 +++--- .../mastodon/features/account/navigation.jsx | 5 +- .../features/ui/components/column_link.jsx | 7 +- .../ui/components/navigation_panel.jsx | 105 ++++++++++-------- .../styles/mastodon/components.scss | 54 +++++---- 5 files changed, 112 insertions(+), 91 deletions(-) diff --git a/app/javascript/mastodon/components/navigation_portal.tsx b/app/javascript/mastodon/components/navigation_portal.tsx index 46f2c0bfac..08f91ce18a 100644 --- a/app/javascript/mastodon/components/navigation_portal.tsx +++ b/app/javascript/mastodon/components/navigation_portal.tsx @@ -4,22 +4,22 @@ import AccountNavigation from 'mastodon/features/account/navigation'; import Trends from 'mastodon/features/getting_started/containers/trends_container'; import { showTrends } from 'mastodon/initial_state'; -const DefaultNavigation: React.FC = () => - showTrends ? ( - <> -
- - - ) : null; +const DefaultNavigation: React.FC = () => (showTrends ? : null); export const NavigationPortal: React.FC = () => ( - - - - - - - - - +
+ + + + + + + + + +
); diff --git a/app/javascript/mastodon/features/account/navigation.jsx b/app/javascript/mastodon/features/account/navigation.jsx index ccebe9043a..aa78135de2 100644 --- a/app/javascript/mastodon/features/account/navigation.jsx +++ b/app/javascript/mastodon/features/account/navigation.jsx @@ -43,10 +43,7 @@ class AccountNavigation extends PureComponent { } return ( - <> -
- - + ); } diff --git a/app/javascript/mastodon/features/ui/components/column_link.jsx b/app/javascript/mastodon/features/ui/components/column_link.jsx index 6ef122c07b..3386c17f07 100644 --- a/app/javascript/mastodon/features/ui/components/column_link.jsx +++ b/app/javascript/mastodon/features/ui/components/column_link.jsx @@ -3,11 +3,11 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; import { useRouteMatch, NavLink } from 'react-router-dom'; -import { Icon } from 'mastodon/components/icon'; +import { Icon } from 'mastodon/components/icon'; -const ColumnLink = ({ icon, activeIcon, iconComponent, activeIconComponent, text, to, href, method, badge, transparent, ...other }) => { +const ColumnLink = ({ icon, activeIcon, iconComponent, activeIconComponent, text, to, href, method, badge, transparent, optional, ...other }) => { const match = useRouteMatch(to); - const className = classNames('column-link', { 'column-link--transparent': transparent }); + const className = classNames('column-link', { 'column-link--transparent': transparent, 'column-link--optional': optional }); const badgeElement = typeof badge !== 'undefined' ? {badge} : null; const iconElement = (typeof icon === 'string' || iconComponent) ? : icon; const activeIconElement = activeIcon ?? (activeIconComponent ? : iconElement); @@ -43,6 +43,7 @@ ColumnLink.propTypes = { method: PropTypes.string, badge: PropTypes.node, transparent: PropTypes.bool, + optional: PropTypes.bool, }; export default ColumnLink; diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx index 0ca21e8976..8fa20a554d 100644 --- a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx +++ b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx @@ -120,14 +120,17 @@ class NavigationPanel extends Component { let banner = undefined; - if(transientSingleColumn) - banner = (
- {intl.formatMessage(messages.openedInClassicInterface)} - {" "} - - {intl.formatMessage(messages.advancedInterface)} - -
); + if (transientSingleColumn) { + banner = ( +
+ {intl.formatMessage(messages.openedInClassicInterface)} + {" "} + + {intl.formatMessage(messages.advancedInterface)} + +
+ ); + } return (
@@ -141,54 +144,58 @@ class NavigationPanel extends Component {
} - {signedIn && ( - <> - - - - - )} +
+ {signedIn && ( + <> + + + + + )} - {trendsEnabled ? ( - - ) : ( - - )} + {trendsEnabled ? ( + + ) : ( + + )} - {(signedIn || timelinePreview) && ( - - )} + {(signedIn || timelinePreview) && ( + + )} - {!signedIn && ( -
+ {!signedIn && ( +
+
+ { disabledAccountId ? : } +
+ )} + + {signedIn && ( + <> + + + + + + + +
+ + + + {canManageReports(permissions) && } + {canViewAdminDashboard(permissions) && } + + )} + +

- { disabledAccountId ? : } +
- )} - - {signedIn && ( - <> - - - - - - - -
- - - - {canManageReports(permissions) && } - {canViewAdminDashboard(permissions) && } - - )} - -
-
-
+
+
); diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index ea163ff9bf..847c594ae5 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -3490,12 +3490,14 @@ $ui-header-logo-wordmark-width: 99px; margin-top: 10px; margin-bottom: 10px; height: calc(100% - 20px); - overflow-y: auto; + overflow: hidden; display: flex; flex-direction: column; - & > a { - flex: 0 0 auto; + &__menu { + flex: 1 1 auto; + min-height: 0; + overflow-y: auto; } .logo { @@ -3506,6 +3508,36 @@ $ui-header-logo-wordmark-width: 99px; &__logo { margin-bottom: 12px; } + + @media screen and (height <= 710px) { + &__portal { + display: none; + } + } + + @media screen and (height <= 765px) { + &__portal .trends__item:nth-child(n + 3) { + display: none; + } + } + + @media screen and (height <= 820px) { + &__portal .trends__item:nth-child(n + 4) { + display: none; + } + } + + @media screen and (height <= 920px) { + .column-link.column-link--optional { + display: none; + } + } + + @media screen and (height <= 1040px) { + .list-panel { + display: none; + } + } } .navigation-panel, @@ -3869,22 +3901,6 @@ $ui-header-logo-wordmark-width: 99px; } } - @media screen and (height <= 810px) { - .trends__item:nth-of-type(3) { - display: none; - } - } - - @media screen and (height <= 720px) { - .trends__item:nth-of-type(2) { - display: none; - } - } - - @media screen and (height <= 670px) { - display: none; - } - .trends__item { border-bottom: 0; padding: 10px; From 3426ea2912d1cd69ebdfa4e43a119dc6e7374c49 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 25 Sep 2024 20:13:36 +0200 Subject: [PATCH 92/93] Add preview of followers removed in domain block modal in web UI (#32032) --- .../v1/domain_blocks/previews_controller.rb | 27 +++ app/javascript/mastodon/api.ts | 1 + app/javascript/mastodon/components/button.tsx | 3 + .../features/ui/components/block_modal.jsx | 2 +- .../ui/components/domain_block_modal.jsx | 106 --------- .../ui/components/domain_block_modal.tsx | 204 ++++++++++++++++++ app/javascript/mastodon/locales/en.json | 2 +- .../styles/mastodon/components.scss | 20 ++ app/javascript/styles/mastodon/variables.scss | 3 + .../domain_block_preview_presenter.rb | 5 + .../rest/domain_block_preview_serializer.rb | 5 + config/routes/api.rb | 4 + 12 files changed, 274 insertions(+), 108 deletions(-) create mode 100644 app/controllers/api/v1/domain_blocks/previews_controller.rb delete mode 100644 app/javascript/mastodon/features/ui/components/domain_block_modal.jsx create mode 100644 app/javascript/mastodon/features/ui/components/domain_block_modal.tsx create mode 100644 app/presenters/domain_block_preview_presenter.rb create mode 100644 app/serializers/rest/domain_block_preview_serializer.rb diff --git a/app/controllers/api/v1/domain_blocks/previews_controller.rb b/app/controllers/api/v1/domain_blocks/previews_controller.rb new file mode 100644 index 0000000000..a917bddd98 --- /dev/null +++ b/app/controllers/api/v1/domain_blocks/previews_controller.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class Api::V1::DomainBlocks::PreviewsController < Api::BaseController + before_action -> { doorkeeper_authorize! :follow, :write, :'write:blocks' } + before_action :require_user! + before_action :set_domain + before_action :set_domain_block_preview + + def show + render json: @domain_block_preview, serializer: REST::DomainBlockPreviewSerializer + end + + private + + def set_domain + @domain = TagManager.instance.normalize_domain(params[:domain]) + end + + def set_domain_block_preview + @domain_block_preview = with_read_replica do + DomainBlockPreviewPresenter.new( + following_count: current_account.following.where(domain: @domain).count, + followers_count: current_account.followers.where(domain: @domain).count + ) + end + end +end diff --git a/app/javascript/mastodon/api.ts b/app/javascript/mastodon/api.ts index 25bb25547c..51cbe0b695 100644 --- a/app/javascript/mastodon/api.ts +++ b/app/javascript/mastodon/api.ts @@ -70,6 +70,7 @@ export async function apiRequest( args: { params?: RequestParamsOrData; data?: RequestParamsOrData; + timeout?: number; } = {}, ) { const { data } = await api().request({ diff --git a/app/javascript/mastodon/components/button.tsx b/app/javascript/mastodon/components/button.tsx index c76aaea42f..3e720f7cee 100644 --- a/app/javascript/mastodon/components/button.tsx +++ b/app/javascript/mastodon/components/button.tsx @@ -7,6 +7,7 @@ interface BaseProps extends Omit, 'children'> { block?: boolean; secondary?: boolean; + dangerous?: boolean; } interface PropsChildren extends PropsWithChildren { @@ -26,6 +27,7 @@ export const Button: React.FC = ({ disabled, block, secondary, + dangerous, className, title, text, @@ -46,6 +48,7 @@ export const Button: React.FC = ({ className={classNames('button', className, { 'button-secondary': secondary, 'button--block': block, + 'button--dangerous': dangerous, })} disabled={disabled} onClick={handleClick} diff --git a/app/javascript/mastodon/features/ui/components/block_modal.jsx b/app/javascript/mastodon/features/ui/components/block_modal.jsx index d6fc6c4154..21a984f97f 100644 --- a/app/javascript/mastodon/features/ui/components/block_modal.jsx +++ b/app/javascript/mastodon/features/ui/components/block_modal.jsx @@ -99,7 +99,7 @@ export const BlockModal = ({ accountId, acct }) => { -
diff --git a/app/javascript/mastodon/features/ui/components/domain_block_modal.jsx b/app/javascript/mastodon/features/ui/components/domain_block_modal.jsx deleted file mode 100644 index 78d5cbb130..0000000000 --- a/app/javascript/mastodon/features/ui/components/domain_block_modal.jsx +++ /dev/null @@ -1,106 +0,0 @@ -import PropTypes from 'prop-types'; -import { useCallback } from 'react'; - -import { FormattedMessage } from 'react-intl'; - -import { useDispatch } from 'react-redux'; - -import CampaignIcon from '@/material-icons/400-24px/campaign.svg?react'; -import DomainDisabledIcon from '@/material-icons/400-24px/domain_disabled.svg?react'; -import HistoryIcon from '@/material-icons/400-24px/history.svg?react'; -import PersonRemoveIcon from '@/material-icons/400-24px/person_remove.svg?react'; -import ReplyIcon from '@/material-icons/400-24px/reply.svg?react'; -import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react'; -import { blockAccount } from 'mastodon/actions/accounts'; -import { blockDomain } from 'mastodon/actions/domain_blocks'; -import { closeModal } from 'mastodon/actions/modal'; -import { Button } from 'mastodon/components/button'; -import { Icon } from 'mastodon/components/icon'; - -export const DomainBlockModal = ({ domain, accountId, acct }) => { - const dispatch = useDispatch(); - - const handleClick = useCallback(() => { - dispatch(closeModal({ modalType: undefined, ignoreFocus: false })); - dispatch(blockDomain(domain)); - }, [dispatch, domain]); - - const handleSecondaryClick = useCallback(() => { - dispatch(closeModal({ modalType: undefined, ignoreFocus: false })); - dispatch(blockAccount(accountId)); - }, [dispatch, accountId]); - - const handleCancel = useCallback(() => { - dispatch(closeModal({ modalType: undefined, ignoreFocus: false })); - }, [dispatch]); - - return ( -
-
-
-
- -
- -
-

-
{domain}
-
-
- -
-
-
-
-
- -
-
-
-
- -
-
-
-
- -
-
-
-
- -
-
-
-
-
-
- -
-
- - -
- - - - -
-
-
- ); -}; - -DomainBlockModal.propTypes = { - domain: PropTypes.string.isRequired, - accountId: PropTypes.string.isRequired, - acct: PropTypes.string.isRequired, -}; - -export default DomainBlockModal; diff --git a/app/javascript/mastodon/features/ui/components/domain_block_modal.tsx b/app/javascript/mastodon/features/ui/components/domain_block_modal.tsx new file mode 100644 index 0000000000..7e6715990e --- /dev/null +++ b/app/javascript/mastodon/features/ui/components/domain_block_modal.tsx @@ -0,0 +1,204 @@ +import { useCallback, useEffect, useState } from 'react'; + +import { FormattedMessage } from 'react-intl'; + +import CampaignIcon from '@/material-icons/400-24px/campaign.svg?react'; +import DomainDisabledIcon from '@/material-icons/400-24px/domain_disabled.svg?react'; +import HistoryIcon from '@/material-icons/400-24px/history.svg?react'; +import PersonRemoveIcon from '@/material-icons/400-24px/person_remove.svg?react'; +import ReplyIcon from '@/material-icons/400-24px/reply.svg?react'; +import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react'; +import { blockAccount } from 'mastodon/actions/accounts'; +import { blockDomain } from 'mastodon/actions/domain_blocks'; +import { closeModal } from 'mastodon/actions/modal'; +import { apiRequest } from 'mastodon/api'; +import { Button } from 'mastodon/components/button'; +import { Icon } from 'mastodon/components/icon'; +import { LoadingIndicator } from 'mastodon/components/loading_indicator'; +import { ShortNumber } from 'mastodon/components/short_number'; +import { useAppDispatch } from 'mastodon/store'; + +interface DomainBlockPreviewResponse { + following_count: number; + followers_count: number; +} + +export const DomainBlockModal: React.FC<{ + domain: string; + accountId: string; + acct: string; +}> = ({ domain, accountId, acct }) => { + const dispatch = useAppDispatch(); + const [loading, setLoading] = useState(true); + const [preview, setPreview] = useState( + null, + ); + + const handleClick = useCallback(() => { + if (loading) { + return; // Prevent destructive action before the preview finishes loading or times out + } + + dispatch(closeModal({ modalType: undefined, ignoreFocus: false })); + dispatch(blockDomain(domain)); + }, [dispatch, loading, domain]); + + const handleSecondaryClick = useCallback(() => { + dispatch(closeModal({ modalType: undefined, ignoreFocus: false })); + dispatch(blockAccount(accountId)); + }, [dispatch, accountId]); + + const handleCancel = useCallback(() => { + dispatch(closeModal({ modalType: undefined, ignoreFocus: false })); + }, [dispatch]); + + useEffect(() => { + setLoading(true); + + apiRequest('GET', 'v1/domain_blocks/preview', { + params: { domain }, + timeout: 5000, + }) + .then((data) => { + setPreview(data); + setLoading(false); + return ''; + }) + .catch(() => { + setLoading(false); + }); + }, [setPreview, setLoading, domain]); + + return ( +
+
+
+
+ +
+ +
+

+ +

+
{domain}
+
+
+ +
+ {preview && preview.followers_count + preview.following_count > 0 && ( +
+
+ +
+
+ + + ), + followingCount: preview.following_count, + followingCountDisplay: ( + + ), + }} + /> + +
+
+ )} + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ + +
+ + + + +
+
+
+ ); +}; + +// eslint-disable-next-line import/no-default-export +export default DomainBlockModal; diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index d0563bb1b2..0dc4ccee80 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -221,7 +221,7 @@ "domain_block_modal.they_cant_follow": "Nobody from this server can follow you.", "domain_block_modal.they_wont_know": "They won't know they've been blocked.", "domain_block_modal.title": "Block domain?", - "domain_block_modal.you_will_lose_followers": "All your followers from this server will be removed.", + "domain_block_modal.you_will_lose_num_followers": "You will lose {followersCount, plural, one {{followersCountDisplay} follower} other {{followersCountDisplay} followers}} and {followingCount, plural, one {{followingCountDisplay} person you follow} other {{followingCountDisplay} people you follow}}.", "domain_block_modal.you_wont_see_posts": "You won't see posts or notifications from users on this server.", "domain_pill.activitypub_lets_connect": "It lets you connect and interact with people not just on Mastodon, but across different social apps too.", "domain_pill.activitypub_like_language": "ActivityPub is like the language Mastodon speaks with other social networks.", diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 847c594ae5..1d710546ca 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -81,6 +81,18 @@ outline: $ui-button-icon-focus-outline; } + &--dangerous { + background-color: var(--error-background-color); + color: var(--on-error-color); + + &:active, + &:focus, + &:hover { + background-color: var(--error-active-background-color); + transition: none; + } + } + &--destructive { &:active, &:focus, @@ -6237,6 +6249,14 @@ a.status-card { display: flex; gap: 16px; align-items: center; + + strong { + font-weight: 700; + } + } + + &--deemphasized { + color: $secondary-text-color; } &__icon { diff --git a/app/javascript/styles/mastodon/variables.scss b/app/javascript/styles/mastodon/variables.scss index c477e7a750..2601113d32 100644 --- a/app/javascript/styles/mastodon/variables.scss +++ b/app/javascript/styles/mastodon/variables.scss @@ -111,4 +111,7 @@ $font-monospace: 'mastodon-font-monospace' !default; --surface-variant-active-background-color: #{lighten($ui-base-color, 4%)}; --on-surface-color: #{transparentize($ui-base-color, 0.5)}; --avatar-border-radius: 8px; + --error-background-color: #{darken($error-red, 16%)}; + --error-active-background-color: #{darken($error-red, 12%)}; + --on-error-color: #fff; } diff --git a/app/presenters/domain_block_preview_presenter.rb b/app/presenters/domain_block_preview_presenter.rb new file mode 100644 index 0000000000..601f76273d --- /dev/null +++ b/app/presenters/domain_block_preview_presenter.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class DomainBlockPreviewPresenter < ActiveModelSerializers::Model + attributes :followers_count, :following_count +end diff --git a/app/serializers/rest/domain_block_preview_serializer.rb b/app/serializers/rest/domain_block_preview_serializer.rb new file mode 100644 index 0000000000..fea8c2f1ee --- /dev/null +++ b/app/serializers/rest/domain_block_preview_serializer.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class REST::DomainBlockPreviewSerializer < ActiveModel::Serializer + attributes :following_count, :followers_count +end diff --git a/config/routes/api.rb b/config/routes/api.rb index 93698381cb..46907a1ce2 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -125,6 +125,10 @@ namespace :api, format: false do get :search, to: 'search#index' end + namespace :domain_blocks do + resource :preview, only: [:show] + end + resource :domain_blocks, only: [:show, :create, :destroy] resource :directory, only: [:show] From db332553c96152302223e6c8c5df61832fab7a08 Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Wed, 25 Sep 2024 21:54:28 +0200 Subject: [PATCH 93/93] Rename "Data export" menu item (#32099) --- config/locales/en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index 625f53f2e5..9c3bab6baa 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1693,7 +1693,7 @@ en: delete: Account deletion development: Development edit_profile: Edit profile - export: Data export + export: Export featured_tags: Featured hashtags import: Import import_and_export: Import and export