about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--app/javascript/mastodon/actions/lists.js28
-rw-r--r--app/javascript/mastodon/actions/streaming.js1
-rw-r--r--app/javascript/mastodon/actions/timelines.js2
-rw-r--r--app/javascript/mastodon/features/compose/components/navigation_bar.js2
-rw-r--r--app/javascript/mastodon/features/list_timeline/index.js106
-rw-r--r--app/javascript/mastodon/features/ui/components/columns_area.js3
-rw-r--r--app/javascript/mastodon/features/ui/index.js2
-rw-r--r--app/javascript/mastodon/features/ui/util/async-components.js4
-rw-r--r--app/javascript/mastodon/locales/ar.json6
-rw-r--r--app/javascript/mastodon/locales/bg.json6
-rw-r--r--app/javascript/mastodon/locales/ca.json1
-rw-r--r--app/javascript/mastodon/locales/de.json6
-rw-r--r--app/javascript/mastodon/locales/defaultMessages.json9
-rw-r--r--app/javascript/mastodon/locales/en.json6
-rw-r--r--app/javascript/mastodon/locales/eo.json6
-rw-r--r--app/javascript/mastodon/locales/es.json6
-rw-r--r--app/javascript/mastodon/locales/fa.json6
-rw-r--r--app/javascript/mastodon/locales/fi.json6
-rw-r--r--app/javascript/mastodon/locales/fr.json6
-rw-r--r--app/javascript/mastodon/locales/he.json6
-rw-r--r--app/javascript/mastodon/locales/hr.json6
-rw-r--r--app/javascript/mastodon/locales/hu.json6
-rw-r--r--app/javascript/mastodon/locales/id.json6
-rw-r--r--app/javascript/mastodon/locales/io.json6
-rw-r--r--app/javascript/mastodon/locales/it.json6
-rw-r--r--app/javascript/mastodon/locales/ja.json6
-rw-r--r--app/javascript/mastodon/locales/ko.json6
-rw-r--r--app/javascript/mastodon/locales/nl.json6
-rw-r--r--app/javascript/mastodon/locales/no.json6
-rw-r--r--app/javascript/mastodon/locales/oc.json6
-rw-r--r--app/javascript/mastodon/locales/pl.json6
-rw-r--r--app/javascript/mastodon/locales/pt-BR.json6
-rw-r--r--app/javascript/mastodon/locales/pt.json6
-rw-r--r--app/javascript/mastodon/locales/ru.json6
-rw-r--r--app/javascript/mastodon/locales/sv.json14
-rw-r--r--app/javascript/mastodon/locales/th.json6
-rw-r--r--app/javascript/mastodon/locales/tr.json6
-rw-r--r--app/javascript/mastodon/locales/uk.json6
-rw-r--r--app/javascript/mastodon/locales/whitelist_sv.json2
-rw-r--r--app/javascript/mastodon/locales/zh-CN.json4
-rw-r--r--app/javascript/mastodon/locales/zh-HK.json6
-rw-r--r--app/javascript/mastodon/locales/zh-TW.json6
-rw-r--r--app/javascript/mastodon/reducers/index.js2
-rw-r--r--app/javascript/mastodon/reducers/lists.js15
-rw-r--r--app/javascript/mastodon/reducers/timelines.js5
-rw-r--r--app/models/status.rb3
-rw-r--r--app/serializers/rest/list_serializer.rb4
-rw-r--r--config/locales/ja.yml38
-rw-r--r--config/navigation.rb2
-rw-r--r--db/migrate/20171122120436_add_index_account_and_reblog_of_id_to_statuses.rb6
-rw-r--r--db/schema.rb3
-rw-r--r--spec/models/concerns/streamable_spec.rb63
-rw-r--r--spec/presenters/account_relationships_presenter_spec.rb82
53 files changed, 559 insertions, 16 deletions
diff --git a/app/javascript/mastodon/actions/lists.js b/app/javascript/mastodon/actions/lists.js
new file mode 100644
index 000000000..332e42166
--- /dev/null
+++ b/app/javascript/mastodon/actions/lists.js
@@ -0,0 +1,28 @@
+import api from '../api';
+
+export const LIST_FETCH_REQUEST = 'LIST_FETCH_REQUEST';
+export const LIST_FETCH_SUCCESS = 'LIST_FETCH_SUCCESS';
+export const LIST_FETCH_FAIL    = 'LIST_FETCH_FAIL';
+
+export const fetchList = id => (dispatch, getState) => {
+  dispatch(fetchListRequest(id));
+
+  api(getState).get(`/api/v1/lists/${id}`)
+    .then(({ data }) => dispatch(fetchListSuccess(data)))
+    .catch(err => dispatch(fetchListFail(err)));
+};
+
+export const fetchListRequest = id => ({
+  type: LIST_FETCH_REQUEST,
+  id,
+});
+
+export const fetchListSuccess = list => ({
+  type: LIST_FETCH_SUCCESS,
+  list,
+});
+
+export const fetchListFail = error => ({
+  type: LIST_FETCH_FAIL,
+  error,
+});
diff --git a/app/javascript/mastodon/actions/streaming.js b/app/javascript/mastodon/actions/streaming.js
index dcce048ca..c22152edd 100644
--- a/app/javascript/mastodon/actions/streaming.js
+++ b/app/javascript/mastodon/actions/streaming.js
@@ -51,3 +51,4 @@ export const connectCommunityStream = () => connectTimelineStream('community', '
 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 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 09abe2702..f8843d1d9 100644
--- a/app/javascript/mastodon/actions/timelines.js
+++ b/app/javascript/mastodon/actions/timelines.js
@@ -118,6 +118,7 @@ export const refreshCommunityTimeline    = () => refreshTimeline('community', '/
 export const refreshAccountTimeline      = accountId => refreshTimeline(`account:${accountId}`, `/api/v1/accounts/${accountId}/statuses`);
 export const refreshAccountMediaTimeline = accountId => refreshTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { only_media: true });
 export const refreshHashtagTimeline      = hashtag => refreshTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`);
+export const refreshListTimeline         = id => refreshTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`);
 
 export function refreshTimelineFail(timeline, error, skipLoading) {
   return {
@@ -158,6 +159,7 @@ export const expandCommunityTimeline    = () => expandTimeline('community', '/ap
 export const expandAccountTimeline      = accountId => expandTimeline(`account:${accountId}`, `/api/v1/accounts/${accountId}/statuses`);
 export const expandAccountMediaTimeline = accountId => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { only_media: true });
 export const expandHashtagTimeline      = hashtag => expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`);
+export const expandListTimeline         = id => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`);
 
 export function expandTimelineRequest(timeline) {
   return {
diff --git a/app/javascript/mastodon/features/compose/components/navigation_bar.js b/app/javascript/mastodon/features/compose/components/navigation_bar.js
index 7f346854c..3014c4033 100644
--- a/app/javascript/mastodon/features/compose/components/navigation_bar.js
+++ b/app/javascript/mastodon/features/compose/components/navigation_bar.js
@@ -11,7 +11,7 @@ export default class NavigationBar extends ImmutablePureComponent {
 
   static propTypes = {
     account: ImmutablePropTypes.map.isRequired,
-    onClose: PropTypes.func.isRequired,
+    onClose: PropTypes.func,
   };
 
   render () {
diff --git a/app/javascript/mastodon/features/list_timeline/index.js b/app/javascript/mastodon/features/list_timeline/index.js
new file mode 100644
index 000000000..71f6e36a8
--- /dev/null
+++ b/app/javascript/mastodon/features/list_timeline/index.js
@@ -0,0 +1,106 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import StatusListContainer from '../ui/containers/status_list_container';
+import Column from '../../components/column';
+import ColumnHeader from '../../components/column_header';
+import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
+import { FormattedMessage } from 'react-intl';
+import { connectListStream } from '../../actions/streaming';
+import { refreshListTimeline, expandListTimeline } from '../../actions/timelines';
+import { fetchList } from '../../actions/lists';
+
+const mapStateToProps = (state, props) => ({
+  list: state.getIn(['lists', props.params.id]),
+  hasUnread: state.getIn(['timelines', `list:${props.params.id}`, 'unread']) > 0,
+});
+
+@connect(mapStateToProps)
+export default class ListTimeline extends React.PureComponent {
+
+  static propTypes = {
+    params: PropTypes.object.isRequired,
+    dispatch: PropTypes.func.isRequired,
+    columnId: PropTypes.string,
+    hasUnread: PropTypes.bool,
+    multiColumn: PropTypes.bool,
+    list: ImmutablePropTypes.map,
+  };
+
+  handlePin = () => {
+    const { columnId, dispatch } = this.props;
+
+    if (columnId) {
+      dispatch(removeColumn(columnId));
+    } else {
+      dispatch(addColumn('LIST', { id: this.props.params.id }));
+    }
+  }
+
+  handleMove = (dir) => {
+    const { columnId, dispatch } = this.props;
+    dispatch(moveColumn(columnId, dir));
+  }
+
+  handleHeaderClick = () => {
+    this.column.scrollTop();
+  }
+
+  componentDidMount () {
+    const { dispatch } = this.props;
+    const { id } = this.props.params;
+
+    dispatch(fetchList(id));
+    dispatch(refreshListTimeline(id));
+
+    this.disconnect = dispatch(connectListStream(id));
+  }
+
+  componentWillUnmount () {
+    if (this.disconnect) {
+      this.disconnect();
+      this.disconnect = null;
+    }
+  }
+
+  setRef = c => {
+    this.column = c;
+  }
+
+  handleLoadMore = () => {
+    const { id } = this.props.params;
+    this.props.dispatch(expandListTimeline(id));
+  }
+
+  render () {
+    const { hasUnread, columnId, multiColumn, list } = this.props;
+    const { id } = this.props.params;
+    const pinned = !!columnId;
+    const title  = list ? list.get('title') : id;
+
+    return (
+      <Column ref={this.setRef}>
+        <ColumnHeader
+          icon='bars'
+          active={hasUnread}
+          title={title}
+          onPin={this.handlePin}
+          onMove={this.handleMove}
+          onClick={this.handleHeaderClick}
+          pinned={pinned}
+          multiColumn={multiColumn}
+        />
+
+        <StatusListContainer
+          trackScroll={!pinned}
+          scrollKey={`list_timeline-${columnId}`}
+          timelineId={`list:${id}`}
+          loadMore={this.handleLoadMore}
+          emptyMessage={<FormattedMessage id='empty_column.list' defaultMessage='There is nothing in this list yet.' />}
+        />
+      </Column>
+    );
+  }
+
+}
diff --git a/app/javascript/mastodon/features/ui/components/columns_area.js b/app/javascript/mastodon/features/ui/components/columns_area.js
index 5610095b9..93ed9e605 100644
--- a/app/javascript/mastodon/features/ui/components/columns_area.js
+++ b/app/javascript/mastodon/features/ui/components/columns_area.js
@@ -11,7 +11,7 @@ import BundleContainer from '../containers/bundle_container';
 import ColumnLoading from './column_loading';
 import DrawerLoading from './drawer_loading';
 import BundleColumnError from './bundle_column_error';
-import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline, FavouritedStatuses } from '../../ui/util/async-components';
+import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline, FavouritedStatuses, ListTimeline } from '../../ui/util/async-components';
 
 import detectPassiveEvents from 'detect-passive-events';
 import { scrollRight } from '../../../scroll';
@@ -24,6 +24,7 @@ const componentMap = {
   'COMMUNITY': CommunityTimeline,
   'HASHTAG': HashtagTimeline,
   'FAVOURITES': FavouritedStatuses,
+  'LIST': ListTimeline,
 };
 
 @component => injectIntl(component, { withRef: true })
diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js
index f28b37099..57289f519 100644
--- a/app/javascript/mastodon/features/ui/index.js
+++ b/app/javascript/mastodon/features/ui/index.js
@@ -33,6 +33,7 @@ import {
   FollowRequests,
   GenericNotFound,
   FavouritedStatuses,
+  ListTimeline,
   Blocks,
   Mutes,
   PinnedStatuses,
@@ -372,6 +373,7 @@ export default class UI extends React.Component {
               <WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} />
               <WrappedRoute path='/timelines/public/local' component={CommunityTimeline} content={children} />
               <WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} />
+              <WrappedRoute path='/timelines/list/:id' component={ListTimeline} content={children} />
 
               <WrappedRoute path='/notifications' component={Notifications} content={children} />
               <WrappedRoute path='/favourites' component={FavouritedStatuses} content={children} />
diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js
index 39663d5ca..ec1630ed6 100644
--- a/app/javascript/mastodon/features/ui/util/async-components.js
+++ b/app/javascript/mastodon/features/ui/util/async-components.js
@@ -26,6 +26,10 @@ export function HashtagTimeline () {
   return import(/* webpackChunkName: "features/hashtag_timeline" */'../../hashtag_timeline');
 }
 
+export function ListTimeline () {
+  return import(/* webpackChunkName: "features/list_timeline" */'../../list_timeline');
+}
+
 export function Status () {
   return import(/* webpackChunkName: "features/status" */'../../status');
 }
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index 7cc8ea237..6573b4a7c 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -9,7 +9,9 @@
   "account.follows_you": "يتابعك",
   "account.media": "وسائط",
   "account.mention": "أُذكُر @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "أكتم @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "المشاركات",
   "account.report": "أبلغ عن @{name}",
   "account.requested": "في انتظار الموافقة",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "فك حظر {domain}",
   "account.unfollow": "إلغاء المتابعة",
   "account.unmute": "إلغاء الكتم عن @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "عرض الملف الشخصي كاملا",
   "boost_modal.combo": "يمكنك ضغط {combo} لتخطّي هذه في المرّة القادمة",
   "bundle_column_error.body": "لقد وقع هناك خطأ أثناء عملية تحميل هذا العنصر.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.",
   "empty_column.home": "إنك لا تتبع بعد أي شخص إلى حد الآن. زر {public} أو استخدام حقل البحث لكي تبدأ على التعرف على مستخدمين آخرين.",
   "empty_column.home.public_timeline": "الخيط العام",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.",
   "empty_column.public": "لا يوجد شيء هنا ! قم بتحرير شيء ما بشكل عام، أو اتبع مستخدمين آخرين في الخوادم المثيلة الأخرى لملء خيط المحادثات العام.",
   "follow_request.authorize": "ترخيص",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "تحميل ...",
   "media_gallery.toggle_visible": "عرض / إخفاء",
   "missing_indicator.label": "تعذر العثور عليه",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "الحسابات المحجوبة",
   "navigation_bar.community_timeline": "الخيط العام المحلي",
   "navigation_bar.edit_profile": "تعديل الملف الشخصي",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "الرئيسية",
   "tabs_bar.local_timeline": "المحلي",
   "tabs_bar.notifications": "الإخطارات",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "إسحب ثم أفلت للرفع",
   "upload_button.label": "إضافة وسائط",
   "upload_form.description": "وصف للمعاقين بصريا",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index da2372cff..cb02a12a0 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Твой последовател",
   "account.media": "Media",
   "account.mention": "Споменаване",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Mute @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Публикации",
   "account.report": "Report @{name}",
   "account.requested": "В очакване на одобрение",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Не следвай",
   "account.unmute": "Unmute @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "View full profile",
   "boost_modal.combo": "You can press {combo} to skip this next time",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
   "empty_column.home.public_timeline": "the public timeline",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
   "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
   "follow_request.authorize": "Authorize",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Зареждане...",
   "media_gallery.toggle_visible": "Toggle visibility",
   "missing_indicator.label": "Not found",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.edit_profile": "Редактирай профил",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Начало",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Известия",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Drag & drop to upload",
   "upload_button.label": "Добави медия",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index c4edf3058..d8c2cc3e5 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -86,6 +86,7 @@
   "empty_column.hashtag": "Encara no hi ha res amb aquesta etiqueta.",
   "empty_column.home": "Encara no segueixes ningú. Visita {public} o fes cerca per començar i conèixer altres usuaris.",
   "empty_column.home.public_timeline": "la línia de temps pública",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "Encara no tens notificacions. Interactua amb altres per iniciar la conversa.",
   "empty_column.public": "No hi ha res aquí! Escriu alguna cosa públicament o segueix manualment usuaris d'altres instàncies per omplir-ho",
   "follow_request.authorize": "Autoritzar",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index 283a2946f..1f625252b 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Folgt dir",
   "account.media": "Medien",
   "account.mention": "@{name} erwähnen",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "@{name} stummschalten",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Beiträge",
   "account.report": "@{name} melden",
   "account.requested": "Warte auf Erlaubnis. Klicke zum Abbrechen",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "{domain} wieder anzeigen",
   "account.unfollow": "Entfolgen",
   "account.unmute": "@{name} nicht mehr stummschalten",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "Vollständiges Profil anzeigen",
   "boost_modal.combo": "Du kannst {combo} drücken, um dies beim nächsten Mal zu überspringen",
   "bundle_column_error.body": "Etwas ist beim Laden schiefgelaufen.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "Unter diesem Hashtag gibt es noch nichts.",
   "empty_column.home": "Deine Startseite ist leer! Besuche {public} oder nutze die Suche, um loszulegen und andere Leute zu finden.",
   "empty_column.home.public_timeline": "die öffentliche Zeitleiste",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "Du hast noch keine Mitteilungen. Interagiere mit anderen, um ins Gespräch zu kommen.",
   "empty_column.public": "Hier ist nichts zu sehen! Schreibe etwas öffentlich oder folge Profilen von anderen Instanzen, um die Zeitleiste aufzufüllen",
   "follow_request.authorize": "Erlauben",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Wird geladen …",
   "media_gallery.toggle_visible": "Sichtbarkeit umschalten",
   "missing_indicator.label": "Nicht gefunden",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blockierte Profile",
   "navigation_bar.community_timeline": "Lokale Zeitleiste",
   "navigation_bar.edit_profile": "Profil bearbeiten",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Startseite",
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Mitteilungen",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Zum Hochladen hereinziehen",
   "upload_button.label": "Mediendatei hinzufügen",
   "upload_form.description": "Für Menschen mit Sehbehinderung beschreiben",
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index 571a763b6..555c904ff 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -926,6 +926,15 @@
   {
     "descriptors": [
       {
+        "defaultMessage": "There is nothing in this list yet.",
+        "id": "empty_column.list"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/list_timeline/index.json"
+  },
+  {
+    "descriptors": [
+      {
         "defaultMessage": "Muted users",
         "id": "column.mutes"
       }
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 1d0bbcee5..4cb2631bc 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Follows you",
   "account.media": "Media",
   "account.mention": "Mention @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Mute @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Posts",
   "account.report": "Report @{name}",
   "account.requested": "Awaiting approval. Click to cancel follow request",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Unfollow",
   "account.unmute": "Unmute @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "View full profile",
   "boost_modal.combo": "You can press {combo} to skip this next time",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
   "empty_column.home.public_timeline": "the public timeline",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
   "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
   "follow_request.authorize": "Authorize",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Loading...",
   "media_gallery.toggle_visible": "Toggle visibility",
   "missing_indicator.label": "Not found",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.edit_profile": "Edit profile",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Home",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notifications",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Drag & drop to upload",
   "upload_button.label": "Add media",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index 3f67a8fff..3263f5677 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Sekvas vin",
   "account.media": "Sonbildaĵoj",
   "account.mention": "Mencii @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Silentigi @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Mesaĝoj",
   "account.report": "Signali @{name}",
   "account.requested": "Atendas aprobon",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Malkaŝi {domain}",
   "account.unfollow": "Ne plus sekvi",
   "account.unmute": "Malsilentigi @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "Vidi plenan profilon",
   "boost_modal.combo": "La proksiman fojon, premu {combo} por pasigi",
   "bundle_column_error.body": "Io malfunkciis ŝargante tiun ĉi komponanton.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "Ĝise, neniu enhavo estas asociita kun tiu kradvorto.",
   "empty_column.home": "Via hejma tempolinio estas malplena! Vizitu {public} aŭ uzu la serĉilon por renkonti aliajn uzantojn.",
   "empty_column.home.public_timeline": "la publika tempolinio",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "Vi dume ne havas sciigojn. Interagi kun aliajn uzantojn por komenci la konversacion.",
   "empty_column.public": "Estas nenio ĉi tie! Publike skribu ion, aŭ mane sekvu uzantojn de aliaj instancoj por plenigi la publikan tempolinion.",
   "follow_request.authorize": "Akcepti",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Ŝarganta…",
   "media_gallery.toggle_visible": "Baskuli videblecon",
   "missing_indicator.label": "Ne trovita",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blokitaj uzantoj",
   "navigation_bar.community_timeline": "Loka tempolinio",
   "navigation_bar.edit_profile": "Redakti la profilon",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Hejmo",
   "tabs_bar.local_timeline": "Loka tempolinio",
   "tabs_bar.notifications": "Sciigoj",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Algliti por alŝuti",
   "upload_button.label": "Aldoni sonbildaĵon",
   "upload_form.description": "Priskribi por la misvidantaj",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index 6e8e94700..3bfa68576 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Te sigue",
   "account.media": "Media",
   "account.mention": "Mencionar a @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Silenciar a @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Publicaciones",
   "account.report": "Reportar a @{name}",
   "account.requested": "Esperando aprobación",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Mostrar a {domain}",
   "account.unfollow": "Dejar de seguir",
   "account.unmute": "Dejar de silenciar a @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "Ver perfil completo",
   "boost_modal.combo": "Puedes presionar {combo} para saltear este aviso la próxima vez",
   "bundle_column_error.body": "Algo salió mal al cargar este componente.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "No hay nada en este hashtag aún.",
   "empty_column.home": "No estás siguiendo a nadie aún. Visita {public} o haz búsquedas para empezar y conocer gente nueva.",
   "empty_column.home.public_timeline": "la línea de tiempo pública",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "No tienes ninguna notificación aún. Interactúa con otros para empezar una conversación.",
   "empty_column.public": "¡No hay nada aquí! Escribe algo públicamente, o sigue usuarios de otras instancias manualmente para llenarlo.",
   "follow_request.authorize": "Autorizar",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Cargando…",
   "media_gallery.toggle_visible": "Cambiar visibilidad",
   "missing_indicator.label": "No encontrado",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Usuarios bloqueados",
   "navigation_bar.community_timeline": "Historia local",
   "navigation_bar.edit_profile": "Editar perfil",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Inicio",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificaciones",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Arrastra y suelta para subir",
   "upload_button.label": "Subir multimedia",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index 995d1b5ae..fb9b8489e 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -9,7 +9,9 @@
   "account.follows_you": "پیگیر شماست",
   "account.media": "رسانه",
   "account.mention": "نام‌بردن از @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "بی‌صدا کردن @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "نوشته‌ها",
   "account.report": "گزارش @{name}",
   "account.requested": "در انتظار پذیرش",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "رفع پنهان‌سازی از {domain}",
   "account.unfollow": "پایان پیگیری",
   "account.unmute": "باصدا کردن @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "نمایش نمایهٔ کامل",
   "boost_modal.combo": "دکمهٔ {combo} را بزنید تا دیگر این را نبینید",
   "bundle_column_error.body": "هنگام بازکردن این بخش خطایی رخ داد.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "هنوز هیچ چیزی با این هشتگ نیست.",
   "empty_column.home": "شما هنوز پیگیر کسی نیستید. {public} را ببینید یا چیزی را جستجو کنید تا کاربران دیگر را ببینید.",
   "empty_column.home.public_timeline": "فهرست نوشته‌های همه‌جا",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "هنوز هیچ اعلانی ندارید. به نوشته‌های دیگران واکنش نشان دهید تا گفتگو آغاز شود.",
   "empty_column.public": "این‌جا هنوز چیزی نیست! خودتان چیزی بنویسید یا کاربران دیگر را پی بگیرید تا این‌جا پر شود",
   "follow_request.authorize": "اجازه دهید",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "بارگیری...",
   "media_gallery.toggle_visible": "تغییر پیدایی",
   "missing_indicator.label": "پیدا نشد",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "کاربران مسدودشده",
   "navigation_bar.community_timeline": "نوشته‌های محلی",
   "navigation_bar.edit_profile": "ویرایش نمایه",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "خانه",
   "tabs_bar.local_timeline": "محلی",
   "tabs_bar.notifications": "اعلان‌ها",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "برای بارگذاری به این‌جا بکشید",
   "upload_button.label": "افزودن تصویر",
   "upload_form.description": "نوشتهٔ توضیحی برای کم‌بینایان و نابینایان",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index af08be5d1..913c27e33 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Seuraa sinua",
   "account.media": "Media",
   "account.mention": "Mainitse @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Mute @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Postit",
   "account.report": "Report @{name}",
   "account.requested": "Odottaa hyväksyntää",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Lopeta seuraaminen",
   "account.unmute": "Unmute @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "View full profile",
   "boost_modal.combo": "You can press {combo} to skip this next time",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
   "empty_column.home.public_timeline": "the public timeline",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
   "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
   "follow_request.authorize": "Authorize",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Ladataan...",
   "media_gallery.toggle_visible": "Toggle visibility",
   "missing_indicator.label": "Not found",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Paikallinen aikajana",
   "navigation_bar.edit_profile": "Muokkaa profiilia",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Koti",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Ilmoitukset",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Drag & drop to upload",
   "upload_button.label": "Lisää mediaa",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index 219bf4da1..8cb35619b 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Vous suit",
   "account.media": "Média",
   "account.mention": "Mentionner",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Masquer",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Statuts",
   "account.report": "Signaler",
   "account.requested": "Invitation envoyée",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Ne plus masquer {domain}",
   "account.unfollow": "Ne plus suivre",
   "account.unmute": "Ne plus masquer",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "Afficher le profil complet",
   "boost_modal.combo": "Vous pouvez appuyer sur {combo} pour pouvoir passer ceci, la prochaine fois",
   "bundle_column_error.body": "Une erreur s’est produite lors du chargement de ce composant.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "Il n’y a encore aucun contenu associé à ce hashtag",
   "empty_column.home": "Vous ne suivez encore personne. Visitez {public} ou bien utilisez la recherche pour vous connecter à d’autres utilisateur⋅ice⋅s.",
   "empty_column.home.public_timeline": "le fil public",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres utilisateur⋅ice⋅s pour débuter la conversation.",
   "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des utilisateur⋅ice⋅s d’autres instances pour remplir le fil public.",
   "follow_request.authorize": "Accepter",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Chargement…",
   "media_gallery.toggle_visible": "Modifier la visibilité",
   "missing_indicator.label": "Non trouvé",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Comptes bloqués",
   "navigation_bar.community_timeline": "Fil public local",
   "navigation_bar.edit_profile": "Modifier le profil",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Accueil",
   "tabs_bar.local_timeline": "Fil public local",
   "tabs_bar.notifications": "Notifications",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Glissez et déposez pour envoyer",
   "upload_button.label": "Joindre un média",
   "upload_form.description": "Décrire pour les malvoyants",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index a260f0968..2e26a95e5 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -9,7 +9,9 @@
   "account.follows_you": "במעקב אחריך",
   "account.media": "מדיה",
   "account.mention": "אזכור של @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "להשתיק את @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "הודעות",
   "account.report": "לדווח על @{name}",
   "account.requested": "בהמתנה לאישור",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "הסר חסימה מקהילת {domain}",
   "account.unfollow": "הפסקת מעקב",
   "account.unmute": "הפסקת השתקת @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "View full profile",
   "boost_modal.combo": "ניתן להקיש {combo} כדי לדלג בפעם הבאה",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "אין כלום בהאשתג הזה עדיין.",
   "empty_column.home": "אף אחד לא במעקב עדיין. אפשר לבקר ב{public} או להשתמש בחיפוש כדי להתחיל ולהכיר חצוצרנים אחרים.",
   "empty_column.home.public_timeline": "ציר זמן בין-קהילתי",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "אין התראות עדיין. יאללה, הגיע הזמן להתחיל להתערבב!",
   "empty_column.public": "אין פה כלום! כדי למלא את הטור הזה אפשר לכתוב משהו, או להתחיל לעקוב אחרי אנשים מקהילות אחרות.",
   "follow_request.authorize": "קבלה",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "טוען...",
   "media_gallery.toggle_visible": "נראה\\בלתי נראה",
   "missing_indicator.label": "לא נמצא",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "חסימות",
   "navigation_bar.community_timeline": "ציר זמן מקומי",
   "navigation_bar.edit_profile": "עריכת פרופיל",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "בבית",
   "tabs_bar.local_timeline": "ציר זמן מקומי",
   "tabs_bar.notifications": "התראות",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "ניתן להעלות על ידי Drag & drop",
   "upload_button.label": "הוספת מדיה",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index 6ac7fc3b4..e63da993e 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -9,7 +9,9 @@
   "account.follows_you": "te slijedi",
   "account.media": "Media",
   "account.mention": "Spomeni @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Utišaj @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Postovi",
   "account.report": "Prijavi @{name}",
   "account.requested": "Čeka pristanak",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Poništi sakrivanje {domain}",
   "account.unfollow": "Prestani slijediti",
   "account.unmute": "Poništi utišavanje @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "View full profile",
   "boost_modal.combo": "Možeš pritisnuti {combo} kako bi ovo preskočio sljedeći put",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "Još ne postoji ništa s ovim hashtagom.",
   "empty_column.home": "Još ne slijediš nikoga. Posjeti {public} ili koristi tražilicu kako bi počeo i upoznao druge korisnike.",
   "empty_column.home.public_timeline": "javni timeline",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "Još nemaš notifikacija. Komuniciraj sa drugima kako bi započeo razgovor.",
   "empty_column.public": "Ovdje nema ništa! Napiši nešto javno, ili ručno slijedi korisnike sa drugih instanci kako bi popunio",
   "follow_request.authorize": "Autoriziraj",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Učitavam...",
   "media_gallery.toggle_visible": "Preklopi vidljivost",
   "missing_indicator.label": "Nije nađen",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blokirani korisnici",
   "navigation_bar.community_timeline": "Lokalni timeline",
   "navigation_bar.edit_profile": "Uredi profil",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Dom",
   "tabs_bar.local_timeline": "Lokalno",
   "tabs_bar.notifications": "Notifikacije",
+  "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",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 5892e606e..4f8a68448 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Követnek téged",
   "account.media": "Media",
   "account.mention": "Említés",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Mute @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Posts",
   "account.report": "Report @{name}",
   "account.requested": "Awaiting approval",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Követés abbahagyása",
   "account.unmute": "Unmute @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "View full profile",
   "boost_modal.combo": "You can press {combo} to skip this next time",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
   "empty_column.home.public_timeline": "the public timeline",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
   "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
   "follow_request.authorize": "Authorize",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Betöltés...",
   "media_gallery.toggle_visible": "Toggle visibility",
   "missing_indicator.label": "Not found",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.edit_profile": "Profil szerkesztése",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Kezdőlap",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notifications",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Drag & drop to upload",
   "upload_button.label": "Média hozzáadása",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index f73ef0e19..9f5bbd140 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Mengikuti anda",
   "account.media": "Media",
   "account.mention": "Balasan @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Bisukan @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Postingan",
   "account.report": "Laporkan @{name}",
   "account.requested": "Menunggu persetujuan",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Berhenti mengikuti",
   "account.unmute": "Berhenti membisukan @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "View full profile",
   "boost_modal.combo": "Anda dapat menekan {combo} untuk melewati ini",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "Tidak ada apapun dalam hashtag ini.",
   "empty_column.home": "Anda sedang tidak mengikuti siapapun. Kunjungi {public} atau gunakan pencarian untuk memulai dan bertemu pengguna lain.",
   "empty_column.home.public_timeline": "linimasa publik",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "Anda tidak memiliki notifikasi apapun. Berinteraksi dengan orang lain untuk memulai percakapan.",
   "empty_column.public": "Tidak ada apapun disini! Tulis sesuatu, atau ikuti pengguna lain dari server lain untuk mengisinya secara manual",
   "follow_request.authorize": "Izinkan",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Tunggu sebentar...",
   "media_gallery.toggle_visible": "Tampil/Sembunyikan",
   "missing_indicator.label": "Tidak ditemukan",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Pengguna diblokir",
   "navigation_bar.community_timeline": "Linimasa lokal",
   "navigation_bar.edit_profile": "Ubah profil",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Beranda",
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Notifikasi",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Seret & lepaskan untuk mengunggah",
   "upload_button.label": "Tambahkan media",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index 53371bece..00705c7f8 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Sequas tu",
   "account.media": "Media",
   "account.mention": "Mencionar @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Celar @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Mesaji",
   "account.report": "Denuncar @{name}",
   "account.requested": "Vartante aprobo",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Ne plus sequar",
   "account.unmute": "Ne plus celar @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "View full profile",
   "boost_modal.combo": "Tu povas presar sur {combo} por omisar co en la venonta foyo",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "Esas ankore nulo en ta gretovorto.",
   "empty_column.home": "Tu sequas ankore nulu. Vizitez {public} od uzez la serchilo por komencar e renkontrar altra uzeri.",
   "empty_column.home.public_timeline": "la publika tempolineo",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "Tu havas ankore nula savigo. Komunikez kun altri por debutar la konverso.",
   "empty_column.public": "Esas nulo hike! Skribez ulo publike, o manuale sequez uzeri de altra instaluri por plenigar ol.",
   "follow_request.authorize": "Yurizar",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Kargante...",
   "media_gallery.toggle_visible": "Chanjar videbleso",
   "missing_indicator.label": "Ne trovita",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blokusita uzeri",
   "navigation_bar.community_timeline": "Lokala tempolineo",
   "navigation_bar.edit_profile": "Modifikar profilo",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Hemo",
   "tabs_bar.local_timeline": "Lokala",
   "tabs_bar.notifications": "Savigi",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Tranar faligar por kargar",
   "upload_button.label": "Adjuntar kontenajo",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index 3873d797e..92ab88d2e 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Ti segue",
   "account.media": "Media",
   "account.mention": "Menziona @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Silenzia @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Posts",
   "account.report": "Segnala @{name}",
   "account.requested": "In attesa di approvazione",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Non seguire",
   "account.unmute": "Non silenziare @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "View full profile",
   "boost_modal.combo": "Puoi premere {combo} per saltare questo passaggio la prossima volta",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "Non c'è ancora nessun post con questo hashtag.",
   "empty_column.home": "Non stai ancora seguendo nessuno. Visita {public} o usa la ricerca per incontrare nuove persone.",
   "empty_column.home.public_timeline": "la timeline pubblica",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "Non hai ancora nessuna notifica. Interagisci con altri per iniziare conversazioni.",
   "empty_column.public": "Qui non c'è nulla! Scrivi qualcosa pubblicamente, o aggiungi utenti da altri server per riempire questo spazio.",
   "follow_request.authorize": "Autorizza",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Carico...",
   "media_gallery.toggle_visible": "Imposta visibilità",
   "missing_indicator.label": "Non trovato",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Utenti bloccati",
   "navigation_bar.community_timeline": "Timeline locale",
   "navigation_bar.edit_profile": "Modifica profilo",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Home",
   "tabs_bar.local_timeline": "Locale",
   "tabs_bar.notifications": "Notifiche",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Trascina per caricare",
   "upload_button.label": "Aggiungi file multimediale",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index fb6d11ebe..19728d93c 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -9,7 +9,9 @@
   "account.follows_you": "フォローされています",
   "account.media": "メディア",
   "account.mention": "返信",
+  "account.moved_to": "{name}さんは引っ越しました:",
   "account.mute": "ミュート",
+  "account.mute_notifications": "@{name}からの通知を受け取る",
   "account.posts": "投稿",
   "account.report": "通報",
   "account.requested": "承認待ち",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "{domain}を表示",
   "account.unfollow": "フォロー解除",
   "account.unmute": "ミュート解除",
+  "account.unmute_notifications": "@{name}からの通知を受け取らない",
   "account.view_full_profile": "全ての情報を見る",
   "boost_modal.combo": "次からは{combo}を押せば、これをスキップできます。",
   "bundle_column_error.body": "コンポーネントの読み込み中に問題が発生しました。",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "このハッシュタグはまだ使われていません。",
   "empty_column.home": "まだ誰もフォローしていません。{public}を見に行くか、検索を使って他のユーザーを見つけましょう。",
   "empty_column.home.public_timeline": "連合タイムライン",
+  "empty_column.list": "このリストにはまだなにもありません。",
   "empty_column.notifications": "まだ通知がありません。他の人とふれ合って会話を始めましょう。",
   "empty_column.public": "ここにはまだ何もありません!公開で何かを投稿したり、他のインスタンスのユーザーをフォローしたりしていっぱいにしましょう!",
   "follow_request.authorize": "許可",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "読み込み中...",
   "media_gallery.toggle_visible": "表示切り替え",
   "missing_indicator.label": "見つかりません",
+  "mute_modal.hide_notifications": "このユーザーからの通知を隠しますか?",
   "navigation_bar.blocks": "ブロックしたユーザー",
   "navigation_bar.community_timeline": "ローカルタイムライン",
   "navigation_bar.edit_profile": "プロフィールを編集",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "ホーム",
   "tabs_bar.local_timeline": "ローカル",
   "tabs_bar.notifications": "通知",
+  "ui.beforeunload": "Mastodonから離れるとあなたのドラフトは失われます。",
   "upload_area.title": "ドラッグ&ドロップでアップロード",
   "upload_button.label": "メディアを追加",
   "upload_form.description": "視覚障害者のための説明",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index 2919928af..63957d5e6 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -9,7 +9,9 @@
   "account.follows_you": "날 팔로우합니다",
   "account.media": "미디어",
   "account.mention": "답장",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "뮤트",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "포스트",
   "account.report": "신고",
   "account.requested": "승인 대기 중",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "{domain} 숨김 해제",
   "account.unfollow": "팔로우 해제",
   "account.unmute": "뮤트 해제",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "전체 프로필 보기",
   "boost_modal.combo": "다음부터 {combo}를 누르면 이 과정을 건너뛸 수 있습니다.",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "이 해시태그는 아직 사용되지 않았습니다.",
   "empty_column.home": "아직 아무도 팔로우 하고 있지 않습니다. {public}를 보러 가거나, 검색하여 다른 사용자를 찾아 보세요.",
   "empty_column.home.public_timeline": "연합 타임라인",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "아직 알림이 없습니다. 다른 사람과 대화를 시작해 보세요!",
   "empty_column.public": "여기엔 아직 아무 것도 없습니다! 공개적으로 무언가 포스팅하거나, 다른 인스턴스 유저를 팔로우 해서 가득 채워보세요!",
   "follow_request.authorize": "허가",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "불러오는 중...",
   "media_gallery.toggle_visible": "표시 전환",
   "missing_indicator.label": "찾을 수 없습니다",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "차단한 사용자",
   "navigation_bar.community_timeline": "로컬 타임라인",
   "navigation_bar.edit_profile": "프로필 편집",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "홈",
   "tabs_bar.local_timeline": "로컬",
   "tabs_bar.notifications": "알림",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "드래그 & 드롭으로 업로드",
   "upload_button.label": "미디어 추가",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index d0727a24d..e27d03ead 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Volgt jou",
   "account.media": "Media",
   "account.mention": "Vermeld @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Negeer @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Toots",
   "account.report": "Rapporteer @{name}",
   "account.requested": "Wacht op goedkeuring. Klik om volgverzoek te annuleren.",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "{domain} niet meer negeren",
   "account.unfollow": "Ontvolgen",
   "account.unmute": "@{name} niet meer negeren",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "Volledig profiel tonen",
   "boost_modal.combo": "Je kunt {combo} klikken om dit de volgende keer over te slaan",
   "bundle_column_error.body": "Tijdens het laden van dit onderdeel is er iets fout gegaan.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "Er is nog niks te vinden onder deze hashtag.",
   "empty_column.home": "Jij volgt nog niemand. Bezoek {public} of gebruik het zoekvenster om andere mensen te ontmoeten.",
   "empty_column.home.public_timeline": "de globale tijdlijn",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "Je hebt nog geen meldingen. Heb interactie met andere mensen om het gesprek aan te gaan.",
   "empty_column.public": "Er is hier helemaal niks! Toot iets in het openbaar of volg mensen van andere Mastodon-servers om het te vullen.",
   "follow_request.authorize": "Goedkeuren",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Laden…",
   "media_gallery.toggle_visible": "Media wel/niet tonen",
   "missing_indicator.label": "Niet gevonden",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Geblokkeerde gebruikers",
   "navigation_bar.community_timeline": "Lokale tijdlijn",
   "navigation_bar.edit_profile": "Profiel bewerken",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Start",
   "tabs_bar.local_timeline": "Lokaal",
   "tabs_bar.notifications": "Meldingen",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Hierin slepen om te uploaden",
   "upload_button.label": "Media toevoegen",
   "upload_form.description": "Omschrijf dit voor mensen met een visuele beperking",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index d74ae0091..b3dbed03f 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Følger deg",
   "account.media": "Media",
   "account.mention": "Nevn @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Demp @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Innlegg",
   "account.report": "Rapportér @{name}",
   "account.requested": "Venter på godkjennelse",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Vis {domain}",
   "account.unfollow": "Avfølg",
   "account.unmute": "Avdemp @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "View full profile",
   "boost_modal.combo": "You kan trykke {combo} for å hoppe over dette neste gang",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "Det er ingenting i denne hashtagen ennå.",
   "empty_column.home": "Du har ikke fulgt noen ennå. Besøk {publlic} eller bruk søk for å komme i gang og møte andre brukere.",
   "empty_column.home.public_timeline": "en offentlig tidslinje",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "Du har ingen varsler ennå. Kommuniser med andre for å begynne samtalen.",
   "empty_column.public": "Det er ingenting her! Skriv noe offentlig, eller følg brukere manuelt fra andre instanser for å fylle den opp",
   "follow_request.authorize": "Autorisér",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Laster...",
   "media_gallery.toggle_visible": "Veksle synlighet",
   "missing_indicator.label": "Ikke funnet",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blokkerte brukere",
   "navigation_bar.community_timeline": "Lokal tidslinje",
   "navigation_bar.edit_profile": "Rediger profil",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Hjem",
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Varslinger",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Dra og slipp for å laste opp",
   "upload_button.label": "Legg til media",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index d826423b5..f4854ae9a 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Vos sèc",
   "account.media": "Mèdias",
   "account.mention": "Mencionar @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Rescondre @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Estatuts",
   "account.report": "Senhalar @{name}",
   "account.requested": "Invitacion mandada. Clicatz per anullar.",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Desblocar {domain}",
   "account.unfollow": "Quitar de sègre",
   "account.unmute": "Quitar de rescondre @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "Veire lo perfil complet",
   "boost_modal.combo": "Podètz botar {combo} per passar aquò lo còp que ven",
   "bundle_column_error.body": "Quicòm a fach meuca pendent lo cargament d’aqueste compausant.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "I a pas encara de contengut ligat a aqueste hashtag",
   "empty_column.home": "Vòstre flux d’acuèlh es void. Visitatz {public} o utilizatz la recèrca per vos connectar a d’autras personas.",
   "empty_column.home.public_timeline": "lo flux public",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "Avètz pas encara de notificacions. Respondètz a qualqu’un per començar una conversacion.",
   "empty_column.public": "I a pas res aquí ! Escrivètz quicòm de public, o seguètz de personas d’autras instàncias per garnir lo flux public.",
   "follow_request.authorize": "Autorizar",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Cargament…",
   "media_gallery.toggle_visible": "Modificar la visibilitat",
   "missing_indicator.label": "Pas trobat",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Personas blocadas",
   "navigation_bar.community_timeline": "Flux public local",
   "navigation_bar.edit_profile": "Modificar lo perfil",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Acuèlh",
   "tabs_bar.local_timeline": "Flux public local",
   "tabs_bar.notifications": "Notificacions",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Lisatz e depausatz per mandar",
   "upload_button.label": "Ajustar un mèdia",
   "upload_form.description": "Descripcion pels mal vesents",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index b23a5e69f..245772bde 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Śledzi Cię",
   "account.media": "Media",
   "account.mention": "Wspomnij o @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Wycisz @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Wpisy",
   "account.report": "Zgłoś @{name}",
   "account.requested": "Oczekująca prośba, kliknij aby anulować",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Odblokuj domenę {domain}",
   "account.unfollow": "Przestań śledzić",
   "account.unmute": "Cofnij wyciszenie @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "Wyświetl pełny profil",
   "boost_modal.combo": "Naciśnij {combo}, aby pominąć to następnym razem",
   "bundle_column_error.body": "Coś poszło nie tak podczas ładowania tego składnika.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "Nie ma wpisów oznaczonych tym hashtagiem. Możesz napisać pierwszy!",
   "empty_column.home": "Nie śledzisz nikogo. Odwiedź publiczną oś czasu lub użyj wyszukiwarki, aby znaleźć interesujące Cię profile.",
   "empty_column.home.public_timeline": "publiczna oś czasu",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "Nie masz żadnych powiadomień. Rozpocznij interakcje z innymi użytkownikami.",
   "empty_column.public": "Tu nic nie ma! Napisz coś publicznie, lub dodaj ludzi z innych instancji, aby to wyświetlić.",
   "follow_request.authorize": "Autoryzuj",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Ładowanie…",
   "media_gallery.toggle_visible": "Przełącz widoczność",
   "missing_indicator.label": "Nie znaleziono",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Zablokowani użytkownicy",
   "navigation_bar.community_timeline": "Lokalna oś czasu",
   "navigation_bar.edit_profile": "Edytuj profil",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Strona główna",
   "tabs_bar.local_timeline": "Lokalne",
   "tabs_bar.notifications": "Powiadomienia",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Przeciągnij i upuść aby wysłać",
   "upload_button.label": "Dodaj zawartość multimedialną",
   "upload_form.description": "Wprowadź opis dla niewidomych i niedowidzących",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index a04d1cc31..4de58408c 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Segue você",
   "account.media": "Mídia",
   "account.mention": "Mencionar @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Silenciar @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Posts",
   "account.report": "Denunciar @{name}",
   "account.requested": "Aguardando aprovação. Clique para cancelar a solicitação.",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Desbloquear {domain}",
   "account.unfollow": "Deixar de seguir",
   "account.unmute": "Não silenciar @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "Ver perfil completo",
   "boost_modal.combo": "Você pode pressionar {combo} para ignorar este diálogo na próxima vez",
   "bundle_column_error.body": "Algo de errado aconteceu enquanto este componente era carregado.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "Ainda não há qualquer conteúdo com essa hashtag",
   "empty_column.home": "Você ainda não segue usuário algo. Visite a timeline {public} ou use o buscador para procurar e conhecer outros usuários.",
   "empty_column.home.public_timeline": "global",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "Você ainda não possui notificações. Interaja com outros usuários para começar a conversar!",
   "empty_column.public": "Não há nada aqui! Escreva algo publicamente ou siga manualmente usuários de outras instâncias.",
   "follow_request.authorize": "Autorizar",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Carregando...",
   "media_gallery.toggle_visible": "Esconder/Mostrar",
   "missing_indicator.label": "Não encontrado",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Usuários bloqueados",
   "navigation_bar.community_timeline": "Local",
   "navigation_bar.edit_profile": "Editar perfil",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Página inicial",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificações",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Arraste e solte para enviar",
   "upload_button.label": "Adicionar mídia",
   "upload_form.description": "Descreva a imagem para deficientes visuais",
diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json
index 9ae140df9..1447bc2be 100644
--- a/app/javascript/mastodon/locales/pt.json
+++ b/app/javascript/mastodon/locales/pt.json
@@ -9,7 +9,9 @@
   "account.follows_you": "É teu seguidor",
   "account.media": "Media",
   "account.mention": "Mencionar @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Silenciar @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Posts",
   "account.report": "Denunciar @{name}",
   "account.requested": "A aguardar aprovação",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Deixar de seguir",
   "account.unmute": "Não silenciar @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "View full profile",
   "boost_modal.combo": "Pode clicar {combo} para não voltar a ver",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "Ainda não existe qualquer conteúdo com essa hashtag",
   "empty_column.home": "Ainda não segues qualquer utilizador. Visita {public} ou utiliza a pesquisa para procurar outros utilizadores.",
   "empty_column.home.public_timeline": "global",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "Não tens notificações. Interage com outros utilizadores para iniciar uma conversa.",
   "empty_column.public": "Não há nada aqui! Escreve algo publicamente ou segue outros utilizadores para ver aqui os conteúdos públicos.",
   "follow_request.authorize": "Autorizar",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Carregando...",
   "media_gallery.toggle_visible": "Esconder/Mostrar",
   "missing_indicator.label": "Não encontrado",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Utilizadores bloqueados",
   "navigation_bar.community_timeline": "Local",
   "navigation_bar.edit_profile": "Editar perfil",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Home",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificações",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Arraste e solte para enviar",
   "upload_button.label": "Adicionar media",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index 65bc5b374..989519058 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Подписан(а) на Вас",
   "account.media": "Медиаконтент",
   "account.mention": "Упомянуть",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Заглушить",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Посты",
   "account.report": "Пожаловаться",
   "account.requested": "Ожидает подтверждения",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Разблокировать {domain}",
   "account.unfollow": "Отписаться",
   "account.unmute": "Снять глушение",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "Показать полный профиль",
   "boost_modal.combo": "Нажмите {combo}, чтобы пропустить это в следующий раз",
   "bundle_column_error.body": "Что-то пошло не так при загрузке этого компонента.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "Статусов с таким хэштегом еще не существует.",
   "empty_column.home": "Пока Вы ни на кого не подписаны. Полистайте {public} или используйте поиск, чтобы освоиться и завести новые знакомства.",
   "empty_column.home.public_timeline": "публичные ленты",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "У Вас еще нет уведомлений. Заведите знакомство с другими пользователями, чтобы начать разговор.",
   "empty_column.public": "Здесь ничего нет! Опубликуйте что-нибудь или подпишитесь на пользователей с других узлов, чтобы заполнить ленту.",
   "follow_request.authorize": "Авторизовать",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Загрузка...",
   "media_gallery.toggle_visible": "Показать/скрыть",
   "missing_indicator.label": "Не найдено",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Список блокировки",
   "navigation_bar.community_timeline": "Локальная лента",
   "navigation_bar.edit_profile": "Изменить профиль",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Главная",
   "tabs_bar.local_timeline": "Локальная",
   "tabs_bar.notifications": "Уведомления",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Перетащите сюда, чтобы загрузить",
   "upload_button.label": "Добавить медиаконтент",
   "upload_form.description": "Описать для людей с нарушениями зрения",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index 70beb70f7..5afedecbc 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Följer dig",
   "account.media": "Media",
   "account.mention": "Nämna @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Tysta @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Inlägg",
   "account.report": "Rapportera @{name}",
   "account.requested": "Inväntar godkännande. Klicka för att avbryta följförfrågan",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Ta fram {domain}",
   "account.unfollow": "Sluta följa",
   "account.unmute": "Ta bort tystad @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "Visa hela profilen",
   "boost_modal.combo": "Du kan trycka {combo} för att slippa denna nästa gång",
   "bundle_column_error.body": "Något gick fel när du laddade denna komponent.",
@@ -82,8 +85,8 @@
   "empty_column.community": "Den lokala tidslinjen är tom. Skriv något offentligt för att få bollen att rulla!",
   "empty_column.hashtag": "Det finns inget i denna hashtag ännu.",
   "empty_column.home": "Din hemma-tidslinje är tom! Besök {public} eller använd sökning för att komma igång och träffa andra användare.",
-  "empty_column.home.inactivity": "Ditt hemmafeed är tomt. Om du har varit inaktiv ett tag kommer det att regenereras för dig snart.",
   "empty_column.home.public_timeline": "den publika tidslinjen",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "Du har inga meddelanden än. Interagera med andra för att starta konversationen.",
   "empty_column.public": "Det finns inget här! Skriv något offentligt, eller följ manuellt användarna från andra instanser för att fylla på det",
   "follow_request.authorize": "Godkänn",
@@ -105,6 +108,7 @@
   "loading_indicator.label": "Laddar...",
   "media_gallery.toggle_visible": "Växla synlighet",
   "missing_indicator.label": "Hittades inte",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blockerade användare",
   "navigation_bar.community_timeline": "Lokal tidslinje",
   "navigation_bar.edit_profile": "Redigera profil",
@@ -114,7 +118,6 @@
   "navigation_bar.logout": "Logga ut",
   "navigation_bar.mutes": "Tystade användare",
   "navigation_bar.pins": "Nålade inlägg (toots)",
-
   "navigation_bar.preferences": "Inställningar",
   "navigation_bar.public_timeline": "Förenad tidslinje",
   "notification.favourite": "{name} favoriserade din status",
@@ -161,6 +164,11 @@
   "privacy.public.short": "Publik",
   "privacy.unlisted.long": "Skicka inte till publik tidslinje",
   "privacy.unlisted.short": "Olistad",
+  "relative_time.days": "{number}d",
+  "relative_time.hours": "{number}h",
+  "relative_time.just_now": "now",
+  "relative_time.minutes": "{number}m",
+  "relative_time.seconds": "{number}s",
   "reply_indicator.cancel": "Ångra",
   "report.placeholder": "Ytterligare kommentarer",
   "report.submit": "Skicka",
@@ -180,6 +188,7 @@
   "status.load_more": "Ladda fler",
   "status.media_hidden": "Media dold",
   "status.mention": "Omnämn @{name}",
+  "status.more": "More",
   "status.mute_conversation": "Tysta konversation",
   "status.open": "Utvidga denna status",
   "status.pin": "Fäst i profil",
@@ -200,6 +209,7 @@
   "tabs_bar.home": "Hem",
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Meddelanden",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Dra & släpp för att ladda upp",
   "upload_button.label": "Lägg till media",
   "upload_form.description": "Beskriv för synskadade",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index db3f3dd0d..3e8422dd1 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Follows you",
   "account.media": "Media",
   "account.mention": "Mention @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Mute @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Posts",
   "account.report": "Report @{name}",
   "account.requested": "Awaiting approval",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Unfollow",
   "account.unmute": "Unmute @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "View full profile",
   "boost_modal.combo": "You can press {combo} to skip this next time",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
   "empty_column.home.public_timeline": "the public timeline",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
   "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
   "follow_request.authorize": "Authorize",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Loading...",
   "media_gallery.toggle_visible": "Toggle visibility",
   "missing_indicator.label": "Not found",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.edit_profile": "Edit profile",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Home",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notifications",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Drag & drop to upload",
   "upload_button.label": "Add media",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index cdd3581da..42f67729e 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Seni takip ediyor",
   "account.media": "Media",
   "account.mention": "Bahset @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Sustur @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Gönderiler",
   "account.report": "Rapor et @{name}",
   "account.requested": "Onay bekleniyor",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Takipten vazgeç",
   "account.unmute": "Sesi aç @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "View full profile",
   "boost_modal.combo": "Bir dahaki sefere {combo} tuşuna basabilirsiniz",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "Henüz bu hashtag’e sahip hiçbir gönderi yok.",
   "empty_column.home": "Henüz kimseyi takip etmiyorsunuz. {public} ziyaret edebilir veya arama kısmını kullanarak diğer kullanıcılarla iletişime geçebilirsiniz.",
   "empty_column.home.public_timeline": "herkese açık zaman tüneli",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "Henüz hiçbir bildiriminiz yok. Diğer insanlarla sobhet edebilmek için etkileşime geçebilirsiniz.",
   "empty_column.public": "Burada hiçbir gönderi yok! Herkese açık bir şeyler yazın, veya diğer sunucudaki insanları takip ederek bu alanın dolmasını sağlayın",
   "follow_request.authorize": "Yetkilendir",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Yükleniyor...",
   "media_gallery.toggle_visible": "Görünürlüğü değiştir",
   "missing_indicator.label": "Bulunamadı",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Engellenen kullanıcılar",
   "navigation_bar.community_timeline": "Yerel zaman tüneli",
   "navigation_bar.edit_profile": "Profili düzenle",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Ana sayfa",
   "tabs_bar.local_timeline": "Yerel",
   "tabs_bar.notifications": "Bildirimler",
+  "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",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index 930529f15..514f988c7 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -9,7 +9,9 @@
   "account.follows_you": "Підписаний(-а) на Вас",
   "account.media": "Медія",
   "account.mention": "Згадати",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "Заглушити",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "Пости",
   "account.report": "Поскаржитися",
   "account.requested": "Очікує підтвердження",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "Розблокувати {domain}",
   "account.unfollow": "Відписатися",
   "account.unmute": "Зняти глушення",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "View full profile",
   "boost_modal.combo": "Ви можете натиснути {combo}, щоб пропустити це наступного разу",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "Дописів з цим хештегом поки не існує.",
   "empty_column.home": "Ви поки ні на кого не підписані. Погортайте {public}, або скористуйтесь пошуком, щоб освоїтися та познайомитися з іншими користувачами.",
   "empty_column.home.public_timeline": "публічні стрічки",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "У вас ще немає сповіщень. Переписуйтесь з іншими користувачами, щоб почати розмову.",
   "empty_column.public": "Тут поки нічого немає! Опублікуйте щось, або вручну підпишіться на користувачів інших інстанцій, щоб заповнити стрічку.",
   "follow_request.authorize": "Авторизувати",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "Завантаження...",
   "media_gallery.toggle_visible": "Показати/приховати",
   "missing_indicator.label": "Не знайдено",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Заблоковані користувачі",
   "navigation_bar.community_timeline": "Локальна стрічка",
   "navigation_bar.edit_profile": "Редагувати профіль",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "Головна",
   "tabs_bar.local_timeline": "Локальна",
   "tabs_bar.notifications": "Сповіщення",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Перетягніть сюди, щоб завантажити",
   "upload_button.label": "Додати медіаконтент",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/whitelist_sv.json b/app/javascript/mastodon/locales/whitelist_sv.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_sv.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index bbdf34d2f..a820b9790 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -9,9 +9,9 @@
   "account.follows_you": "关注了你",
   "account.media": "媒体",
   "account.mention": "提及 @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "隐藏 @{name}",
   "account.mute_notifications": "隐藏来自 @{name} 的通知",
-  "account.unmute_notifications": "不再隐藏来自 @{name} 的通知",
   "account.posts": "嘟文",
   "account.report": "举报 @{name}",
   "account.requested": "正在等待对方同意。点击以取消发送关注请求",
@@ -20,6 +20,7 @@
   "account.unblock_domain": "不再隐藏来自 {domain} 的内容",
   "account.unfollow": "取消关注",
   "account.unmute": "不再隐藏 @{name}",
+  "account.unmute_notifications": "不再隐藏来自 @{name} 的通知",
   "account.view_full_profile": "查看完整资料",
   "boost_modal.combo": "下次按住 {combo} 即可跳过此提示",
   "bundle_column_error.body": "载入组件出错。",
@@ -85,6 +86,7 @@
   "empty_column.hashtag": "这个话题标签下暂时没有内容。",
   "empty_column.home": "你还没有关注任何用户。快看看{public},向其他用户搭讪吧。",
   "empty_column.home.public_timeline": "公共时间轴",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "你还没有收到过通知信息,快向其他用户搭讪吧。",
   "empty_column.public": "这里神马都没有!写一些公开的嘟文,或者关注其他实例的用户,这里就会有嘟文出现了哦!",
   "follow_request.authorize": "同意",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index f6f56fee1..be5a47370 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -9,7 +9,9 @@
   "account.follows_you": "關注你",
   "account.media": "媒體",
   "account.mention": "提及 @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "將 @{name} 靜音",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "文章",
   "account.report": "舉報 @{name}",
   "account.requested": "等候審批",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "不再隱藏 {domain}",
   "account.unfollow": "取消關注",
   "account.unmute": "取消 @{name} 的靜音",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "查看完整資料",
   "boost_modal.combo": "如你想在下次路過這顯示,請按{combo},",
   "bundle_column_error.body": "加載本組件出錯。",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "這個標籤暫時未有內容。",
   "empty_column.home": "你還沒有關注任何用戶。快看看{public},向其他用戶搭訕吧。",
   "empty_column.home.public_timeline": "公共時間軸",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "你沒有任何通知紀錄,快向其他用戶搭訕吧。",
   "empty_column.public": "跨站時間軸暫時沒有內容!快寫一些公共的文章,或者關注另一些服務站的用戶吧!你和本站、友站的交流,將決定這裏出現的內容。",
   "follow_request.authorize": "批准",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "載入中...",
   "media_gallery.toggle_visible": "打開或關上",
   "missing_indicator.label": "找不到內容",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "被你封鎖的用戶",
   "navigation_bar.community_timeline": "本站時間軸",
   "navigation_bar.edit_profile": "修改個人資料",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "主頁",
   "tabs_bar.local_timeline": "本站",
   "tabs_bar.notifications": "通知",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "將檔案拖放至此上載",
   "upload_button.label": "上載媒體檔案",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index 1f43c6a20..9aa9973fe 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -9,7 +9,9 @@
   "account.follows_you": "關注你",
   "account.media": "媒體",
   "account.mention": "提到 @{name}",
+  "account.moved_to": "{name} has moved to:",
   "account.mute": "消音 @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
   "account.posts": "貼文",
   "account.report": "檢舉 @{name}",
   "account.requested": "正在等待許可",
@@ -18,6 +20,7 @@
   "account.unblock_domain": "不再隱藏 {domain}",
   "account.unfollow": "取消關注",
   "account.unmute": "不再消音 @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
   "account.view_full_profile": "查看完整資訊",
   "boost_modal.combo": "下次你可以按 {combo} 來跳過",
   "bundle_column_error.body": "加載本組件出錯。",
@@ -83,6 +86,7 @@
   "empty_column.hashtag": "這個主題標籤下什麼都沒有。",
   "empty_column.home": "你還沒關注任何人。造訪{public}或利用搜尋功能找到其他用者。",
   "empty_column.home.public_timeline": "公開時間軸",
+  "empty_column.list": "There is nothing in this list yet.",
   "empty_column.notifications": "還沒有任何通知。和別的使用者互動來開始對話。",
   "empty_column.public": "這裡什麼都沒有!公開寫些什麼,或是關注其他副本的使用者。",
   "follow_request.authorize": "授權",
@@ -104,6 +108,7 @@
   "loading_indicator.label": "讀取中...",
   "media_gallery.toggle_visible": "切換可見性",
   "missing_indicator.label": "找不到",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "封鎖的使用者",
   "navigation_bar.community_timeline": "本地時間軸",
   "navigation_bar.edit_profile": "編輯用者資訊",
@@ -204,6 +209,7 @@
   "tabs_bar.home": "家",
   "tabs_bar.local_timeline": "本地",
   "tabs_bar.notifications": "通知",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "拖放來上傳",
   "upload_button.label": "增加媒體",
   "upload_form.description": "Describe for the visually impaired",
diff --git a/app/javascript/mastodon/reducers/index.js b/app/javascript/mastodon/reducers/index.js
index 17c870351..425a2acdd 100644
--- a/app/javascript/mastodon/reducers/index.js
+++ b/app/javascript/mastodon/reducers/index.js
@@ -22,6 +22,7 @@ import media_attachments from './media_attachments';
 import notifications from './notifications';
 import height_cache from './height_cache';
 import custom_emojis from './custom_emojis';
+import lists from './lists';
 
 const reducers = {
   timelines,
@@ -47,6 +48,7 @@ const reducers = {
   notifications,
   height_cache,
   custom_emojis,
+  lists,
 };
 
 export default combineReducers(reducers);
diff --git a/app/javascript/mastodon/reducers/lists.js b/app/javascript/mastodon/reducers/lists.js
new file mode 100644
index 000000000..3e3908869
--- /dev/null
+++ b/app/javascript/mastodon/reducers/lists.js
@@ -0,0 +1,15 @@
+import { LIST_FETCH_SUCCESS } from '../actions/lists';
+import { Map as ImmutableMap, fromJS } from 'immutable';
+
+const initialState = ImmutableMap();
+
+const normalizeList = (state, list) => state.set(list.id, fromJS(list));
+
+export default function lists(state = initialState, action) {
+  switch(action.type) {
+  case LIST_FETCH_SUCCESS:
+    return normalizeList(state, action.list);
+  default:
+    return state;
+  }
+};
diff --git a/app/javascript/mastodon/reducers/timelines.js b/app/javascript/mastodon/reducers/timelines.js
index bee4c4ef9..9984c3b5d 100644
--- a/app/javascript/mastodon/reducers/timelines.js
+++ b/app/javascript/mastodon/reducers/timelines.js
@@ -55,7 +55,7 @@ const appendNormalizedTimeline = (state, timeline, statuses, next) => {
   }));
 };
 
-const updateTimeline = (state, timeline, status, references) => {
+const updateTimeline = (state, timeline, status) => {
   const top        = state.getIn([timeline, 'top']);
   const ids        = state.getIn([timeline, 'items'], ImmutableList());
   const includesId = ids.includes(status.get('id'));
@@ -70,7 +70,6 @@ const updateTimeline = (state, timeline, status, references) => {
   return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
     if (!top) mMap.set('unread', unread + 1);
     if (top && ids.size > 40) newIds = newIds.take(20);
-    if (status.getIn(['reblog', 'id'], null) !== null) newIds = newIds.filterNot(item => references.includes(item));
     mMap.set('items', newIds.unshift(status.get('id')));
   }));
 };
@@ -129,7 +128,7 @@ export default function timelines(state = initialState, action) {
   case TIMELINE_EXPAND_SUCCESS:
     return appendNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next);
   case TIMELINE_UPDATE:
-    return updateTimeline(state, action.timeline, fromJS(action.status), action.references);
+    return updateTimeline(state, action.timeline, fromJS(action.status));
   case TIMELINE_DELETE:
     return deleteStatus(state, action.id, action.accountId, action.references, action.reblogOf);
   case ACCOUNT_BLOCK_SUCCESS:
diff --git a/app/models/status.rb b/app/models/status.rb
index 172d3a665..70cfdc1c7 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -183,7 +183,7 @@ class Status < ApplicationRecord
     end
 
     def reblogs_map(status_ids, account_id)
-      select('reblog_of_id').where(reblog_of_id: status_ids).where(account_id: account_id).map { |s| [s.reblog_of_id, true] }.to_h
+      select('reblog_of_id').where(reblog_of_id: status_ids).where(account_id: account_id).reorder(nil).map { |s| [s.reblog_of_id, true] }.to_h
     end
 
     def mutes_map(conversation_ids, account_id)
@@ -291,6 +291,7 @@ class Status < ApplicationRecord
 
   def set_visibility
     self.visibility = (account.locked? ? :private : :public) if visibility.nil?
+    self.visibility = reblog.visibility if reblog?
     self.sensitive  = false if sensitive.nil?
   end
 
diff --git a/app/serializers/rest/list_serializer.rb b/app/serializers/rest/list_serializer.rb
index c0150888e..977da7439 100644
--- a/app/serializers/rest/list_serializer.rb
+++ b/app/serializers/rest/list_serializer.rb
@@ -2,4 +2,8 @@
 
 class REST::ListSerializer < ActiveModel::Serializer
   attributes :id, :title
+
+  def id
+    object.id.to_s
+  end
 end
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index a6c59738a..e52ffe642 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -133,6 +133,32 @@ ja:
       unsubscribe: 購読の解除
       username: ユーザー名
       web: Web
+    action_logs:
+      actions:
+        confirm_user: "%{name} さんが %{target} さんのメールアドレスを確認済みにしました"
+        create_custom_emoji: "%{name} さんがカスタム絵文字 %{target} を追加しました"
+        create_domain_block: "%{name} さんがドメイン %{target} をブロックしました"
+        create_email_domain_block: "%{name} さんがドメイン %{target} をメールアドレス用ブラックリストに追加しました"
+        demote_user: "%{name} さんが %{target} さんを降格しました"
+        destroy_domain_block: "%{name} さんがドメイン %{target} のブロックを外しました"
+        destroy_email_domain_block: "%{name} さんがドメイン %{target} をメールアドレス用ブラックリストから外しました"
+        destroy_status: "%{name} さんが %{target} さんの投稿を削除しました"
+        disable_2fa_user: "%{name} さんが %{target} さんの二段階認証を無効化しました"
+        disable_custom_emoji: "%{name} さんがカスタム絵文字 %{target} を無効化しました"
+        disable_user: "%{name} さんが %{target} さんのログインを無効化しました"
+        enable_custom_emoji: "%{name} さんがカスタム絵文字 %{target} を有効化しました"
+        enable_user: "%{name} さんが %{target} さんのログインを有効化しました"
+        memorialize_account: "%{name} さんが %{target} さんを追悼アカウントページに登録しました"
+        promote_user: "%{name} さんが %{target} さんを昇格しました"
+        reset_password_user: "%{name} さんが %{target} さんのパスワードをリセットしました"
+        resolve_report: "%{name} さんがレポート %{target} を棄却しました"
+        silence_account: "%{name} さんが %{target} さんをサイレンスにしました"
+        suspend_account: "%{name} さんが %{target} さんを停止しました"
+        unsilence_account: "%{name} さんが %{target} さんのサイレンスを解除しました"
+        unsuspend_account: "%{name} さんが %{target} さんの停止を解除しました"
+        update_custom_emoji: "%{name} さんがカスタム絵文字 %{target} を更新しました"
+        update_status: "%{name} さんが %{target} さんの投稿を更新しました"
+      title: 操作履歴
     custom_emojis:
       copied_msg: 絵文字のコピーをローカルに作成しました
       copy: コピー
@@ -191,14 +217,14 @@ ja:
       undo: 元に戻す
     email_domain_blocks:
       add_new: 新規追加
-      created_msg: 処理を完了しました
+      created_msg: ブラックリストに追加しました
       delete: 消去
-      destroyed_msg: 消去しました
+      destroyed_msg: ブラックリストから外しました
       domain: ドメイン
       new:
-        create: ブロックを作成
-        title: 新規メールドメインブロック
-      title: メールドメインブロック
+        create: ドメインを追加
+        title: メールアドレス用ブラックリスト新規追加
+      title: メールブラックリスト
     instances:
       account_count: 既知のアカウント数
       domain_name: ドメイン名
@@ -404,6 +430,8 @@ ja:
     validations:
       images_and_video: 既に画像が追加されているため、動画を追加することはできません。
       too_many: 追加できるファイルは4つまでです。
+  moderation:
+    title: モデレーション
   notification_mailer:
     digest:
       body: "%{instance} での最後のログインからの出来事:"
diff --git a/config/navigation.rb b/config/navigation.rb
index 4d46e4439..4ae931df0 100644
--- a/config/navigation.rb
+++ b/config/navigation.rb
@@ -30,7 +30,7 @@ SimpleNavigation::Configuration.run do |navigation|
       admin.item :email_domain_blocks, safe_join([fa_icon('envelope fw'), t('admin.email_domain_blocks.title')]), admin_email_domain_blocks_url, highlights_on: %r{/admin/email_domain_blocks}, if: -> { current_user.admin? }
     end
 
-    primary.item :admin, safe_join([fa_icon('cogs fw'), t('admin.title')]), edit_admin_settings_url, if: proc { current_user.staff? } do |admin|
+    primary.item :admin, safe_join([fa_icon('cogs fw'), t('admin.title')]), proc { current_user.admin? ? edit_admin_settings_url : admin_custom_emojis_url }, if: proc { current_user.staff? } do |admin|
       admin.item :settings, safe_join([fa_icon('cogs fw'), t('admin.settings.title')]), edit_admin_settings_url, if: -> { current_user.admin? }
       admin.item :custom_emojis, safe_join([fa_icon('smile-o fw'), t('admin.custom_emojis.title')]), admin_custom_emojis_url, highlights_on: %r{/admin/custom_emojis}
       admin.item :subscriptions, safe_join([fa_icon('paper-plane-o fw'), t('admin.subscriptions.title')]), admin_subscriptions_url, if: -> { current_user.admin? }
diff --git a/db/migrate/20171122120436_add_index_account_and_reblog_of_id_to_statuses.rb b/db/migrate/20171122120436_add_index_account_and_reblog_of_id_to_statuses.rb
new file mode 100644
index 000000000..49c1ce0a8
--- /dev/null
+++ b/db/migrate/20171122120436_add_index_account_and_reblog_of_id_to_statuses.rb
@@ -0,0 +1,6 @@
+class AddIndexAccountAndReblogOfIdToStatuses < ActiveRecord::Migration[5.1]
+  def change
+    commit_db_transaction
+    add_index :statuses, [:account_id, :reblog_of_id], algorithm: :concurrently
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 712269118..5bce18271 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20171119172437) do
+ActiveRecord::Schema.define(version: 20171122120436) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -408,6 +408,7 @@ ActiveRecord::Schema.define(version: 20171119172437) do
     t.bigint "application_id"
     t.bigint "in_reply_to_account_id"
     t.index ["account_id", "id"], name: "index_statuses_on_account_id_id"
+    t.index ["account_id", "reblog_of_id"], name: "index_statuses_on_account_id_and_reblog_of_id"
     t.index ["conversation_id"], name: "index_statuses_on_conversation_id"
     t.index ["in_reply_to_id"], name: "index_statuses_on_in_reply_to_id"
     t.index ["reblog_of_id"], name: "index_statuses_on_reblog_of_id"
diff --git a/spec/models/concerns/streamable_spec.rb b/spec/models/concerns/streamable_spec.rb
new file mode 100644
index 000000000..b5f2d5192
--- /dev/null
+++ b/spec/models/concerns/streamable_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe Streamable do
+  class Parent
+    def title; end
+
+    def target; end
+
+    def thread; end
+
+    def self.has_one(*); end
+
+    def self.after_create; end
+  end
+
+  class Child < Parent
+    include Streamable
+  end
+
+  child = Child.new
+
+  describe '#title' do
+    it 'calls Parent#title' do
+      expect_any_instance_of(Parent).to receive(:title)
+      child.title
+    end
+  end
+
+  describe '#content' do
+    it 'calls #title' do
+      expect_any_instance_of(Parent).to receive(:title)
+      child.content
+    end
+  end
+
+  describe '#target' do
+    it 'calls Parent#target' do
+      expect_any_instance_of(Parent).to receive(:target)
+      child.target
+    end
+  end
+
+  describe '#object_type' do
+    it 'returns :activity' do
+      expect(child.object_type).to eq :activity
+    end
+  end
+
+  describe '#thread' do
+    it 'calls Parent#thread' do
+      expect_any_instance_of(Parent).to receive(:thread)
+      child.thread
+    end
+  end
+
+  describe '#hidden?' do
+    it 'returns false' do
+      expect(child.hidden?).to be false
+    end
+  end
+end
diff --git a/spec/presenters/account_relationships_presenter_spec.rb b/spec/presenters/account_relationships_presenter_spec.rb
new file mode 100644
index 000000000..f8b048d38
--- /dev/null
+++ b/spec/presenters/account_relationships_presenter_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe AccountRelationshipsPresenter do
+  describe '.initialize' do
+    before do
+      allow(Account).to receive(:following_map).with(account_ids, current_account_id).and_return(default_map)
+      allow(Account).to receive(:followed_by_map).with(account_ids, current_account_id).and_return(default_map)
+      allow(Account).to receive(:blocking_map).with(account_ids, current_account_id).and_return(default_map)
+      allow(Account).to receive(:muting_map).with(account_ids, current_account_id).and_return(default_map)
+      allow(Account).to receive(:requested_map).with(account_ids, current_account_id).and_return(default_map)
+      allow(Account).to receive(:domain_blocking_map).with(account_ids, current_account_id).and_return(default_map)
+    end
+
+    let(:presenter)          { AccountRelationshipsPresenter.new(account_ids, current_account_id, options) }
+    let(:current_account_id) { Fabricate(:account).id }
+    let(:account_ids)        { [Fabricate(:account).id] }
+    let(:default_map)        { { 1 => true } }
+
+    context 'options are not set' do
+      let(:options) { {} }
+
+      it 'sets default maps' do
+        expect(presenter.following).to       eq default_map
+        expect(presenter.followed_by).to     eq default_map
+        expect(presenter.blocking).to        eq default_map
+        expect(presenter.muting).to          eq default_map
+        expect(presenter.requested).to       eq default_map
+        expect(presenter.domain_blocking).to eq default_map
+      end
+    end
+
+    context 'options[:following_map] is set' do
+      let(:options) { { following_map: { 2 => true } } }
+
+      it 'sets @following merged with default_map and options[:following_map]' do
+        expect(presenter.following).to eq default_map.merge(options[:following_map])
+      end
+    end
+
+    context 'options[:followed_by_map] is set' do
+      let(:options) { { followed_by_map: { 3 => true } } }
+
+      it 'sets @followed_by merged with default_map and options[:followed_by_map]' do
+        expect(presenter.followed_by).to eq default_map.merge(options[:followed_by_map])
+      end
+    end
+
+    context 'options[:blocking_map] is set' do
+      let(:options) { { blocking_map: { 4 => true } } }
+
+      it 'sets @blocking merged with default_map and options[:blocking_map]' do
+        expect(presenter.blocking).to eq default_map.merge(options[:blocking_map])
+      end
+    end
+
+    context 'options[:muting_map] is set' do
+      let(:options) { { muting_map: { 5 => true } } }
+
+      it 'sets @muting merged with default_map and options[:muting_map]' do
+        expect(presenter.muting).to eq default_map.merge(options[:muting_map])
+      end
+    end
+
+    context 'options[:requested_map] is set' do
+      let(:options) { { requested_map: { 6 => true } } }
+
+      it 'sets @requested merged with default_map and options[:requested_map]' do
+        expect(presenter.requested).to eq default_map.merge(options[:requested_map])
+      end
+    end
+
+    context 'options[:domain_blocking_map] is set' do
+      let(:options) { { domain_blocking_map: { 7 => true } } }
+
+      it 'sets @domain_blocking merged with default_map and options[:domain_blocking_map]' do
+        expect(presenter.domain_blocking).to eq default_map.merge(options[:domain_blocking_map])
+      end
+    end
+  end
+end