From 07672e06093c9d46522a04969448b56aa5ee10df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Miko=C5=82ajczak?= <me@m4sk.in>
Date: Sun, 20 May 2018 00:55:56 +0200
Subject: [PATCH 01/14] =?UTF-8?q?=F0=9F=8C=8D:=20=F0=9F=87=B5=F0=9F=87=B1?=
 =?UTF-8?q?=E2=AC=86=EF=B8=8F=20(#7554)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Marcin Mikołajczak <me@m4sk.in>
---
 app/javascript/mastodon/locales/pl.json | 4 ++--
 config/locales/pl.yml                   | 1 +
 config/locales/simple_form.pl.yml       | 2 ++
 3 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index 34c4c72189a..7bfb7643c6c 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -61,7 +61,7 @@
   "column_subheading.navigation": "Nawigacja",
   "column_subheading.settings": "Ustawienia",
   "compose_form.direct_message_warning": "Ten wpis będzie widoczny tylko dla wszystkich wspomnianych użytkowników.",
-  "compose_form.direct_message_warning_learn_more": "Learn more",
+  "compose_form.direct_message_warning_learn_more": "Dowiedz się więcej",
   "compose_form.hashtag_warning": "Ten wpis nie będzie widoczny pod podanymi hashtagami, ponieważ jest oznaczony jako niewidoczny. Tylko publiczne wpisy mogą zostać znalezione z użyciem hashtagów.",
   "compose_form.lock_disclaimer": "Twoje konto nie jest {locked}. Każdy, kto Cię śledzi, może wyświetlać Twoje wpisy przeznaczone tylko dla śledzących.",
   "compose_form.lock_disclaimer.lock": "zablokowane",
@@ -187,7 +187,7 @@
   "notifications.column_settings.reblog": "Podbicia:",
   "notifications.column_settings.show": "Pokaż w kolumnie",
   "notifications.column_settings.sound": "Odtwarzaj dźwięk",
-  "notifications.group": "{count} notifications",
+  "notifications.group": "{count, number} {count, plural, one {powiadomienie} few {powiadomienia} many {powiadomień} more {powiadomień}}",
   "onboarding.done": "Gotowe",
   "onboarding.next": "Dalej",
   "onboarding.page_five.public_timelines": "Lokalna oś czasu zawiera wszystkie publiczne wpisy z {domain}. Globalna oś czasu wyświetla publiczne wpisy śledzonych przez członków {domain}. Są to publiczne osie czasu – najlepszy sposób na poznanie nowych osób.",
diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index 15dfdf740a7..649db3796cf 100644
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -40,6 +40,7 @@ pl:
     following: Śledzeni
     media: Zawartość multimedialna
     moved_html: "%{name} korzysta teraz z konta %{new_profile_link}:"
+    network_hidden: Ta informacja nie jest dostępna
     nothing_here: Niczego tu nie ma!
     people_followed_by: Konta śledzone przez %{name}
     people_who_follow: Osoby, które śledzą konto %{name}
diff --git a/config/locales/simple_form.pl.yml b/config/locales/simple_form.pl.yml
index 9341fc9e598..66f2a0f0b7b 100644
--- a/config/locales/simple_form.pl.yml
+++ b/config/locales/simple_form.pl.yml
@@ -19,6 +19,7 @@ pl:
           many: Pozostało <span class="name-counter">%{count}</span> znaków
           one: Pozostał <span class="name-counter">1</span> znak
           other: Pozostało <span class="name-counter">%{count}</span> znaków
+        setting_hide_network: Informacje o tym, kto Cię śledzi i kogo śledzisz nie będą widoczne
         setting_noindex: Wpływa na widoczność strony profilu i Twoich wpisów
         setting_theme: Zmienia wygląd Mastodona po zalogowaniu z dowolnego urządzenia.
       imports:
@@ -58,6 +59,7 @@ pl:
         setting_default_sensitive: Zawsze oznaczaj zawartość multimedialną jako wrażliwą
         setting_delete_modal: Pytaj o potwierdzenie przed usunięciem wpisu
         setting_display_sensitive_media: Zawsze oznaczaj zawartość multimedialną jako wrażliwą
+        setting_hide_network: Ukryj swoją sieć
         setting_noindex: Nie indeksuj mojego profilu w wyszukiwarkach internetowych
         setting_reduce_motion: Ogranicz ruch w animacjach
         setting_system_font_ui: Używaj domyślnej czcionki systemu

From 0bdda362c94c94ed34b6c5fa2ad9a2944716970c Mon Sep 17 00:00:00 2001
From: Evgeny Petrov <groosha@protonmail.com>
Date: Sun, 20 May 2018 02:03:24 +0300
Subject: [PATCH 02/14] [RU] Updated strings for 2.4.0 (#7555)

---
 config/locales/ru.yml             | 5 +++--
 config/locales/simple_form.ru.yml | 2 ++
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/config/locales/ru.yml b/config/locales/ru.yml
index 19b84461236..0f4565bbcfb 100644
--- a/config/locales/ru.yml
+++ b/config/locales/ru.yml
@@ -40,6 +40,7 @@ ru:
     following: Подписан(а)
     media: Медиаконтент
     moved_html: "%{name} переехал(а) на %{new_profile_link}:"
+    network_hidden: Эта информация недоступна
     nothing_here: Здесь ничего нет!
     people_followed_by: Люди, на которых подписан(а) %{name}
     people_who_follow: Подписчики %{name}
@@ -71,7 +72,7 @@ ru:
         title: Сменить e-mail для %{username}
       confirm: Подтвердить
       confirmed: Подтверждено
-      confirming: подтверждающий
+      confirming: Подтверждение
       demote: Разжаловать
       disable: Отключить
       disable_two_factor_authentication: Отключить 2FA
@@ -80,7 +81,7 @@ ru:
       domain: Домен
       edit: Изменить
       email: E-mail
-      email_status: Статус электронной почты
+      email_status: Статус e-mail
       enable: Включить
       enabled: Включен
       feed_url: URL фида
diff --git a/config/locales/simple_form.ru.yml b/config/locales/simple_form.ru.yml
index 955b49a1a39..a6b50b9d2e6 100644
--- a/config/locales/simple_form.ru.yml
+++ b/config/locales/simple_form.ru.yml
@@ -19,6 +19,7 @@ ru:
           many: Осталось <span class="name-counter">%{count}</span> символов
           one: Остался <span class="name-counter">1</span> символ
           other: Осталось <span class="name-counter">%{count}</span> символов
+        setting_hide_network: Те, на кого Вы подписаны и кто подписан на Вас, не будут отображены в Вашем профиле
         setting_noindex: Относится к Вашему публичному профилю и страницам статусов
         setting_theme: Влияет на внешний вид Mastodon при выполненном входе в аккаунт.
       imports:
@@ -58,6 +59,7 @@ ru:
         setting_default_sensitive: Всегда отмечать медиаконтент как чувствительный
         setting_delete_modal: Показывать диалог подтверждения перед удалением
         setting_display_sensitive_media: Всегда показывать медиаконтент, отмеченный как чувствительный
+        setting_hide_network: Скрыть свои связи
         setting_noindex: Отказаться от индексации в поисковых машинах
         setting_reduce_motion: Уменьшить движение в анимации
         setting_system_font_ui: Использовать шрифт системы по умолчанию

From 779eb07d75a27f01ab87ffefcc12a292611a03b5 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Sun, 20 May 2018 13:09:28 +0200
Subject: [PATCH 03/14] Improve default background of public profile header
 (#7556)

Looks better when no header image is set
---
 app/javascript/styles/mastodon/accounts.scss | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss
index 93aa134cf4a..de0a7ea427c 100644
--- a/app/javascript/styles/mastodon/accounts.scss
+++ b/app/javascript/styles/mastodon/accounts.scss
@@ -1,5 +1,5 @@
 .card {
-  background-color: lighten($ui-base-color, 4%);
+  background-color: $base-shadow-color;
   background-size: cover;
   background-position: center;
   border-radius: 4px 4px 0 0;

From 6eb2bc4348eb000e55cd2b59c6d6ea809196f8f4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Quent=C3=AD?= <33203663+Quenty31@users.noreply.github.com>
Date: Sun, 20 May 2018 20:26:32 +0200
Subject: [PATCH 04/14] [i18n] Update for Occitan (#7558)

* Update oc.json

* Update oc.yml

* Update simple_form.oc.yml
---
 app/javascript/mastodon/locales/oc.json | 12 ++++----
 config/locales/oc.yml                   | 39 +++++++++++++------------
 config/locales/simple_form.oc.yml       |  2 ++
 3 files changed, 28 insertions(+), 25 deletions(-)

diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index 9378cfa8f8c..44bb44e9774 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -61,9 +61,9 @@
   "column_subheading.navigation": "Navigacion",
   "column_subheading.settings": "Paramètres",
   "compose_form.direct_message_warning": "Aqueste tut serà pas que visibile pel monde mencionat.",
-  "compose_form.direct_message_warning_learn_more": "Learn more",
-  "compose_form.hashtag_warning": "Aqueste tut serà pas ligat a cap etiqueta estant qu’es pas listat. Òm pas cercar que los tuts publics per etiqueta.",
-  "compose_form.lock_disclaimer": "Vòstre compte es pas {locked}. Tot lo mond pòt vos sègre e veire los estatuts reservats als seguidors.",
+  "compose_form.direct_message_warning_learn_more": "Ne saber mai",
+  "compose_form.hashtag_warning": "Aqueste tut serà pas ligat a cap d’etiqueta estant qu’es pas listat. Òm pas cercar que los tuts publics per etiqueta.",
+  "compose_form.lock_disclaimer": "Vòstre compte es pas {locked}. Tot lo monde pòt vos sègre e veire los estatuts reservats als seguidors.",
   "compose_form.lock_disclaimer.lock": "clavat",
   "compose_form.placeholder": "A de qué pensatz ?",
   "compose_form.publish": "Tut",
@@ -187,11 +187,11 @@
   "notifications.column_settings.reblog": "Partatges :",
   "notifications.column_settings.show": "Mostrar dins la colomna",
   "notifications.column_settings.sound": "Emetre un son",
-  "notifications.group": "{count} notifications",
+  "notifications.group": "{count} notificacions",
   "onboarding.done": "Sortir",
   "onboarding.next": "Seguent",
   "onboarding.page_five.public_timelines": "Lo flux local mòstra los estatuts publics del monde de vòstra instància, aquí {domain}. Lo flux federat mòstra los estatuts publics de la gent que los de {domain} sègon. Son los fluxes publics, un bon biais de trobar de mond.",
-  "onboarding.page_four.home": "Lo flux d’acuèlh mòstra los estatuts del mond que seguètz.",
+  "onboarding.page_four.home": "Lo flux d’acuèlh mòstra los estatuts del monde que seguètz.",
   "onboarding.page_four.notifications": "La colomna de notificacions vos fa veire quand qualqu’un interagís amb vos.",
   "onboarding.page_one.federation": "Mastodon es un malhum de servidors independents que comunican per construire un malhum mai larg. Òm los apèla instàncias.",
   "onboarding.page_one.full_handle": "Vòstre escais-nom complèt",
@@ -206,7 +206,7 @@
   "onboarding.page_six.read_guidelines": "Mercés de legir la {guidelines} de {domain} !",
   "onboarding.page_six.various_app": "aplicacions per mobil",
   "onboarding.page_three.profile": "Modificatz vòstre perfil per cambiar vòstre avatar, bio e escais-nom. I a enlà totas las preferéncias.",
-  "onboarding.page_three.search": "Emplegatz la barra de recèrca per trobar de mond e engachatz las etiquetas coma {illustration} e {introductions}. Per trobar una persona d’una autra instància, picatz son identificant complèt.",
+  "onboarding.page_three.search": "Emplegatz la barra de recèrca per trobar de monde e engachatz las etiquetas coma {illustration} e {introductions}. Per trobar una persona d’una autra instància, picatz son identificant complèt.",
   "onboarding.page_two.compose": "Escrivètz un estatut dempuèi la colomna per compausar. Podètz mandar un imatge, cambiar la confidencialitat e ajustar un avertiment amb las icònas cai-jos.",
   "onboarding.skip": "Passar",
   "privacy.change": "Ajustar la confidencialitat del messatge",
diff --git a/config/locales/oc.yml b/config/locales/oc.yml
index 4a84534571f..1c596a69a04 100644
--- a/config/locales/oc.yml
+++ b/config/locales/oc.yml
@@ -40,9 +40,10 @@ oc:
     following: Abonaments
     media: Mèdias
     moved_html: "%{name} a mudat a %{new_profile_link} :"
+    network_hidden: Aquesta informacion es pas disponibla
     nothing_here: I a pas res aquí !
-    people_followed_by: Lo mond que %{name} sèc
-    people_who_follow: Lo mond que sègon %{name}
+    people_followed_by: Lo monde que %{name} sèc
+    people_who_follow: Lo monde que sègon %{name}
     posts: Tuts
     posts_with_replies: Tuts e responsas
     remote_follow: Sègre a distància
@@ -319,13 +320,13 @@ oc:
           desc_html: Afichat sus las pagina d’acuèlh quand las inscripcions son tampadas.<br>Podètz utilizar de balisas HTML
           title: Messatge de barradura de las inscripcions
         deletion:
-          desc_html: Autorizar lo mond a suprimir lor compte
+          desc_html: Autorizar lo monde a suprimir lor compte
           title: Possibilitat de suprimir lo compte
         min_invite_role:
           disabled: Degun
           title: Autorizat amb invitacions
         open:
-          desc_html: Autorizar lo mond a se marcar
+          desc_html: Autorizar lo monde a se marcar
           title: Inscripcions
       show_known_fediverse_at_about_page:
         desc_html: Un còp activat mostrarà los tuts de totes los fediverse dins l’apercebut. Autrament mostrarà pas que los tuts locals.
@@ -359,7 +360,7 @@ oc:
       failed_to_execute: Fracàs
       media:
         title: Mèdia
-      no_media: Cap mèdia
+      no_media: Cap de mèdia
       title: Estatuts del compte
       with_media: Amb mèdia
     subscriptions:
@@ -549,7 +550,7 @@ oc:
     storage: Mèdias gardats
   followers:
     domain: Domeni
-    explanation_html: Se volètz vos assegurar de la confidencialitat de vòstres estatuts, vos cal saber qual sèc vòstre compte. <strong>Vòstres estatuts privats son enviats a totas las instàncias qu’an de mond que vos sègon.</strong>. Benlèu que volètz repassar vòstra lista e tirar los seguidors s’avètz de dobtes tocant las politica de confidencialitat de lor instàncias.
+    explanation_html: Se volètz vos assegurar de la confidencialitat de vòstres estatuts, vos cal saber qual sèc vòstre compte. <strong>Vòstres estatuts privats son enviats a totas las instàncias qu’an de monde que vos sègon.</strong>. Benlèu que volètz repassar vòstra lista e tirar los seguidors s’avètz de dobtes tocant las politica de confidencialitat de lor instàncias.
     followers_count: Nombre de seguidors
     lock_link: Clavar vòstre compte
     purge: Tirar dels seguidors
@@ -557,7 +558,7 @@ oc:
       one: Soi a blocar los seguidors d’un domeni…
       other: Soi a blocar los seguidors de %{count} domenis…
     true_privacy_html: Mèfi que la <strong>vertadièra confidencialitat pòt solament èsser amb un chiframent del cap a la fin (end-to-end)</strong>.
-    unlocked_warning_html: Tot lo mond pòt vos sègre e veire sulpic vòstres estatuts privats. %{lock_link} per poder repassar e regetar los seguidors.
+    unlocked_warning_html: Tot lo monde pòt vos sègre e veire sulpic vòstres estatuts privats. %{lock_link} per poder repassar e regetar los seguidors.
     unlocked_warning_title: Vòstre compte es pas clavat
   generic:
     changes_saved_msg: Cambiaments ben realizats !
@@ -567,12 +568,12 @@ oc:
       one: I a quicòm que truca ! Mercés de corregir l’error çai-jos
       other: I a quicòm que truca ! Mercés de corregir las %{count} errors çai-jos
   imports:
-    preface: Podètz importar qualques donadas coma lo mond que seguètz o blocatz a-n aquesta instància d’un fichièr creat d’una autra instància.
+    preface: Podètz importar qualques donadas coma lo monde que seguètz o blocatz a-n aquesta instància d’un fichièr creat d’una autra instància.
     success: Vòstras donadas son ben estadas mandadas e seràn tractadas tre que possible
     types:
       blocking: Lista de blocatge
-      following: Lista de mond que seguètz
-      muting: Lista de mond que volètz pas legir
+      following: Lista de monde que seguètz
+      muting: Lista de monde que volètz pas legir
     upload: Importar
   in_memoriam_html: En Memòria.
   invites:
@@ -658,9 +659,9 @@ oc:
           trillion: T
           unit: ''
   pagination:
-    newer: Mai recent
+    newer: Mai recents
     next: Seguent
-    older: Mai ancian
+    older: Mai ancians
     prev: Precedent
     truncate: "&hellip;"
   preferences:
@@ -692,7 +693,7 @@ oc:
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
       opera: Opera
-      otter: Autre
+      otter: Otter
       phantom_js: PhantomJS
       qq: QQ Browser
       safari: Safari
@@ -760,9 +761,9 @@ oc:
       private: Seguidors solament
       private_long: Mostrar pas qu’als seguidors
       public: Public
-      public_long: Tot lo mond pòt veire
+      public_long: Tot lo monde pòt veire
       unlisted: Pas listat
-      unlisted_long: Tot lo mond pòt veire mai serà pas visible sul flux public
+      unlisted_long: Tot lo monde pòt veire mai serà pas visible sul flux public
   stream_entries:
     click_to_show: Clicatz per veire
     pinned: Tut penjat
@@ -790,7 +791,7 @@ oc:
     recovery_codes_regenerated: Los còdis de recuperacion son ben estats tornats generar
     recovery_instructions_html: Se vos arriba de perdre vòstre mobil, podètz utilizar un dels còdis de recuperacion cai-jos per poder tornar accedir a vòstre compte. <strong>Gardatz los còdis en seguretat</strong>, per exemple, imprimissètz los e gardatz los amb vòstres documents importants.
     setup: Paramètres
-    wrong_code: Lo còdi picat es invalid ! L’ora es la bona sul servidor e lo mobil ?
+    wrong_code: Lo còdi picat es invalid ! L’ora es bona sul servidor e lo mobil ?
   user_mailer:
     backup_ready:
       explanation: Avètz demandat una salvagarda complèta de vòstre compte Mastodon. Es prèsta per telecargament !
@@ -808,9 +809,9 @@ oc:
       review_preferences_step: Pensatz de configurar vòstras preferéncias, tal coma los corrièls que volètz recebrer o lo nivèl de confidencialitat de vòstres tuts per defaut. O se l’animacion vos dòna pas enveja de rendre, podètz activar la lectura automatica dels GIF.
       subject: Benvengut a Mastodon
       tip_bridge_html: Se venètz de Twitter, podètz trobar vòstres amics sus Mastodon en utilizant l‘<a href="%{bridge_url}">aplicacion de Pont</a>. Aquò fonciona pas que s’utilizan lo Pont tanben !
-      tip_federated_timeline: Lo flux d’actualitat federat es una vista generala del malhum Mastodon. Mas aquò inclutz solament lo mond que vòstres vesins sègon, doncas es pas complèt.
-      tip_following: Seguètz l’administrator del servidor per defaut. Per trobar de mond mai interessant, agachatz lo flux d’actualitat local e lo global.
-      tip_local_timeline: Lo flux d’actualitat local es una vista del mond de %{instance}. Son vòstres vesins dirèctes !
+      tip_federated_timeline: Lo flux d’actualitat federat es una vista generala del malhum Mastodon. Mas aquò inclutz solament lo monde que vòstres vesins sègon, doncas es pas complèt.
+      tip_following: Seguètz l’administrator del servidor per defaut. Per trobar de monde mai interessant, agachatz lo flux d’actualitat local e lo global.
+      tip_local_timeline: Lo flux d’actualitat local es una vista del monde de %{instance}. Son vòstres vesins dirèctes !
       tip_mobile_webapp: Se vòstre navigator mobil nos permet d’apondre Mastodon a l’ecran d‘acuèlh, podètz recebre de notificacions. Aquò se compòrta coma una aplicacion nativa !
       tips: Astúcias
       title: Vos desirem la benvenguda a bòrd %{name} !
diff --git a/config/locales/simple_form.oc.yml b/config/locales/simple_form.oc.yml
index d2ed108caa5..65b27e71669 100644
--- a/config/locales/simple_form.oc.yml
+++ b/config/locales/simple_form.oc.yml
@@ -15,6 +15,7 @@ oc:
         note:
           one: Demòra encara <span class="name-counter">1</span> caractèr
           other: Demòran encara <span class="name-counter">%{count}</span> caractèrs
+        setting_hide_network: Vòstre perfil mostrarà pas los que vos sègon e lo monde que seguètz
         setting_noindex: Aquò es destinat a vòstre perfil public e vòstra pagina d’estatuts
         setting_theme: Aquò càmbia lo tèma grafic de Mastodon quand sètz connectat qual que siasque lo periferic.
       imports:
@@ -54,6 +55,7 @@ oc:
         setting_default_sensitive: Totjorn marcar los mèdias coma sensibles
         setting_delete_modal: Afichar una fenèstra de confirmacion abans de suprimir un estatut
         setting_display_sensitive_media: Totjorn mostrar los mèdias coma sensibles
+        setting_hide_network: Amagar vòstre malhum
         setting_noindex: Èsser pas indexat pels motors de recèrca
         setting_reduce_motion: Reduire la velocitat de las animacions
         setting_system_font_ui: Utilizar la polissa del sisèma

From 9ada5328095e21691befb8ddd1081512c1858118 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Mon, 21 May 2018 03:35:37 +0200
Subject: [PATCH 05/14] Convert rich push notifications to plaintext in webapp
 (#7563)

* Convert rich push notifications to plaintext in webapp

* Fix code style issues
---
 .../mastodon/service_worker/web_push_notifications.js    | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/app/javascript/mastodon/service_worker/web_push_notifications.js b/app/javascript/mastodon/service_worker/web_push_notifications.js
index 16eaefcbb17..d104638228c 100644
--- a/app/javascript/mastodon/service_worker/web_push_notifications.js
+++ b/app/javascript/mastodon/service_worker/web_push_notifications.js
@@ -59,6 +59,9 @@ const fetchFromApi = (path, method, accessToken) => {
 const formatMessage = (messageId, locale, values = {}) =>
   (new IntlMessageFormat(locales[locale][messageId], locale)).format(values);
 
+const htmlToPlainText = html =>
+  html.replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n').replace(/<[^>]*>/g, '');
+
 const handlePush = (event) => {
   const { access_token, notification_id, preferred_locale, title, body, icon } = event.data.json();
 
@@ -76,7 +79,7 @@ const handlePush = (event) => {
       const options = {};
 
       options.title     = formatMessage(`notification.${notification.type}`, preferred_locale, { name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username });
-      options.body      = notification.status && notification.status.content;
+      options.body      = notification.status && htmlToPlainText(notification.status.content);
       options.icon      = notification.account.avatar_static;
       options.timestamp = notification.created_at && new Date(notification.created_at);
       options.tag       = notification.id;
@@ -85,10 +88,10 @@ const handlePush = (event) => {
       options.data      = { access_token, preferred_locale, id: notification.status ? notification.status.id : notification.account.id, url: notification.status ? `/web/statuses/${notification.status.id}` : `/web/accounts/${notification.account.id}` };
 
       if (notification.status && notification.status.sensitive) {
-        options.data.hiddenBody  = notification.status.content;
+        options.data.hiddenBody  = htmlToPlainText(notification.status.content);
         options.data.hiddenImage = notification.status.media_attachments.length > 0 && notification.status.media_attachments[0].preview_url;
 
-        options.body    = undefined;
+        options.body    = notification.status.spoiler_text;
         options.image   = undefined;
         options.actions = [actionExpand(preferred_locale)];
       } else if (notification.status) {

From 05f8c375a245ff6709a20f6a4ac5115a75db3f2b Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Mon, 21 May 2018 12:43:05 +0200
Subject: [PATCH 06/14] Remove small pagination limit from context API (#7564)

Fix #7557
---
 app/controllers/api/v1/statuses_controller.rb | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb
index 01880565c88..289d910454d 100644
--- a/app/controllers/api/v1/statuses_controller.rb
+++ b/app/controllers/api/v1/statuses_controller.rb
@@ -10,6 +10,12 @@ class Api::V1::StatusesController < Api::BaseController
 
   respond_to :json
 
+  # This API was originally unlimited, pagination cannot be introduced without
+  # breaking backwards-compatibility. Arbitrarily high number to cover most
+  # conversations as quasi-unlimited, it would be too much work to render more
+  # than this anyway
+  CONTEXT_LIMIT = 4_096
+
   def show
     cached  = Rails.cache.read(@status.cache_key)
     @status = cached unless cached.nil?
@@ -17,8 +23,8 @@ class Api::V1::StatusesController < Api::BaseController
   end
 
   def context
-    ancestors_results   = @status.in_reply_to_id.nil? ? [] : @status.ancestors(DEFAULT_STATUSES_LIMIT, current_account)
-    descendants_results = @status.descendants(DEFAULT_STATUSES_LIMIT, current_account)
+    ancestors_results   = @status.in_reply_to_id.nil? ? [] : @status.ancestors(CONTEXT_LIMIT, current_account)
+    descendants_results = @status.descendants(CONTEXT_LIMIT, current_account)
     loaded_ancestors    = cache_collection(ancestors_results, Status)
     loaded_descendants  = cache_collection(descendants_results, Status)
 

From 7403e5d306d36c83bfb80cd900235373186cd51a Mon Sep 17 00:00:00 2001
From: Yamagishi Kazutoshi <ykzts@desire.sh>
Date: Mon, 21 May 2018 19:43:38 +0900
Subject: [PATCH 07/14] Add media timeline (#6631)

---
 app/javascript/mastodon/actions/streaming.js  | 13 ++++---
 app/javascript/mastodon/actions/timelines.js  | 16 ++++-----
 .../features/community_timeline/index.js      | 34 ++++++++++++++-----
 .../features/public_timeline/index.js         | 34 ++++++++++++++-----
 app/javascript/mastodon/features/ui/index.js  |  2 ++
 app/javascript/mastodon/locales/ar.json       |  2 ++
 app/javascript/mastodon/locales/bg.json       |  2 ++
 app/javascript/mastodon/locales/ca.json       |  2 ++
 app/javascript/mastodon/locales/co.json       |  2 ++
 app/javascript/mastodon/locales/de.json       |  2 ++
 .../mastodon/locales/defaultMessages.json     | 16 +++++++++
 app/javascript/mastodon/locales/el.json       |  2 ++
 app/javascript/mastodon/locales/en.json       |  2 ++
 app/javascript/mastodon/locales/eo.json       |  2 ++
 app/javascript/mastodon/locales/es.json       |  2 ++
 app/javascript/mastodon/locales/eu.json       |  2 ++
 app/javascript/mastodon/locales/fa.json       |  2 ++
 app/javascript/mastodon/locales/fi.json       |  2 ++
 app/javascript/mastodon/locales/fr.json       |  2 ++
 app/javascript/mastodon/locales/gl.json       |  2 ++
 app/javascript/mastodon/locales/he.json       |  2 ++
 app/javascript/mastodon/locales/hr.json       |  2 ++
 app/javascript/mastodon/locales/hu.json       |  2 ++
 app/javascript/mastodon/locales/hy.json       |  2 ++
 app/javascript/mastodon/locales/id.json       |  2 ++
 app/javascript/mastodon/locales/io.json       |  2 ++
 app/javascript/mastodon/locales/it.json       |  2 ++
 app/javascript/mastodon/locales/ja.json       |  2 ++
 app/javascript/mastodon/locales/ko.json       |  2 ++
 app/javascript/mastodon/locales/nl.json       |  2 ++
 app/javascript/mastodon/locales/no.json       |  2 ++
 app/javascript/mastodon/locales/oc.json       |  2 ++
 app/javascript/mastodon/locales/pl.json       |  2 ++
 app/javascript/mastodon/locales/pt-BR.json    |  2 ++
 app/javascript/mastodon/locales/pt.json       |  2 ++
 app/javascript/mastodon/locales/ru.json       |  2 ++
 app/javascript/mastodon/locales/sk.json       |  2 ++
 app/javascript/mastodon/locales/sl.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/te.json       |  2 ++
 app/javascript/mastodon/locales/th.json       |  2 ++
 app/javascript/mastodon/locales/tr.json       |  2 ++
 app/javascript/mastodon/locales/uk.json       |  2 ++
 app/javascript/mastodon/locales/zh-CN.json    |  2 ++
 app/javascript/mastodon/locales/zh-HK.json    |  2 ++
 app/javascript/mastodon/locales/zh-TW.json    |  2 ++
 .../styles/mastodon/components.scss           |  2 ++
 app/services/batched_remove_status_service.rb |  5 +++
 app/services/fan_out_on_write_service.rb      |  8 +++++
 app/services/remove_status_service.rb         |  8 +++++
 streaming/index.js                            | 18 ++++++++--
 53 files changed, 205 insertions(+), 35 deletions(-)

diff --git a/app/javascript/mastodon/actions/streaming.js b/app/javascript/mastodon/actions/streaming.js
index 10e68bf3aa9..f56853bffbf 100644
--- a/app/javascript/mastodon/actions/streaming.js
+++ b/app/javascript/mastodon/actions/streaming.js
@@ -40,10 +40,9 @@ const refreshHomeTimelineAndNotification = (dispatch, done) => {
   dispatch(expandHomeTimeline({}, () => dispatch(expandNotifications({}, done))));
 };
 
-export const connectUserStream = () => connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification);
-export const connectCommunityStream = () => connectTimelineStream('community', 'public:local');
-export const connectMediaStream = () => connectTimelineStream('community', 'public:local');
-export const connectPublicStream = () => connectTimelineStream('public', 'public');
-export const connectHashtagStream = (tag) => connectTimelineStream(`hashtag:${tag}`, `hashtag&tag=${tag}`);
-export const connectDirectStream = () => connectTimelineStream('direct', 'direct');
-export const connectListStream = (id) => connectTimelineStream(`list:${id}`, `list&list=${id}`);
+export const connectUserStream      = () => connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification);
+export const connectCommunityStream = ({ onlyMedia } = {}) => connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`);
+export const connectPublicStream    = ({ onlyMedia } = {}) => connectTimelineStream(`public${onlyMedia ? ':media' : ''}`, `public${onlyMedia ? ':media' : ''}`);
+export const connectHashtagStream   = tag => connectTimelineStream(`hashtag:${tag}`, `hashtag&tag=${tag}`);
+export const connectDirectStream    = () => connectTimelineStream('direct', 'direct');
+export const connectListStream      = id => connectTimelineStream(`list:${id}`, `list&list=${id}`);
diff --git a/app/javascript/mastodon/actions/timelines.js b/app/javascript/mastodon/actions/timelines.js
index 8bcfe4db9c3..8f54dfd8a01 100644
--- a/app/javascript/mastodon/actions/timelines.js
+++ b/app/javascript/mastodon/actions/timelines.js
@@ -93,15 +93,15 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
   };
 };
 
-export const expandHomeTimeline         = ({ maxId } = {}, done = noOp) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId }, done);
-export const expandPublicTimeline       = ({ maxId } = {}, done = noOp) => expandTimeline('public', '/api/v1/timelines/public', { max_id: maxId }, done);
-export const expandCommunityTimeline    = ({ maxId } = {}, done = noOp) => expandTimeline('community', '/api/v1/timelines/public', { local: true, max_id: maxId }, done);
-export const expandDirectTimeline       = ({ maxId } = {}, done = noOp) => expandTimeline('direct', '/api/v1/timelines/direct', { max_id: maxId }, done);
-export const expandAccountTimeline      = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId });
+export const expandHomeTimeline            = ({ maxId } = {}, done = noOp) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId }, done);
+export const expandPublicTimeline          = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`public${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { max_id: maxId, only_media: !!onlyMedia }, done);
+export const expandCommunityTimeline       = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { local: true, max_id: maxId, only_media: !!onlyMedia }, done);
+export const expandDirectTimeline          = ({ maxId } = {}, done = noOp) => expandTimeline('direct', '/api/v1/timelines/direct', { max_id: maxId }, done);
+export const expandAccountTimeline         = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId });
 export const expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
-export const expandAccountMediaTimeline = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true });
-export const expandHashtagTimeline      = (hashtag, { maxId } = {}, done = noOp) => expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`, { max_id: maxId }, done);
-export const expandListTimeline         = (id, { maxId } = {}, done = noOp) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done);
+export const expandAccountMediaTimeline    = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true });
+export const expandHashtagTimeline         = (hashtag, { maxId } = {}, done = noOp) => expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`, { max_id: maxId }, done);
+export const expandListTimeline            = (id, { maxId } = {}, done = noOp) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done);
 
 export function expandTimelineRequest(timeline) {
   return {
diff --git a/app/javascript/mastodon/features/community_timeline/index.js b/app/javascript/mastodon/features/community_timeline/index.js
index 870474ed580..8ad11440718 100644
--- a/app/javascript/mastodon/features/community_timeline/index.js
+++ b/app/javascript/mastodon/features/community_timeline/index.js
@@ -1,12 +1,13 @@
 import React from 'react';
 import { connect } from 'react-redux';
+import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
+import { NavLink } from 'react-router-dom';
 import PropTypes from 'prop-types';
 import StatusListContainer from '../ui/containers/status_list_container';
 import Column from '../../components/column';
 import ColumnHeader from '../../components/column_header';
 import { expandCommunityTimeline } from '../../actions/timelines';
 import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import ColumnSettingsContainer from './containers/column_settings_container';
 import { connectCommunityStream } from '../../actions/streaming';
 
@@ -14,20 +15,25 @@ const messages = defineMessages({
   title: { id: 'column.community', defaultMessage: 'Local timeline' },
 });
 
-const mapStateToProps = state => ({
-  hasUnread: state.getIn(['timelines', 'community', 'unread']) > 0,
+const mapStateToProps = (state, { onlyMedia }) => ({
+  hasUnread: state.getIn(['timelines', `community${onlyMedia ? ':media' : ''}`, 'unread']) > 0,
 });
 
 @connect(mapStateToProps)
 @injectIntl
 export default class CommunityTimeline extends React.PureComponent {
 
+  static defaultProps = {
+    onlyMedia: false,
+  };
+
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
     columnId: PropTypes.string,
     intl: PropTypes.object.isRequired,
     hasUnread: PropTypes.bool,
     multiColumn: PropTypes.bool,
+    onlyMedia: PropTypes.bool,
   };
 
   handlePin = () => {
@@ -50,10 +56,10 @@ export default class CommunityTimeline extends React.PureComponent {
   }
 
   componentDidMount () {
-    const { dispatch } = this.props;
+    const { dispatch, onlyMedia } = this.props;
 
-    dispatch(expandCommunityTimeline());
-    this.disconnect = dispatch(connectCommunityStream());
+    dispatch(expandCommunityTimeline({ onlyMedia }));
+    this.disconnect = dispatch(connectCommunityStream({ onlyMedia }));
   }
 
   componentWillUnmount () {
@@ -68,13 +74,22 @@ export default class CommunityTimeline extends React.PureComponent {
   }
 
   handleLoadMore = maxId => {
-    this.props.dispatch(expandCommunityTimeline({ maxId }));
+    const { dispatch, onlyMedia } = this.props;
+
+    dispatch(expandCommunityTimeline({ maxId, onlyMedia }));
   }
 
   render () {
-    const { intl, hasUnread, columnId, multiColumn } = this.props;
+    const { intl, hasUnread, columnId, multiColumn, onlyMedia } = this.props;
     const pinned = !!columnId;
 
+    const headline = (
+      <div className='community-timeline__section-headline'>
+        <NavLink exact to='/timelines/public/local' replace><FormattedMessage id='timeline.posts' defaultMessage='Toots' /></NavLink>
+        <NavLink exact to='/timelines/public/local/media' replace><FormattedMessage id='timeline.media' defaultMessage='Media' /></NavLink>
+      </div>
+    );
+
     return (
       <Column ref={this.setRef}>
         <ColumnHeader
@@ -91,9 +106,10 @@ export default class CommunityTimeline extends React.PureComponent {
         </ColumnHeader>
 
         <StatusListContainer
+          prepend={headline}
           trackScroll={!pinned}
           scrollKey={`community_timeline-${columnId}`}
-          timelineId='community'
+          timelineId={`community${onlyMedia ? ':media' : ''}`}
           onLoadMore={this.handleLoadMore}
           emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />}
         />
diff --git a/app/javascript/mastodon/features/public_timeline/index.js b/app/javascript/mastodon/features/public_timeline/index.js
index 5a88f760178..54904c5cfa1 100644
--- a/app/javascript/mastodon/features/public_timeline/index.js
+++ b/app/javascript/mastodon/features/public_timeline/index.js
@@ -1,12 +1,13 @@
 import React from 'react';
 import { connect } from 'react-redux';
+import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
+import { NavLink } from 'react-router-dom';
 import PropTypes from 'prop-types';
 import StatusListContainer from '../ui/containers/status_list_container';
 import Column from '../../components/column';
 import ColumnHeader from '../../components/column_header';
 import { expandPublicTimeline } from '../../actions/timelines';
 import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import ColumnSettingsContainer from './containers/column_settings_container';
 import { connectPublicStream } from '../../actions/streaming';
 
@@ -14,20 +15,25 @@ const messages = defineMessages({
   title: { id: 'column.public', defaultMessage: 'Federated timeline' },
 });
 
-const mapStateToProps = state => ({
-  hasUnread: state.getIn(['timelines', 'public', 'unread']) > 0,
+const mapStateToProps = (state, { onlyMedia }) => ({
+  hasUnread: state.getIn(['timelines', `public${onlyMedia ? ':media' : ''}`, 'unread']) > 0,
 });
 
 @connect(mapStateToProps)
 @injectIntl
 export default class PublicTimeline extends React.PureComponent {
 
+  static defaultProps = {
+    onlyMedia: false,
+  };
+
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
     intl: PropTypes.object.isRequired,
     columnId: PropTypes.string,
     multiColumn: PropTypes.bool,
     hasUnread: PropTypes.bool,
+    onlyMedia: PropTypes.bool,
   };
 
   handlePin = () => {
@@ -50,10 +56,10 @@ export default class PublicTimeline extends React.PureComponent {
   }
 
   componentDidMount () {
-    const { dispatch } = this.props;
+    const { dispatch, onlyMedia } = this.props;
 
-    dispatch(expandPublicTimeline());
-    this.disconnect = dispatch(connectPublicStream());
+    dispatch(expandPublicTimeline({ onlyMedia }));
+    this.disconnect = dispatch(connectPublicStream({ onlyMedia }));
   }
 
   componentWillUnmount () {
@@ -68,13 +74,22 @@ export default class PublicTimeline extends React.PureComponent {
   }
 
   handleLoadMore = maxId => {
-    this.props.dispatch(expandPublicTimeline({ maxId }));
+    const { dispatch, onlyMedia } = this.props;
+
+    dispatch(expandPublicTimeline({ maxId, onlyMedia }));
   }
 
   render () {
-    const { intl, columnId, hasUnread, multiColumn } = this.props;
+    const { intl, columnId, hasUnread, multiColumn, onlyMedia } = this.props;
     const pinned = !!columnId;
 
+    const headline = (
+      <div className='public-timeline__section-headline'>
+        <NavLink exact to='/timelines/public' replace><FormattedMessage id='timeline.posts' defaultMessage='Toots' /></NavLink>
+        <NavLink exact to='/timelines/public/media' replace><FormattedMessage id='timeline.media' defaultMessage='Media' /></NavLink>
+      </div>
+    );
+
     return (
       <Column ref={this.setRef}>
         <ColumnHeader
@@ -91,7 +106,8 @@ export default class PublicTimeline extends React.PureComponent {
         </ColumnHeader>
 
         <StatusListContainer
-          timelineId='public'
+          prepend={headline}
+          timelineId={`public${onlyMedia ? ':media' : ''}`}
           onLoadMore={this.handleLoadMore}
           trackScroll={!pinned}
           scrollKey={`public_timeline-${columnId}`}
diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js
index adca0d617d0..a64d623c214 100644
--- a/app/javascript/mastodon/features/ui/index.js
+++ b/app/javascript/mastodon/features/ui/index.js
@@ -141,7 +141,9 @@ class SwitchingColumnsArea extends React.PureComponent {
           <WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
           <WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} />
           <WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} />
+          <WrappedRoute path='/timelines/public/media' extract component={PublicTimeline} content={children} componentParams={{ onlyMedia: true }} />
           <WrappedRoute path='/timelines/public/local' component={CommunityTimeline} content={children} />
+          <WrappedRoute path='/timelines/public/local/media' component={CommunityTimeline} content={children} componentParams={{ onlyMedia: true }} />
           <WrappedRoute path='/timelines/direct' component={DirectTimeline} content={children} />
           <WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} />
           <WrappedRoute path='/timelines/list/:id' component={ListTimeline} content={children} />
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index 9827ac5f0ee..c8e37a3665a 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "المحلي",
   "tabs_bar.notifications": "الإخطارات",
   "tabs_bar.search": "البحث",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "سوف تفقد مسودتك إن تركت ماستدون.",
   "upload_area.title": "إسحب ثم أفلت للرفع",
   "upload_button.label": "إضافة وسائط",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index caba7703d94..9ac5aad9e74 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Известия",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Drag & drop to upload",
   "upload_button.label": "Добави медия",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index 7181589ec20..8fd42ca34a6 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificacions",
   "tabs_bar.search": "Cerca",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "El vostre esborrany es perdrà si sortiu de Mastodon.",
   "upload_area.title": "Arrossega i deixa anar per carregar",
   "upload_button.label": "Afegir multimèdia",
diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json
index c6de9d016b8..bc75813ea18 100644
--- a/app/javascript/mastodon/locales/co.json
+++ b/app/javascript/mastodon/locales/co.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Lucale",
   "tabs_bar.notifications": "Nutificazione",
   "tabs_bar.search": "Cercà",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "A bruttacopia sarà persa s'ellu hè chjosu Mastodon.",
   "upload_area.title": "Drag & drop per caricà un fugliale",
   "upload_button.label": "Aghjunghje un media",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index 4d89ea7041b..29756aff0ba 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Mitteilungen",
   "tabs_bar.search": "Suchen",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Dein Entwurf geht verloren, wenn du Mastodon verlässt.",
   "upload_area.title": "Zum Hochladen hereinziehen",
   "upload_button.label": "Mediendatei hinzufügen",
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index d2ec8e71009..1431346fe57 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -596,6 +596,14 @@
         "defaultMessage": "Local timeline",
         "id": "column.community"
       },
+      {
+        "defaultMessage": "Toots",
+        "id": "timeline.posts"
+      },
+      {
+        "defaultMessage": "Media",
+        "id": "timeline.media"
+      },
       {
         "defaultMessage": "The local timeline is empty. Write something publicly to get the ball rolling!",
         "id": "empty_column.community"
@@ -1393,6 +1401,14 @@
         "defaultMessage": "Federated timeline",
         "id": "column.public"
       },
+      {
+        "defaultMessage": "Toots",
+        "id": "timeline.posts"
+      },
+      {
+        "defaultMessage": "Media",
+        "id": "timeline.media"
+      },
       {
         "defaultMessage": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
         "id": "empty_column.public"
diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json
index c547b2e2842..1e7707b4a0e 100644
--- a/app/javascript/mastodon/locales/el.json
+++ b/app/javascript/mastodon/locales/el.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notifications",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Drag & drop to upload",
   "upload_button.label": "Add media",
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index caec0cbd315..7a977673eaa 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notifications",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Drag & drop to upload",
   "upload_button.label": "Add media",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index 2715b80c9b8..04d9dc8d511 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Loka tempolinio",
   "tabs_bar.notifications": "Sciigoj",
   "tabs_bar.search": "Serĉi",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Via malneto perdiĝos se vi eliras de Mastodon.",
   "upload_area.title": "Altreni kaj lasi por alŝuti",
   "upload_button.label": "Aldoni aŭdovidaĵon",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index 668f437b9de..6092630c483 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificaciones",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Tu borrador se perderá si sales de Mastodon.",
   "upload_area.title": "Arrastra y suelta para subir",
   "upload_button.label": "Subir multimedia",
diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json
index 8ec1407532a..a6b1f8e836e 100644
--- a/app/javascript/mastodon/locales/eu.json
+++ b/app/javascript/mastodon/locales/eu.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notifications",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Drag & drop to upload",
   "upload_button.label": "Add media",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index 605961a018b..c45277d82f4 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "محلی",
   "tabs_bar.notifications": "اعلان‌ها",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "اگر از ماستدون خارج شوید پیش‌نویس شما پاک خواهد شد.",
   "upload_area.title": "برای بارگذاری به این‌جا بکشید",
   "upload_button.label": "افزودن تصویر",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index ead1eb8c4b2..d04e0230a53 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Paikallinen",
   "tabs_bar.notifications": "Ilmoitukset",
   "tabs_bar.search": "Hae",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Luonnos häviää, jos poistut Mastodonista.",
   "upload_area.title": "Lataa raahaamalla ja pudottamalla tähän",
   "upload_button.label": "Lisää mediaa",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index c05cb40c12c..4c2bd39cacd 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Fil public local",
   "tabs_bar.notifications": "Notifications",
   "tabs_bar.search": "Chercher",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Votre brouillon sera perdu si vous quittez Mastodon.",
   "upload_area.title": "Glissez et déposez pour envoyer",
   "upload_button.label": "Joindre un média",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index 90447ad4c58..f2f0fc260c7 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificacións",
   "tabs_bar.search": "Buscar",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "O borrador perderase se sae de Mastodon.",
   "upload_area.title": "Arrastre e solte para subir",
   "upload_button.label": "Engadir medios",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index 5da3e9ebbc3..361beebcca3 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "ציר זמן מקומי",
   "tabs_bar.notifications": "התראות",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "הטיוטא תאבד אם תעזבו את מסטודון.",
   "upload_area.title": "ניתן להעלות על ידי Drag & drop",
   "upload_button.label": "הוספת מדיה",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index 6486284ec49..93dc1c17d28 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Lokalno",
   "tabs_bar.notifications": "Notifikacije",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Povuci i spusti kako bi uploadao",
   "upload_button.label": "Dodaj media",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 6c04eced63a..158c0fe414e 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Értesítések",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "A piszkozata el fog vesztődni ha elhagyja Mastodon-t.",
   "upload_area.title": "Húzza ide a feltöltéshez",
   "upload_button.label": "Média hozzáadása",
diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json
index f1f4df9688e..76b3a9c124e 100644
--- a/app/javascript/mastodon/locales/hy.json
+++ b/app/javascript/mastodon/locales/hy.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Տեղական",
   "tabs_bar.notifications": "Ծանուցումներ",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Քո սեւագիրը կկորի, եթե լքես Մաստոդոնը։",
   "upload_area.title": "Քաշիր ու նետիր՝ վերբեռնելու համար",
   "upload_button.label": "Ավելացնել մեդիա",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index e0fa959b071..00240530d07 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Notifikasi",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Naskah anda akan hilang jika anda keluar dari Mastodon.",
   "upload_area.title": "Seret & lepaskan untuk mengunggah",
   "upload_button.label": "Tambahkan media",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index 616232c3ec1..d20ee0f6220 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Lokala",
   "tabs_bar.notifications": "Savigi",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Tranar faligar por kargar",
   "upload_button.label": "Adjuntar kontenajo",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index 6bf9c73a60e..485fe0fad86 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Locale",
   "tabs_bar.notifications": "Notifiche",
   "tabs_bar.search": "Cerca",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "La bozza andrà persa se esci da Mastodon.",
   "upload_area.title": "Trascina per caricare",
   "upload_button.label": "Aggiungi file multimediale",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index 09dc624326d..614e526e357 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "ローカル",
   "tabs_bar.notifications": "通知",
   "tabs_bar.search": "検索",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Mastodonから離れると送信前の投稿は失われます。",
   "upload_area.title": "ドラッグ&ドロップでアップロード",
   "upload_button.label": "メディアを追加",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index 66657abcdb1..edcf5f8176b 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "로컬",
   "tabs_bar.notifications": "알림",
   "tabs_bar.search": "검색",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "지금 나가면 저장되지 않은 항목을 잃게 됩니다.",
   "upload_area.title": "드래그 & 드롭으로 업로드",
   "upload_button.label": "미디어 추가",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index 8ddf47f3f55..1ead2011d70 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Lokaal",
   "tabs_bar.notifications": "Meldingen",
   "tabs_bar.search": "Zoeken",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Je concept zal verloren gaan als je Mastodon verlaat.",
   "upload_area.title": "Hierin slepen om te uploaden",
   "upload_button.label": "Media toevoegen",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index ab755a3e8cf..d8de2ad4be9 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Varslinger",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Din kladd vil bli forkastet om du forlater Mastodon.",
   "upload_area.title": "Dra og slipp for å laste opp",
   "upload_button.label": "Legg til media",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index 44bb44e9774..e7bc148fa33 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Flux public local",
   "tabs_bar.notifications": "Notificacions",
   "tabs_bar.search": "Recèrcas",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Vòstre brolhon serà perdut se quitatz Mastodon.",
   "upload_area.title": "Lisatz e depausatz per mandar",
   "upload_button.label": "Ajustar un mèdia",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index 7bfb7643c6c..92c17556f2e 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Lokalne",
   "tabs_bar.notifications": "Powiadomienia",
   "tabs_bar.search": "Szukaj",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Utracisz tworzony wpis, jeżeli opuścisz Mastodona.",
   "upload_area.title": "Przeciągnij i upuść aby wysłać",
   "upload_button.label": "Dodaj zawartość multimedialną",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index 82bbd17a353..c69a90e6207 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificações",
   "tabs_bar.search": "Buscar",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Seu rascunho será perdido se você sair do Mastodon.",
   "upload_area.title": "Arraste e solte para enviar",
   "upload_button.label": "Adicionar mídia",
diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json
index 6463d001dd7..18d66be7f2a 100644
--- a/app/javascript/mastodon/locales/pt.json
+++ b/app/javascript/mastodon/locales/pt.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificações",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "O teu rascunho vai ser perdido se abandonares o Mastodon.",
   "upload_area.title": "Arraste e solte para enviar",
   "upload_button.label": "Adicionar media",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index 6c7926ffe66..577ec992125 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Локальная",
   "tabs_bar.notifications": "Уведомления",
   "tabs_bar.search": "Поиск",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Ваш черновик будет утерян, если вы покинете Mastodon.",
   "upload_area.title": "Перетащите сюда, чтобы загрузить",
   "upload_button.label": "Добавить медиаконтент",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index 6ac646bee17..b85ba13181d 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Lokálna",
   "tabs_bar.notifications": "Notifikácie",
   "tabs_bar.search": "Hľadaj",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Čo máte rozpísané sa stratí, ak opustíte Mastodon.",
   "upload_area.title": "Ťahaj a pusti pre nahratie",
   "upload_button.label": "Pridať médiá",
diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json
index edd5ab62b43..3cc7389ae30 100644
--- a/app/javascript/mastodon/locales/sl.json
+++ b/app/javascript/mastodon/locales/sl.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Lokalno",
   "tabs_bar.notifications": "Obvestila",
   "tabs_bar.search": "Poišči",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Vaš osnutek bo izgubljen, če zapustite Mastodona.",
   "upload_area.title": "Povlecite in spustite za pošiljanje",
   "upload_button.label": "Dodaj medij",
diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json
index 2a9d3737d60..e2b55c17944 100644
--- a/app/javascript/mastodon/locales/sr-Latn.json
+++ b/app/javascript/mastodon/locales/sr-Latn.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Lokalno",
   "tabs_bar.notifications": "Obaveštenja",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Ako napustite Mastodont, izgubićete napisani nacrt.",
   "upload_area.title": "Prevucite ovde da otpremite",
   "upload_button.label": "Dodaj multimediju",
diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json
index b53f1ecc986..09efb985cbc 100644
--- a/app/javascript/mastodon/locales/sr.json
+++ b/app/javascript/mastodon/locales/sr.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Локално",
   "tabs_bar.notifications": "Обавештења",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Ако напустите Мастодонт, изгубићете написани нацрт.",
   "upload_area.title": "Превуците овде да отпремите",
   "upload_button.label": "Додај мултимедију",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index dbe2c182eda..6c396e5250b 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Meddelanden",
   "tabs_bar.search": "Sök",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Ditt utkast kommer att förloras om du lämnar Mastodon.",
   "upload_area.title": "Dra & släpp för att ladda upp",
   "upload_button.label": "Lägg till media",
diff --git a/app/javascript/mastodon/locales/te.json b/app/javascript/mastodon/locales/te.json
index 52bd41935b0..ce0d4b9cc2a 100644
--- a/app/javascript/mastodon/locales/te.json
+++ b/app/javascript/mastodon/locales/te.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notifications",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Drag & drop to upload",
   "upload_button.label": "Add media",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index 32aa240804f..f0ba25ec378 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notifications",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Drag & drop to upload",
   "upload_button.label": "Add media",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index b73157e27d3..89f00fd3b8d 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Yerel",
   "tabs_bar.notifications": "Bildirimler",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Upload için sürükle bırak yapınız",
   "upload_button.label": "Görsel ekle",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index b301c6827c0..e81476541b1 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "Локальна",
   "tabs_bar.notifications": "Сповіщення",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Перетягніть сюди, щоб завантажити",
   "upload_button.label": "Додати медіаконтент",
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index 455f1f828b9..05194c90942 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "本站",
   "tabs_bar.notifications": "通知",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "如果你现在离开 Mastodon,你的草稿内容将会被丢弃。",
   "upload_area.title": "将文件拖放到此处开始上传",
   "upload_button.label": "上传媒体文件",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index 18be1779242..cad53c69ac9 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "本站",
   "tabs_bar.notifications": "通知",
   "tabs_bar.search": "搜尋",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "如果你現在離開 Mastodon,你的草稿內容將會被丟棄。",
   "upload_area.title": "將檔案拖放至此上載",
   "upload_button.label": "上載媒體檔案",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index 3222c035a52..91b44012f07 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -280,6 +280,8 @@
   "tabs_bar.local_timeline": "本地",
   "tabs_bar.notifications": "通知",
   "tabs_bar.search": "Search",
+  "timeline.media": "Media",
+  "timeline.posts": "Toots",
   "ui.beforeunload": "如果離開 Mastodon,你的草稿將會不見。",
   "upload_area.title": "拖放來上傳",
   "upload_button.label": "增加媒體",
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 90325be9c4f..0765b3ffc58 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -4737,6 +4737,8 @@ a.status-card {
   }
 }
 
+.community-timeline__section-headline,
+.public-timeline__section-headline,
 .account__section-headline {
   background: darken($ui-base-color, 4%);
   border-bottom: 1px solid lighten($ui-base-color, 8%);
diff --git a/app/services/batched_remove_status_service.rb b/app/services/batched_remove_status_service.rb
index cb65a225653..425a11d012d 100644
--- a/app/services/batched_remove_status_service.rb
+++ b/app/services/batched_remove_status_service.rb
@@ -81,6 +81,11 @@ class BatchedRemoveStatusService < BaseService
       redis.publish('timeline:public', payload)
       redis.publish('timeline:public:local', payload) if status.local?
 
+      if status.media_attachments.exists?
+        redis.publish('timeline:public:media', payload)
+        redis.publish('timeline:public:local:media', payload) if status.local?
+      end
+
       @tags[status.id].each do |hashtag|
         redis.publish("timeline:hashtag:#{hashtag}", payload)
         redis.publish("timeline:hashtag:#{hashtag}:local", payload) if status.local?
diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb
index cb82a79ed33..b4b9986f3b6 100644
--- a/app/services/fan_out_on_write_service.rb
+++ b/app/services/fan_out_on_write_service.rb
@@ -25,6 +25,7 @@ class FanOutOnWriteService < BaseService
     return if status.reply? && status.in_reply_to_account_id != status.account_id
 
     deliver_to_public(status)
+    deliver_to_media(status) if status.media_attachments.exists?
   end
 
   private
@@ -85,6 +86,13 @@ class FanOutOnWriteService < BaseService
     Redis.current.publish('timeline:public:local', @payload) if status.local?
   end
 
+  def deliver_to_media(status)
+    Rails.logger.debug "Delivering status #{status.id} to media timeline"
+
+    Redis.current.publish('timeline:public:media', @payload)
+    Redis.current.publish('timeline:public:local:media', @payload) if status.local?
+  end
+
   def deliver_to_direct_timelines(status)
     Rails.logger.debug "Delivering status #{status.id} to direct timelines"
 
diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb
index e164c03ab19..53f2be6f860 100644
--- a/app/services/remove_status_service.rb
+++ b/app/services/remove_status_service.rb
@@ -20,6 +20,7 @@ class RemoveStatusService < BaseService
     remove_reblogs
     remove_from_hashtags
     remove_from_public
+    remove_from_media if status.media_attachments.exists?
     remove_from_direct if status.direct_visibility?
 
     @status.destroy!
@@ -131,6 +132,13 @@ class RemoveStatusService < BaseService
     Redis.current.publish('timeline:public:local', @payload) if @status.local?
   end
 
+  def remove_from_media
+    return unless @status.public_visibility?
+
+    Redis.current.publish('timeline:public:media', @payload)
+    Redis.current.publish('timeline:public:local:media', @payload) if @status.local?
+  end
+
   def remove_from_direct
     @mentions.each do |mention|
       Redis.current.publish("timeline:direct:#{mention.account.id}", @payload) if mention.account.local?
diff --git a/streaming/index.js b/streaming/index.js
index 48bab8078e5..4eaf6686583 100644
--- a/streaming/index.js
+++ b/streaming/index.js
@@ -241,7 +241,9 @@ const startWorker = (workerId) => {
 
   const PUBLIC_STREAMS = [
     'public',
+    'public:media',
     'public:local',
+    'public:local:media',
     'hashtag',
     'hashtag:local',
   ];
@@ -459,11 +461,17 @@ const startWorker = (workerId) => {
   });
 
   app.get('/api/v1/streaming/public', (req, res) => {
-    streamFrom('timeline:public', req, streamToHttp(req, res), streamHttpEnd(req), true);
+    const onlyMedia = req.query.only_media === '1' || req.query.only_media === 'true';
+    const channel   = onlyMedia ? 'timeline:public:media' : 'timeline:public';
+
+    streamFrom(channel, req, streamToHttp(req, res), streamHttpEnd(req), true);
   });
 
   app.get('/api/v1/streaming/public/local', (req, res) => {
-    streamFrom('timeline:public:local', req, streamToHttp(req, res), streamHttpEnd(req), true);
+    const onlyMedia = req.query.only_media === '1' || req.query.only_media === 'true';
+    const channel   = onlyMedia ? 'timeline:public:local:media' : 'timeline:public:local';
+
+    streamFrom(channel, req, streamToHttp(req, res), streamHttpEnd(req), true);
   });
 
   app.get('/api/v1/streaming/direct', (req, res) => {
@@ -521,6 +529,12 @@ const startWorker = (workerId) => {
     case 'public:local':
       streamFrom('timeline:public:local', req, streamToWs(req, ws), streamWsEnd(req, ws), true);
       break;
+    case 'public:media':
+      streamFrom('timeline:public:media', req, streamToWs(req, ws), streamWsEnd(req, ws), true);
+      break;
+    case 'public:local:media':
+      streamFrom('timeline:public:local:media', req, streamToWs(req, ws), streamWsEnd(req, ws), true);
+      break;
     case 'direct':
       streamFrom(`timeline:direct:${req.accountId}`, req, streamToWs(req, ws), streamWsEnd(req, ws), true);
       break;

From ea969000a5ffba0a0196b88c87e6e49b12ab6bfb Mon Sep 17 00:00:00 2001
From: Sylvhem <Sylvhem@users.noreply.github.com>
Date: Mon, 21 May 2018 13:40:31 +0200
Subject: [PATCH 08/14] Add a light theme (#7027)

* Add a light theme to Mastodon

This add a second default theme to Mastodon. This new theme is supposed to be a light version of the dark original one.

* Update locales

Update the English and French locales.

* Change capitalization of hexadecimal triplets

Change capitalization of hexadecimal color codes to match Code Climate's recomandation.

* Add variable

Add a new variable to use instead of hardcoding a color. Change made to match Code Climate's recomandation.

* Use Mastodon branding palette

Replace the colors previously used by the ones found in Mastodon branding palette.
The resulting theme is lighter than the previous version.

* Make the overlay background white

Make the overly background used on on sensitive medias white instead of black

* Change the color used on the envelop icon

Change the color used on the envelop icon for a darker one. The same color is now used on both the envelop icon and the padlock icon.

* Add contrast

Add contrast to various places inside the main interface and the profile pages.

* Change the text color used in the compose form

Change the text color used in the compose form for a darker one.

* Make the code easier to read

Add some blank lines to make the code easier to parse for a human eye.

* Change columns' background default colors

Change columns' background default colors, making the composition column the darker one.

* Change the color of the log in button

Change the log in button's text color to make it more readable.

* Fix the color of the boost buttons on the landing page

The disabled boost buttons on the landing page are now of the same color that the other disabled buttons.

* Change the colors used in the dropdown menu

Make the dropdown menu light instead of dark.
---
 app/javascript/styles/mastodon-light.scss | 227 ++++++++++++++++++++++
 config/locales/en.yml                     |   1 +
 config/locales/fr.yml                     |   1 +
 config/themes.yml                         |   1 +
 4 files changed, 230 insertions(+)
 create mode 100644 app/javascript/styles/mastodon-light.scss

diff --git a/app/javascript/styles/mastodon-light.scss b/app/javascript/styles/mastodon-light.scss
new file mode 100644
index 00000000000..ce9452ff13e
--- /dev/null
+++ b/app/javascript/styles/mastodon-light.scss
@@ -0,0 +1,227 @@
+// Set variables
+$ui-base-color: #d9e1e8;
+$ui-base-lighter-color: darken($ui-base-color, 57%);
+$ui-highlight-color: #2b90d9;
+$ui-primary-color: darken($ui-highlight-color, 28%);
+$ui-secondary-color: #282c37;
+
+$primary-text-color: black;
+$base-overlay-background: $ui-base-color;
+
+$login-button-color: white;
+$account-background-color: white;
+
+// Import defaults
+@import 'application';
+
+// Change the color of the log in button
+.button {
+  &.button-alternative-2 {
+    color: $login-button-color;
+  }
+}
+
+// Change columns' default background colors
+.column {
+  > .scrollable {
+    background: lighten($ui-base-color, 13%);
+  }
+}
+
+.drawer__inner {
+  background: $ui-base-color;
+}
+
+.drawer__inner__mastodon {
+  background: $ui-base-color url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 234.80078 31.757813" width="234.80078" height="31.757812"><path d="M19.599609 0c-1.05 0-2.10039.375-2.90039 1.125L0 16.925781v14.832031h234.80078V17.025391l-16.5-15.900391c-1.6-1.5-4.20078-1.5-5.80078 0l-13.80078 13.099609c-1.6 1.5-4.19883 1.5-5.79883 0L179.09961 1.125c-1.6-1.5-4.19883-1.5-5.79883 0L159.5 14.224609c-1.6 1.5-4.20078 1.5-5.80078 0L139.90039 1.125c-1.6-1.5-4.20078-1.5-5.80078 0l-13.79883 13.099609c-1.6 1.5-4.20078 1.5-5.80078 0L100.69922 1.125c-1.600001-1.5-4.198829-1.5-5.798829 0l-13.59961 13.099609c-1.6 1.5-4.200781 1.5-5.800781 0L61.699219 1.125c-1.6-1.5-4.198828-1.5-5.798828 0L42.099609 14.224609c-1.6 1.5-4.198828 1.5-5.798828 0L22.5 1.125C21.7.375 20.649609 0 19.599609 0z" fill="#{hex-color(lighten($ui-base-color, 13%))}"/></svg>') no-repeat bottom / 100% auto;
+}
+
+// Change the default appearance of the content warning button
+.status__content,
+.reply-indicator__content {
+
+  .status__content__spoiler-link {
+
+    background: darken($ui-base-color, 30%);
+
+    &:hover {
+      background: darken($ui-base-color, 35%);
+      text-decoration: none;
+    }
+
+  }
+
+}
+
+// Change the default appearance of the action buttons
+.icon-button {
+
+  &:hover,
+  &:active,
+  &:focus {
+    color: darken($ui-base-color, 40%);
+    transition: color 200ms ease-out;
+  }
+
+  &.disabled {
+    color: darken($ui-base-color, 30%);
+  }
+
+}
+
+.status {
+  &.status-direct {
+    .icon-button.disabled {
+      color: darken($ui-base-color, 30%);
+    }
+  }
+}
+
+button.icon-button i.fa-retweet {
+  &:hover {
+    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color(darken($ui-base-color, 30%))}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($ui-highlight-color)}' stroke-width='0'/></svg>");
+  }
+}
+
+button.icon-button.disabled i.fa-retweet {
+  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color(darken($ui-base-color, 30%))}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($ui-highlight-color)}' stroke-width='0'/></svg>");
+}
+
+// Change the colors used in the dropdown menu
+.dropdown-menu {
+  background: $ui-base-color;
+}
+
+.dropdown-menu__arrow {
+
+  &.left {
+    border-left-color: $ui-base-color;
+  }
+
+  &.top {
+    border-top-color: $ui-base-color;
+  }
+
+  &.bottom {
+    border-bottom-color: $ui-base-color;
+  }
+
+  &.right {
+    border-right-color: $ui-base-color;
+  }
+
+}
+
+.dropdown-menu__item {
+  a {
+    background: $ui-base-color;
+    color: $ui-secondary-color;
+  }
+}
+
+// Change the default color of several parts of the compose form
+.compose-form {
+
+  .compose-form__warning {
+    color: lighten($ui-secondary-color, 65%);
+  }
+
+  strong {
+    color: lighten($ui-secondary-color, 65%);
+  }
+
+  .autosuggest-textarea__textarea,
+  .spoiler-input__input {
+
+    color: darken($ui-base-color, 80%);
+
+    &::placeholder {
+      color: darken($ui-base-color, 70%);
+    }
+
+  }
+
+  .compose-form__buttons-wrapper {
+    background: darken($ui-base-color, 10%);
+  }
+
+  .privacy-dropdown__option {
+    color: $ui-primary-color;
+  }
+
+  .privacy-dropdown__option__content {
+
+    strong {
+      color: $ui-primary-color;
+    }
+
+  }
+
+}
+
+// Change the default color used for the text in an empty column or on the error column
+.empty-column-indicator,
+.error-column {
+  color: darken($ui-base-color, 60%);
+}
+
+// Change the default colors used on some parts of the profile pages
+.activity-stream-tabs {
+
+  background: $account-background-color;
+
+  a {
+    &.active {
+      color: $ui-primary-color;
+      }
+  }
+
+}
+
+.activity-stream {
+
+  .entry {
+    background: $account-background-color;
+  }
+
+  .status.light {
+
+    .status__content {
+      color: $primary-text-color;
+    }
+
+    .display-name {
+      strong {
+        color: $primary-text-color;
+      }
+    }
+
+  }
+
+}
+
+.accounts-grid {
+  .account-grid-card {
+
+    .controls {
+      .icon-button {
+        color: $ui-secondary-color;
+      }
+    }
+
+    .name {
+      a {
+        color: $primary-text-color;
+      }
+    }
+
+    .username {
+      color: $ui-secondary-color;
+    }
+
+    .account__header__content {
+      color: $primary-text-color;
+    }
+
+  }
+}
diff --git a/config/locales/en.yml b/config/locales/en.yml
index b9429dbee5c..65b5b1e0b02 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -777,6 +777,7 @@ en:
   themes:
     contrast: High contrast
     default: Mastodon
+    mastodon-light: Mastodon (light)
   time:
     formats:
       default: "%b %d, %Y, %H:%M"
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 753fd6bd786..cd323b602da 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -700,6 +700,7 @@ fr:
   themes:
     contrast: Contraste élevé
     default: Mastodon
+    mastodon-light: Mastodon (clair)
   time:
     formats:
       default: "%d %b %Y, %H:%M"
diff --git a/config/themes.yml b/config/themes.yml
index f0bb1e6f725..9c21c9459f3 100644
--- a/config/themes.yml
+++ b/config/themes.yml
@@ -1,2 +1,3 @@
 default: styles/application.scss
 contrast: styles/contrast.scss
+mastodon-light: styles/mastodon-light.scss

From 41b2cfe5b899e1464c0b62f8e496841aa4380c56 Mon Sep 17 00:00:00 2001
From: SerCom_KC <szescxz@gmail.com>
Date: Mon, 21 May 2018 19:40:48 +0800
Subject: [PATCH 09/14] i18n: Update Simplified Chinese translations (#7565)

* i18n: (zh-CN) #7532

* i18n: (zh-CN) #6984

* i18n: (zh-CN) #7391, #7507

* i18n: (zh-CN) #6998

* i18n: (zh-CN) #7074

* i18n: (zh-CN) #7000, #7032, #7131 (#7032, #7040)

* i18n: (zh-CN) #7130, #7188

* i18n: (zh-CN) #6486

* i18n: (zh-CN) #6292

* i18n: (zh-CN) #7347

* i18n: (zh-CN) #6661

* i18n: (zh-CN) #6425

* i18n: (zh-CN) #6597

* i18n: (zh-CN) #6695

* i18n: (zh-CN) #6325

* i18n: (zh-CN) #6460, #7375

* i18n: (zh-CN) #6872

* i18n: (zh-CN) #6818

* i18n: (zh-CN) #7452

* i18n: (zh-CN) #7176

* i18n: (zh-CN) #6460

* i18n: (zh-CN) #7213

* i18n: (zh-CN) #7376

* i18n: (zh-CN) #6556

* i18n: (zh-CN) #6645

* i18n: (zh-CN) #6448

* i18n: (zh-CN) #5303

* i18n: (zh-CN) #7445

* i18n: (zh-CN) Normalization and improvements

* i18n: (zh-CN) #7391

* i18n: (zh-CN) #6627

* i18n: (zh-CN) #6956, #7546

* i18n: (zh-CN) #6636

* i18n: (zh-CN) #6610, #6875

* i18n: (zh-CN) #6887

* i18n: (zh-CN) #4514

* i18n: (zh-CN) #6628

* i18n: (zh-CN) #6771

* i18n: (zh-CN) #6772

* i18n: (zh-CN) #7178

* i18n: (zh-CN) #7521

* i18n: (zh-CN) #6570

* i18n: (zh-CN) #6593

* i18n: (zh-CN) #6423

* i18n: (zh-CN) #6157

* i18n: (zh-CN) #7089

* i18n: (zh-CN) #6733

* i18n: (zh-CN) #7072

* i18n: (zh-CN) #6520

* i18n: (zh-CN) Improvment

* i18n: (zh-CN) #6631
---
 app/javascript/mastodon/locales/zh-CN.json | 78 +++++++++---------
 config/locales/doorkeeper.zh-CN.yml        |  1 +
 config/locales/simple_form.zh-CN.yml       | 14 +++-
 config/locales/zh-CN.yml                   | 93 ++++++++++++++++++++--
 4 files changed, 140 insertions(+), 46 deletions(-)

diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index 05194c90942..b920bc19ead 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -1,11 +1,11 @@
 {
-  "account.badges.bot": "Bot",
+  "account.badges.bot": "机器人",
   "account.block": "屏蔽 @{name}",
   "account.block_domain": "隐藏来自 {domain} 的内容",
-  "account.blocked": "Blocked",
-  "account.direct": "Direct Message @{name}",
+  "account.blocked": "已屏蔽",
+  "account.direct": "发送私信给 @{name}",
   "account.disclaimer_full": "此处显示的信息可能不是全部内容。",
-  "account.domain_blocked": "Domain hidden",
+  "account.domain_blocked": "网站已屏蔽",
   "account.edit_profile": "修改个人资料",
   "account.follow": "关注",
   "account.followers": "关注者",
@@ -17,9 +17,9 @@
   "account.moved_to": "{name} 已经迁移到:",
   "account.mute": "隐藏 @{name}",
   "account.mute_notifications": "隐藏来自 @{name} 的通知",
-  "account.muted": "Muted",
+  "account.muted": "已隐藏",
   "account.posts": "嘟文",
-  "account.posts_with_replies": "Toots with replies",
+  "account.posts_with_replies": "嘟文和回复",
   "account.report": "举报 @{name}",
   "account.requested": "正在等待对方同意。点击以取消发送关注请求",
   "account.share": "分享 @{name} 的个人资料",
@@ -30,8 +30,8 @@
   "account.unmute": "不再隐藏 @{name}",
   "account.unmute_notifications": "不再隐藏来自 @{name} 的通知",
   "account.view_full_profile": "查看完整资料",
-  "alert.unexpected.message": "An unexpected error occurred.",
-  "alert.unexpected.title": "Oops!",
+  "alert.unexpected.message": "发生了意外错误。",
+  "alert.unexpected.title": "哎呀!",
   "boost_modal.combo": "下次按住 {combo} 即可跳过此提示",
   "bundle_column_error.body": "载入这个组件时发生了错误。",
   "bundle_column_error.retry": "重试",
@@ -41,8 +41,8 @@
   "bundle_modal_error.retry": "重试",
   "column.blocks": "屏蔽用户",
   "column.community": "本站时间轴",
-  "column.direct": "Direct messages",
-  "column.domain_blocks": "Hidden domains",
+  "column.direct": "私信",
+  "column.domain_blocks": "已屏蔽的网站",
   "column.favourites": "收藏过的嘟文",
   "column.follow_requests": "关注请求",
   "column.home": "主页",
@@ -60,18 +60,18 @@
   "column_header.unpin": "取消固定",
   "column_subheading.navigation": "导航",
   "column_subheading.settings": "设置",
-  "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.",
-  "compose_form.direct_message_warning_learn_more": "Learn more",
+  "compose_form.direct_message_warning": "这条嘟文仅对所有被提及的用户可见。",
+  "compose_form.direct_message_warning_learn_more": "了解详情",
   "compose_form.hashtag_warning": "这条嘟文被设置为“不公开”,因此它不会出现在任何话题标签的列表下。只有公开的嘟文才能通过话题标签进行搜索。",
   "compose_form.lock_disclaimer": "你的帐户没有{locked}。任何人都可以在关注你后立即查看仅关注者可见的嘟文。",
   "compose_form.lock_disclaimer.lock": "开启保护",
   "compose_form.placeholder": "在想啥?",
   "compose_form.publish": "嘟嘟",
   "compose_form.publish_loud": "{publish}!",
-  "compose_form.sensitive.marked": "Media is marked as sensitive",
-  "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
-  "compose_form.spoiler.marked": "Text is hidden behind warning",
-  "compose_form.spoiler.unmarked": "Text is not hidden",
+  "compose_form.sensitive.marked": "媒体已被标记为敏感内容",
+  "compose_form.sensitive.unmarked": "媒体未被标记为敏感内容",
+  "compose_form.spoiler.marked": "正文已被折叠在警告信息之后",
+  "compose_form.spoiler.unmarked": "正文未被折叠",
   "compose_form.spoiler_placeholder": "折叠部分的警告消息",
   "confirmation_modal.cancel": "取消",
   "confirmations.block.confirm": "屏蔽",
@@ -103,7 +103,7 @@
   "emoji_button.symbols": "符号",
   "emoji_button.travel": "旅行和地点",
   "empty_column.community": "本站时间轴暂时没有内容,快嘟几个来抢头香啊!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "你还没有使用过私信。当你发出或者收到私信时,它会在这里显示。",
   "empty_column.hashtag": "这个话题标签下暂时没有内容。",
   "empty_column.home": "你还没有关注任何用户。快看看{public},向其他用户搭讪吧。",
   "empty_column.home.public_timeline": "公共时间轴",
@@ -137,7 +137,7 @@
   "keyboard_shortcuts.mention": "提及嘟文作者",
   "keyboard_shortcuts.reply": "回复嘟文",
   "keyboard_shortcuts.search": "选择搜索框",
-  "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW",
+  "keyboard_shortcuts.toggle_hidden": "显示或隐藏被折叠的正文",
   "keyboard_shortcuts.toot": "发送新嘟文",
   "keyboard_shortcuts.unfocus": "取消输入",
   "keyboard_shortcuts.up": "在列表中让光标上移",
@@ -159,8 +159,8 @@
   "mute_modal.hide_notifications": "同时隐藏来自这个用户的通知",
   "navigation_bar.blocks": "被屏蔽的用户",
   "navigation_bar.community_timeline": "本站时间轴",
-  "navigation_bar.direct": "Direct messages",
-  "navigation_bar.domain_blocks": "Hidden domains",
+  "navigation_bar.direct": "私信",
+  "navigation_bar.domain_blocks": "已屏蔽的网站",
   "navigation_bar.edit_profile": "修改个人资料",
   "navigation_bar.favourites": "收藏的内容",
   "navigation_bar.follow_requests": "关注请求",
@@ -187,7 +187,7 @@
   "notifications.column_settings.reblog": "当有人转嘟了你的嘟文时:",
   "notifications.column_settings.show": "在通知栏显示",
   "notifications.column_settings.sound": "播放音效",
-  "notifications.group": "{count} notifications",
+  "notifications.group": "{count} 条通知",
   "onboarding.done": "出发!",
   "onboarding.next": "下一步",
   "onboarding.page_five.public_timelines": "“本站时间轴”显示的是由本站({domain})用户发布的所有公开嘟文。“跨站公共时间轴”显示的的是由本站用户关注对象所发布的所有公开嘟文。这些就是寻人好去处的公共时间轴啦。",
@@ -226,29 +226,29 @@
   "relative_time.minutes": "{number}分",
   "relative_time.seconds": "{number}秒",
   "reply_indicator.cancel": "取消",
-  "report.forward": "Forward to {target}",
-  "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
-  "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
+  "report.forward": "发送举报至 {target}",
+  "report.forward_hint": "这名用户来自另一个实例。是否要向那个实例发送一条匿名的举报?",
+  "report.hint": "举报将会发送给你所在实例的监察员。你可以在下面填写举报这个用户的理由:",
   "report.placeholder": "附言",
   "report.submit": "提交",
   "report.target": "举报 {target}",
   "search.placeholder": "搜索",
   "search_popout.search_format": "高级搜索格式",
-  "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
+  "search_popout.tips.full_text": "输入其他内容将会返回所有你撰写、收藏、转嘟过或提及到你的嘟文,同时也会在用户名、昵称和话题标签中进行搜索。",
   "search_popout.tips.hashtag": "话题标签",
   "search_popout.tips.status": "嘟文",
-  "search_popout.tips.text": "使用普通字符进行搜索将会返回昵称、用户名和话题标签",
+  "search_popout.tips.text": "输入其他内容将会返回昵称、用户名和话题标签",
   "search_popout.tips.user": "用户",
-  "search_results.accounts": "People",
-  "search_results.hashtags": "Hashtags",
-  "search_results.statuses": "Toots",
+  "search_results.accounts": "用户",
+  "search_results.hashtags": "话题标签",
+  "search_results.statuses": "嘟文",
   "search_results.total": "共 {count, number} 个结果",
   "standalone.public_title": "大家都在干啥?",
   "status.block": "屏蔽 @{name}",
-  "status.cancel_reblog_private": "Unboost",
+  "status.cancel_reblog_private": "取消转嘟",
   "status.cannot_reblog": "无法转嘟这条嘟文",
   "status.delete": "删除",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "发送私信给 @{name}",
   "status.embed": "嵌入",
   "status.favourite": "收藏",
   "status.load_more": "加载更多",
@@ -259,9 +259,9 @@
   "status.mute_conversation": "隐藏此对话",
   "status.open": "展开嘟文",
   "status.pin": "在个人资料页面置顶",
-  "status.pinned": "Pinned toot",
+  "status.pinned": "置顶嘟文",
   "status.reblog": "转嘟",
-  "status.reblog_private": "Boost to original audience",
+  "status.reblog_private": "转嘟给原有关注者",
   "status.reblogged_by": "{name} 转嘟了",
   "status.reply": "回复",
   "status.replyAll": "回复所有人",
@@ -270,23 +270,23 @@
   "status.sensitive_warning": "敏感内容",
   "status.share": "分享",
   "status.show_less": "隐藏内容",
-  "status.show_less_all": "Show less for all",
+  "status.show_less_all": "隐藏所有内容",
   "status.show_more": "显示内容",
-  "status.show_more_all": "Show more for all",
+  "status.show_more_all": "显示所有内容",
   "status.unmute_conversation": "不再隐藏此对话",
   "status.unpin": "在个人资料页面取消置顶",
   "tabs_bar.federated_timeline": "跨站",
   "tabs_bar.home": "主页",
   "tabs_bar.local_timeline": "本站",
   "tabs_bar.notifications": "通知",
-  "tabs_bar.search": "Search",
-  "timeline.media": "Media",
-  "timeline.posts": "Toots",
+  "tabs_bar.search": "搜索",
+  "timeline.media": "媒体",
+  "timeline.posts": "嘟文",
   "ui.beforeunload": "如果你现在离开 Mastodon,你的草稿内容将会被丢弃。",
   "upload_area.title": "将文件拖放到此处开始上传",
   "upload_button.label": "上传媒体文件",
   "upload_form.description": "为视觉障碍人士添加文字说明",
-  "upload_form.focus": "Crop",
+  "upload_form.focus": "剪裁",
   "upload_form.undo": "取消上传",
   "upload_progress.label": "上传中…",
   "video.close": "关闭视频",
diff --git a/config/locales/doorkeeper.zh-CN.yml b/config/locales/doorkeeper.zh-CN.yml
index 90c484369ca..3c7dd99be4f 100644
--- a/config/locales/doorkeeper.zh-CN.yml
+++ b/config/locales/doorkeeper.zh-CN.yml
@@ -115,5 +115,6 @@ zh-CN:
         title: 需要 OAuth 认证
     scopes:
       follow: 关注或屏蔽用户
+      push: 接收你的帐户的推送通知
       read: 读取你的帐户数据
       write: 为你发表嘟文
diff --git a/config/locales/simple_form.zh-CN.yml b/config/locales/simple_form.zh-CN.yml
index 8252757566b..b12538b3568 100644
--- a/config/locales/simple_form.zh-CN.yml
+++ b/config/locales/simple_form.zh-CN.yml
@@ -4,22 +4,30 @@ zh-CN:
     hints:
       defaults:
         avatar: 文件大小限制 2MB,只支持 PNG、GIF 或 JPG 格式。图片分辨率将会压缩至 400×400px
+        bot: 来自这个帐户的绝大多数操作都是自动进行的,并且可能无人监控
         digest: 仅在你长时间未登录,且收到了私信时发送
         display_name: 还能输入 <span class="name-counter">%{count}</span> 个字符
+        fields: 这将会在个人资料页上以表格的形式展示,最多 4 个项目
         header: 文件大小限制 2MB,只支持 PNG、GIF 或 JPG 格式。图片分辨率将会压缩至 700×335px
         locked: 你需要手动审核所有关注请求
         note: 还能输入 <span class="note-counter">%{count}</span> 个字符
+        setting_hide_network: 你关注的人和关注你的人将不会在你的个人资料页上展示
         setting_noindex: 此设置会影响到你的公开个人资料以及嘟文页面
         setting_theme: 此设置会影响到你从任意设备登录时 Mastodon 的显示样式
       imports:
         data: 请上传从其他 Mastodon 实例导出的 CSV 文件
       sessions:
-        otp: 输入你手机上生成的双重认证码,或者任意一个恢复代码。
+        otp: 输入你手机应用上生成的双重认证码,或者任意一个恢复代码:
       user:
         filtered_languages: 被勾选语言的嘟文将不会出现在你的公共时间轴上
     labels:
+      account:
+        fields:
+          name: 标签
+          value: 内容
       defaults:
         avatar: 头像
+        bot: 这是一个机器人帐户
         confirm_new_password: 确认新密码
         confirm_password: 确认密码
         current_password: 当前密码
@@ -27,6 +35,7 @@ zh-CN:
         display_name: 昵称
         email: 电子邮件地址
         expires_in: 失效时间
+        fields: 个人资料附加信息
         filtered_languages: 语言过滤
         header: 个人资料页横幅图片
         locale: 语言
@@ -41,6 +50,8 @@ zh-CN:
         setting_default_privacy: 嘟文默认可见范围
         setting_default_sensitive: 总是将我发送的媒体文件标记为敏感内容
         setting_delete_modal: 在删除嘟文前询问我
+        setting_display_sensitive_media: 总是显示标记为敏感的媒体文件
+        setting_hide_network: 隐藏你的社交网络
         setting_noindex: 禁止搜索引擎建立索引
         setting_reduce_motion: 降低过渡动画效果
         setting_system_font_ui: 使用系统默认字体
@@ -49,6 +60,7 @@ zh-CN:
         severity: 级别
         type: 导入数据类型
         username: 用户名
+        username_or_email: 用户名或电子邮件地址
       interactions:
         must_be_follower: 屏蔽来自未关注我的用户的通知
         must_be_following: 屏蔽来自我未关注的用户的通知
diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml
index 9f98a893d9e..f7fc1a7a92d 100644
--- a/config/locales/zh-CN.yml
+++ b/config/locales/zh-CN.yml
@@ -4,6 +4,7 @@ zh-CN:
     about_hashtag_html: 这里展示的是带有话题标签 <strong>#%{hashtag}</strong> 的公开嘟文。如果你想与他们互动,你需要在任意一个 Mastodon 实例或与其兼容的网站上拥有一个帐户。
     about_mastodon_html: Mastodon(长毛象)是一个建立在开放式网络协议和自由、开源软件之上的社交网络,有着类似于电子邮件的分布式设计。
     about_this: 关于本实例
+    administered_by: 本实例的管理员:
     closed_registrations: 这个实例目前没有开放注册。不过,你可以前往其他实例注册一个帐户,同样可以加入到这个网络中哦!
     contact: 联系方式
     contact_missing: 未设定
@@ -39,6 +40,7 @@ zh-CN:
     following: 正在关注
     media: 媒体
     moved_html: "%{name} 已经迁移到 %{new_profile_link}:"
+    network_hidden: 此信息不可用。
     nothing_here: 这里神马都没有!
     people_followed_by: "%{name} 关注的人"
     people_who_follow: 关注 %{name} 的人
@@ -48,6 +50,7 @@ zh-CN:
     reserved_username: 此用户名已被保留
     roles:
       admin: 管理员
+      bot: 机器人
       moderator: 监察员
     unfollow: 取消关注
   admin:
@@ -58,7 +61,15 @@ zh-CN:
       destroyed_msg: 管理备忘删除成功!
     accounts:
       are_you_sure: 你确定吗?
+      avatar: 头像
       by_domain: 域名
+      change_email:
+        changed_msg: 帐户电子邮件地址更改成功!
+        current_email: 当前的电子邮件地址
+        label: 更改电子邮件地址
+        new_email: 新的电子邮件地址
+        submit: 更改电子邮件地址
+        title: 为 %{username} 更改电子邮件地址
       confirm: 确认
       confirmed: 已确认
       confirming: 确认
@@ -70,7 +81,7 @@ zh-CN:
       domain: 域名
       edit: 编辑
       email: 电子邮件地址
-      email_status: 电子邮件状态
+      email_status: 电子邮件地址状态
       enable: 启用
       enabled: 已启用
       feed_url: 订阅 URL
@@ -108,10 +119,11 @@ zh-CN:
       public: 公开页面
       push_subscription_expires: PuSH 订阅过期时间
       redownload: 刷新头像
+      remove_avatar: 删除头像
       resend_confirmation:
         already_confirmed: 该用户已被确认
         send: 重发确认邮件
-        success: 确认电子邮件成功发送!
+        success: 确认邮件发送成功!
       reset: 重置
       reset_password: 重置密码
       resubscribe: 重新订阅
@@ -132,6 +144,7 @@ zh-CN:
       statuses: 嘟文
       subscribe: 订阅
       title: 用户
+      unconfirmed_email: 待验证的电子邮件地址
       undo_silenced: 解除隐藏
       undo_suspension: 解除封禁
       unsubscribe: 取消订阅
@@ -139,6 +152,8 @@ zh-CN:
       web: 站内页面
     action_logs:
       actions:
+        assigned_to_self_report: "%{name} 接管了举报 %{target}"
+        change_email_user: "%{name} 更改了用户 %{target} 的电子邮件地址"
         confirm_user: "%{name} 确认了用户 %{target} 的电子邮件地址"
         create_custom_emoji: "%{name} 添加了新的自定义表情 %{target}"
         create_domain_block: "%{name} 屏蔽了域名 %{target}"
@@ -154,10 +169,13 @@ zh-CN:
         enable_user: "%{name} 将用户 %{target} 设置为允许登录"
         memorialize_account: "%{name} 将 %{target} 设置为追悼帐户"
         promote_user: "%{name} 对用户 %{target} 进行了升任操作"
+        remove_avatar_user: "%{name} 删除了 %{target} 的头像"
+        reopen_report: "%{name} 重开了举报 %{target}"
         reset_password_user: "%{name} 重置了用户 %{target} 的密码"
         resolve_report: "%{name} 处理了举报 %{target}"
         silence_account: "%{name} 隐藏了用户 %{target}"
         suspend_account: "%{name} 封禁了用户 %{target}"
+        unassigned_report: "%{name} 放弃了举报 %{target} 的接管"
         unsilence_account: "%{name} 解除了用户 %{target} 的隐藏状态"
         unsuspend_account: "%{name} 解除了用户 %{target} 的封禁状态"
         update_custom_emoji: "%{name} 更新了自定义表情 %{target}"
@@ -241,24 +259,44 @@ zh-CN:
         expired: 已失效
         title: 筛选
       title: 邀请用户
+    report_notes:
+      created_msg: 举报记录建立成功!
+      destroyed_msg: 举报记录删除成功!
     reports:
+      account:
+        note: 条记录
+        report: 条举报
       action_taken_by: 操作执行者
       are_you_sure: 你确定吗?
+      assign_to_self: 接管
+      assigned: 已接管的监察员
       comment:
         none: 没有
+      created_at: 举报时间
       id: ID
       mark_as_resolved: 标记为“已处理”
+      mark_as_unresolved: 标记为“未处理”
+      notes:
+        create: 添加记录
+        create_and_resolve: 添加记录并标记为“已处理”
+        create_and_unresolve: 添加记录并重开
+        delete: 删除
+        placeholder: 描述已经执行的操作,或其他任何与此条举报相关的跟进情况
+      reopen: 重开举报
       report: '举报 #%{id}'
       report_contents: 内容
       reported_account: 举报用户
       reported_by: 举报人
       resolved: 已处理
+      resolved_msg: 举报处理成功!
       silence_account: 隐藏用户
       status: 状态
       suspend_account: 封禁用户
       target: 被举报人
       title: 举报
+      unassign: 取消接管
       unresolved: 未处理
+      updated_at: 更新时间
       view: 查看
     settings:
       activity_api_enabled:
@@ -270,6 +308,9 @@ zh-CN:
       contact_information:
         email: 用于联系的公开电子邮件地址
         username: 用于联系的公开用户名
+      hero:
+        desc_html: 用于在首页展示。推荐分辨率 600×100px 以上。未指定的情况下将默认使用本站缩略图
+        title: 主题图片
       peers_api_enabled:
         desc_html: 截至目前本实例在网络中已发现的域名
         title: 公开已知实例的列表
@@ -286,11 +327,14 @@ zh-CN:
         open:
           desc_html: 允许所有人建立帐户
           title: 开放注册
+      show_known_fediverse_at_about_page:
+        desc_html: 启用此选项将会在预览中显示来自已知实例的嘟文,否则只会显示本站时间轴的内容
+        title: 在时间轴预览中显示已知实例
       show_staff_badge:
         desc_html: 在个人资料页上显示管理人员标志
         title: 显示管理人员标志
       site_description:
-        desc_html: 用于首页展示以及 meta 标签中的网站简介。可以使用 HTML 标签,包括 <code>&lt;a&gt;</code> 和 <code>&lt;em&gt;</code>。
+        desc_html: 用于首页展示以及 meta 标签中的网站简介。可以使用 HTML 标签,包括 <code>&lt;a&gt;</code> 和 <code>&lt;em&gt;</code>
         title: 本站简介
       site_description_extended:
         desc_html: 可以填写行为守则、规定、指南或其他本站特有的内容。可以使用 HTML 标签
@@ -310,8 +354,8 @@ zh-CN:
       back_to_account: 返回帐户信息页
       batch:
         delete: 删除
-        nsfw_off: 取消 NSFW 标记
-        nsfw_on: 添加 NSFW 标记
+        nsfw_off: 标记为非敏感内容
+        nsfw_on: 标记为敏感内容
       failed_to_execute: 执行失败
       media:
         title: 媒体文件
@@ -328,7 +372,8 @@ zh-CN:
     title: 管理
   admin_mailer:
     new_report:
-      body: "%{reporter} 举报了用户 %{target}。"
+      body: "%{reporter} 举报了用户 %{target}"
+      body_remote: 来自 %{domain} 的用户举报了用户 %{target}
       subject: 来自 %{instance} 的用户举报(#%{id})
   application_mailer:
     notification_preferences: 更改电子邮件首选项
@@ -347,6 +392,8 @@ zh-CN:
     your_token: 你的访问令牌
   auth:
     agreement_html: 注册即表示你同意遵守<a href="%{rules_path}">本实例的相关规定</a>和<a href="%{terms_path}">我们的使用条款</a>。
+    change_password: 密码
+    confirm_email: 确认电子邮件地址
     delete_account: 删除帐户
     delete_account_html: 如果你想删除你的帐户,请<a href="%{path}">点击这里继续</a>。你需要确认你的操作。
     didnt_get_confirmation: 没有收到确认邮件?
@@ -356,12 +403,16 @@ zh-CN:
     logout: 登出
     migrate_account: 迁移到另一个帐户
     migrate_account_html: 如果你希望引导他人关注另一个帐户,请<a href="%{path}">点击这里进行设置</a>。
+    or: 或者
+    or_log_in_with: 或通过其他方式登录
     register: 注册
+    register_elsewhere: 前往其他实例注册
     resend_confirmation: 重新发送确认邮件
     reset_password: 重置密码
     security: 帐户安全
     set_new_password: 设置新密码
   authorize_follow:
+    already_following: 你已经在关注此用户了
     error: 对不起,寻找这个跨站用户时出错
     follow: 关注
     follow_request: 关注请求已发送给:
@@ -406,6 +457,13 @@ zh-CN:
       title: 这个页面有问题
     noscript_html: 使用 Mastodon 网页版应用需要启用 JavaScript。你也可以选择适用于你的平台的 <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md">Mastodon 应用</a>。
   exports:
+    archive_takeout:
+      date: 日期
+      download: 下载你的存档
+      hint_html: 你可以请求一份包含你的<strong>嘟文和已上传的媒体文件</strong>的存档。导出的数据为 ActivityPub 格式,因而可以通过相应的软件读取。每次可以请求存档的间隔至少为 7 天。
+      in_progress: 正在准备你的存档……
+      request: 请求你的存档
+      size: 大小
     blocks: 屏蔽的用户
     csv: CSV
     follows: 关注的用户
@@ -445,6 +503,7 @@ zh-CN:
       '21600': 6 小时后
       '3600': 1 小时后
       '43200': 12 小时后
+      '604800': 1 周后
       '86400': 1 天后
     expires_in_prompt: 永不过期
     generate: 生成邀请链接
@@ -528,6 +587,10 @@ zh-CN:
     missing_resource: 无法确定你的帐户的跳转 URL
     proceed: 确认关注
     prompt: 你正准备关注:
+  remote_unfollow:
+    error: 错误
+    title: 标题
+    unfollowed: 已取消关注
   sessions:
     activity: 最后一次活跃的时间
     browser: 浏览器
@@ -583,6 +646,15 @@ zh-CN:
     two_factor_authentication: 双重认证
     your_apps: 你的应用
   statuses:
+    attached:
+      description: 附加媒体:%{attached}
+      image: "%{count} 张图片"
+      video: "%{count} 个视频"
+    boosted_from_html: 转嘟自 %{acct_link}
+    content_warning: 内容警告:%{warning}
+    disallowed_hashtags:
+      one: 包含了一个禁止的话题标签:%{tags}
+      other: 包含了这些禁止的话题标签:%{tags}
     open_in_web: 在站内打开
     over_character_limit: 超过了 %{max} 字的限制
     pin_errors:
@@ -606,6 +678,9 @@ zh-CN:
     sensitive_content: 敏感内容
   terms:
     title: "%{instance} 使用条款和隐私权政策"
+  themes:
+    contrast: 高对比度
+    default: Mastodon
   time:
     formats:
       default: "%Y年%-m月%d日 %H:%M"
@@ -626,6 +701,10 @@ zh-CN:
     setup: 设置
     wrong_code: 输入的认证码无效!请核对一下你的设备显示的时间,如果正确,你可能需要联系一下实例的管理员,让他们校准服务器的时间。
   user_mailer:
+    backup_ready:
+      explanation: 你请求了一份 Mastodon 帐户的完整备份。现在你可以下载了!
+      subject: 你的存档已经准备完毕
+      title: 存档导出
     welcome:
       edit_profile_action: 设置个人资料
       edit_profile_step: 你可以自定义你的个人资料,包括上传头像、横幅图片、更改昵称等等。如果你想在新的关注者关注你之前对他们进行审核,你也可以选择为你的帐户开启保护。
@@ -647,4 +726,6 @@ zh-CN:
   users:
     invalid_email: 输入的电子邮件地址无效
     invalid_otp_token: 输入的双重认证代码无效
+    otp_lost_help_html: 如果你不慎丢失了所有的代码,请联系 %{email} 寻求帮助
+    seamless_external_login: 因为你是通过外部服务登录的,所以密码和电子邮件地址设置都不可用。
     signed_in_as: 当前登录的帐户:

From e583f110c3ce737c292de1f47f39dd09450195dc Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Mon, 21 May 2018 16:00:56 +0200
Subject: [PATCH 10/14] Unescape HTML entities in rich web push notifications
 in web UI (#7569)

---
 .../mastodon/service_worker/web_push_notifications.js          | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/app/javascript/mastodon/service_worker/web_push_notifications.js b/app/javascript/mastodon/service_worker/web_push_notifications.js
index d104638228c..f922f7dd09d 100644
--- a/app/javascript/mastodon/service_worker/web_push_notifications.js
+++ b/app/javascript/mastodon/service_worker/web_push_notifications.js
@@ -1,5 +1,6 @@
 import IntlMessageFormat from 'intl-messageformat';
 import locales from './web_push_locales';
+import { unescape } from 'lodash';
 
 const MAX_NOTIFICATIONS = 5;
 const GROUP_TAG = 'tag';
@@ -60,7 +61,7 @@ const formatMessage = (messageId, locale, values = {}) =>
   (new IntlMessageFormat(locales[locale][messageId], locale)).format(values);
 
 const htmlToPlainText = html =>
-  html.replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n').replace(/<[^>]*>/g, '');
+  unescape(html.replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n').replace(/<[^>]*>/g, ''));
 
 const handlePush = (event) => {
   const { access_token, notification_id, preferred_locale, title, body, icon } = event.data.json();

From 32d437238191c94534c2010e640c2b5711fa3d0d Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Mon, 21 May 2018 16:01:16 +0200
Subject: [PATCH 11/14] Use #any? instead of #exists? when checking media
 attachments (#7570)

If media_attachments are not loaded, SQL query is the same, but
the #exists? method performs SQL query even if preloaded
---
 app/services/batched_remove_status_service.rb | 2 +-
 app/services/fan_out_on_write_service.rb      | 2 +-
 app/services/remove_status_service.rb         | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/app/services/batched_remove_status_service.rb b/app/services/batched_remove_status_service.rb
index 425a11d012d..ace51a1fcc7 100644
--- a/app/services/batched_remove_status_service.rb
+++ b/app/services/batched_remove_status_service.rb
@@ -81,7 +81,7 @@ class BatchedRemoveStatusService < BaseService
       redis.publish('timeline:public', payload)
       redis.publish('timeline:public:local', payload) if status.local?
 
-      if status.media_attachments.exists?
+      if status.media_attachments.any?
         redis.publish('timeline:public:media', payload)
         redis.publish('timeline:public:local:media', payload) if status.local?
       end
diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb
index b4b9986f3b6..8b363022991 100644
--- a/app/services/fan_out_on_write_service.rb
+++ b/app/services/fan_out_on_write_service.rb
@@ -25,7 +25,7 @@ class FanOutOnWriteService < BaseService
     return if status.reply? && status.in_reply_to_account_id != status.account_id
 
     deliver_to_public(status)
-    deliver_to_media(status) if status.media_attachments.exists?
+    deliver_to_media(status) if status.media_attachments.any?
   end
 
   private
diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb
index 53f2be6f860..8c3e184442e 100644
--- a/app/services/remove_status_service.rb
+++ b/app/services/remove_status_service.rb
@@ -20,7 +20,7 @@ class RemoveStatusService < BaseService
     remove_reblogs
     remove_from_hashtags
     remove_from_public
-    remove_from_media if status.media_attachments.exists?
+    remove_from_media if status.media_attachments.any?
     remove_from_direct if status.direct_visibility?
 
     @status.destroy!

From 22e067bf5c30a5aa7aec4b3c17dc112cacbe7873 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Mon, 21 May 2018 16:04:01 +0200
Subject: [PATCH 12/14] Use real container width in MediaGallery srcSet (#7571)

Fix #7568
---
 app/javascript/mastodon/components/media_gallery.js | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js
index 13e1fcc524f..7c4444e0e87 100644
--- a/app/javascript/mastodon/components/media_gallery.js
+++ b/app/javascript/mastodon/components/media_gallery.js
@@ -20,6 +20,7 @@ class Item extends React.PureComponent {
     index: PropTypes.number.isRequired,
     size: PropTypes.number.isRequired,
     onClick: PropTypes.func.isRequired,
+    displayWidth: PropTypes.number,
   };
 
   static defaultProps = {
@@ -58,7 +59,7 @@ class Item extends React.PureComponent {
   }
 
   render () {
-    const { attachment, index, size, standalone } = this.props;
+    const { attachment, index, size, standalone, displayWidth } = this.props;
 
     let width  = 50;
     let height = 100;
@@ -121,7 +122,7 @@ class Item extends React.PureComponent {
       const hasSize = typeof originalWidth === 'number' && typeof previewWidth === 'number';
 
       const srcSet = hasSize ? `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w` : null;
-      const sizes  = hasSize ? `(min-width: 1025px) ${320 * (width / 100)}px, ${width}vw` : null;
+      const sizes  = hasSize ? `${displayWidth * (width / 100)}px` : null;
 
       const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0;
       const focusY = attachment.getIn(['meta', 'focus', 'y']) || 0;
@@ -263,9 +264,9 @@ export default class MediaGallery extends React.PureComponent {
       const size = media.take(4).size;
 
       if (this.isStandaloneEligible()) {
-        children = <Item standalone onClick={this.handleClick} attachment={media.get(0)} />;
+        children = <Item standalone onClick={this.handleClick} attachment={media.get(0)} displayWidth={width} />;
       } else {
-        children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} />);
+        children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} displayWidth={width} />);
       }
     }
 

From 292c987522dbb87702b2b4df2a75fb4bd1ea0828 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Mon, 21 May 2018 16:05:31 +0200
Subject: [PATCH 13/14] Bump version to 2.4.0rc5

---
 lib/mastodon/version.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb
index a08a7bf1e62..7f7b5505a91 100644
--- a/lib/mastodon/version.rb
+++ b/lib/mastodon/version.rb
@@ -21,7 +21,7 @@ module Mastodon
     end
 
     def flags
-      'rc4'
+      'rc5'
     end
 
     def to_a

From 46061dc041b0a2a4a3907976cc3432abdb1d67ec Mon Sep 17 00:00:00 2001
From: Yamagishi Kazutoshi <ykzts@desire.sh>
Date: Mon, 21 May 2018 23:26:00 +0900
Subject: [PATCH 14/14] Add exact attribute to WrappedRoute for community
 timeline (#7572)

* Add extra attribute to WrappedRoute for community timeline

* fix
---
 app/javascript/mastodon/features/ui/index.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js
index a64d623c214..adb856b9275 100644
--- a/app/javascript/mastodon/features/ui/index.js
+++ b/app/javascript/mastodon/features/ui/index.js
@@ -141,8 +141,8 @@ class SwitchingColumnsArea extends React.PureComponent {
           <WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
           <WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} />
           <WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} />
-          <WrappedRoute path='/timelines/public/media' extract component={PublicTimeline} content={children} componentParams={{ onlyMedia: true }} />
-          <WrappedRoute path='/timelines/public/local' component={CommunityTimeline} content={children} />
+          <WrappedRoute path='/timelines/public/media' component={PublicTimeline} content={children} componentParams={{ onlyMedia: true }} />
+          <WrappedRoute path='/timelines/public/local' exact component={CommunityTimeline} content={children} />
           <WrappedRoute path='/timelines/public/local/media' component={CommunityTimeline} content={children} componentParams={{ onlyMedia: true }} />
           <WrappedRoute path='/timelines/direct' component={DirectTimeline} content={children} />
           <WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} />