about summary refs log tree commit diff
path: root/app/javascript
diff options
context:
space:
mode:
Diffstat (limited to 'app/javascript')
-rw-r--r--app/javascript/flavours/glitch/actions/accounts.js84
-rw-r--r--app/javascript/flavours/glitch/actions/search.js2
-rw-r--r--app/javascript/flavours/glitch/components/hashtag.js34
-rw-r--r--app/javascript/flavours/glitch/components/status.js1
-rw-r--r--app/javascript/flavours/glitch/components/status_action_bar.js34
-rw-r--r--app/javascript/flavours/glitch/features/drawer/results/index.js11
-rw-r--r--app/javascript/flavours/glitch/features/getting_started_misc/index.js25
-rw-r--r--app/javascript/flavours/glitch/features/list_editor/components/account.js23
-rw-r--r--app/javascript/flavours/glitch/features/list_editor/components/search.js16
-rw-r--r--app/javascript/flavours/glitch/features/list_editor/containers/account_container.js24
-rw-r--r--app/javascript/flavours/glitch/features/list_editor/containers/search_container.js17
-rw-r--r--app/javascript/flavours/glitch/features/list_editor/index.js10
-rw-r--r--app/javascript/flavours/glitch/features/local_settings/page/index.js59
-rw-r--r--app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/account_container.js24
-rw-r--r--app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/search_container.js21
-rw-r--r--app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js78
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/modal_root.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/accounts.js4
-rw-r--r--app/javascript/flavours/glitch/reducers/index.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/local_settings.js1
-rw-r--r--app/javascript/flavours/glitch/reducers/pinned_accounts_editor.js57
-rw-r--r--app/javascript/flavours/glitch/reducers/search.js4
-rw-r--r--app/javascript/flavours/glitch/styles/components/search.scss84
-rw-r--r--app/javascript/flavours/glitch/styles/components/status.scss20
-rw-r--r--app/javascript/flavours/glitch/util/async-components.js4
-rw-r--r--app/javascript/flavours/glitch/util/numbers.js10
-rw-r--r--app/javascript/mastodon/locales/ar.json13
-rw-r--r--app/javascript/mastodon/locales/ast.json1
-rw-r--r--app/javascript/mastodon/locales/bg.json1
-rw-r--r--app/javascript/mastodon/locales/ca.json1
-rw-r--r--app/javascript/mastodon/locales/co.json1
-rw-r--r--app/javascript/mastodon/locales/cs.json13
-rw-r--r--app/javascript/mastodon/locales/da.json31
-rw-r--r--app/javascript/mastodon/locales/de.json1
-rw-r--r--app/javascript/mastodon/locales/defaultMessages.json4
-rw-r--r--app/javascript/mastodon/locales/el.json1
-rw-r--r--app/javascript/mastodon/locales/en.json1
-rw-r--r--app/javascript/mastodon/locales/eo.json11
-rw-r--r--app/javascript/mastodon/locales/es.json1
-rw-r--r--app/javascript/mastodon/locales/eu.json5
-rw-r--r--app/javascript/mastodon/locales/fa.json7
-rw-r--r--app/javascript/mastodon/locales/fi.json1
-rw-r--r--app/javascript/mastodon/locales/fr.json5
-rw-r--r--app/javascript/mastodon/locales/gl.json5
-rw-r--r--app/javascript/mastodon/locales/he.json11
-rw-r--r--app/javascript/mastodon/locales/hr.json1
-rw-r--r--app/javascript/mastodon/locales/hu.json1
-rw-r--r--app/javascript/mastodon/locales/hy.json1
-rw-r--r--app/javascript/mastodon/locales/id.json1
-rw-r--r--app/javascript/mastodon/locales/io.json1
-rw-r--r--app/javascript/mastodon/locales/it.json3
-rw-r--r--app/javascript/mastodon/locales/ja.json5
-rw-r--r--app/javascript/mastodon/locales/ka.json1
-rw-r--r--app/javascript/mastodon/locales/ko.json1
-rw-r--r--app/javascript/mastodon/locales/nl.json5
-rw-r--r--app/javascript/mastodon/locales/no.json1
-rw-r--r--app/javascript/mastodon/locales/oc.json1
-rw-r--r--app/javascript/mastodon/locales/pl.json1
-rw-r--r--app/javascript/mastodon/locales/pt-BR.json1
-rw-r--r--app/javascript/mastodon/locales/pt.json1
-rw-r--r--app/javascript/mastodon/locales/ru.json1
-rw-r--r--app/javascript/mastodon/locales/sk.json1
-rw-r--r--app/javascript/mastodon/locales/sl.json1
-rw-r--r--app/javascript/mastodon/locales/sr-Latn.json1
-rw-r--r--app/javascript/mastodon/locales/sr.json1
-rw-r--r--app/javascript/mastodon/locales/sv.json5
-rw-r--r--app/javascript/mastodon/locales/te.json1
-rw-r--r--app/javascript/mastodon/locales/th.json1
-rw-r--r--app/javascript/mastodon/locales/tr.json1
-rw-r--r--app/javascript/mastodon/locales/uk.json1
-rw-r--r--app/javascript/mastodon/locales/zh-CN.json1
-rw-r--r--app/javascript/mastodon/locales/zh-HK.json1
-rw-r--r--app/javascript/mastodon/locales/zh-TW.json1
-rw-r--r--app/javascript/styles/mastodon/forms.scss6
74 files changed, 659 insertions, 154 deletions
diff --git a/app/javascript/flavours/glitch/actions/accounts.js b/app/javascript/flavours/glitch/actions/accounts.js
index d5c4a02f9..d67ab112e 100644
--- a/app/javascript/flavours/glitch/actions/accounts.js
+++ b/app/javascript/flavours/glitch/actions/accounts.js
@@ -72,6 +72,17 @@ export const FOLLOW_REQUEST_REJECT_REQUEST = 'FOLLOW_REQUEST_REJECT_REQUEST';
 export const FOLLOW_REQUEST_REJECT_SUCCESS = 'FOLLOW_REQUEST_REJECT_SUCCESS';
 export const FOLLOW_REQUEST_REJECT_FAIL    = 'FOLLOW_REQUEST_REJECT_FAIL';
 
+export const PINNED_ACCOUNTS_FETCH_REQUEST = 'PINNED_ACCOUNTS_FETCH_REQUEST';
+export const PINNED_ACCOUNTS_FETCH_SUCCESS = 'PINNED_ACCOUNTS_FETCH_SUCCESS';
+export const PINNED_ACCOUNTS_FETCH_FAIL    = 'PINNED_ACCOUNTS_FETCH_FAIL';
+
+export const PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_READY  = 'PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_READY';
+export const PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CLEAR  = 'PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CLEAR';
+export const PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CHANGE = 'PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CHANGE';
+
+export const PINNED_ACCOUNTS_EDITOR_RESET = 'PINNED_ACCOUNTS_EDITOR_RESET';
+
+
 export function fetchAccount(id) {
   return (dispatch, getState) => {
     dispatch(fetchRelationships([id]));
@@ -733,3 +744,76 @@ export function unpinAccountFail(error) {
     error,
   };
 };
+
+export function fetchPinnedAccounts() {
+  return (dispatch, getState) => {
+    dispatch(fetchPinnedAccountsRequest());
+
+    api(getState).get(`/api/v1/endorsements`, { params: { limit: 0 } })
+      .then(({ data }) => dispatch(fetchPinnedAccountsSuccess(data)))
+      .catch(err => dispatch(fetchPinnedAccountsFail(err)));
+  };
+};
+
+export function fetchPinnedAccountsRequest() {
+  return {
+    type: PINNED_ACCOUNTS_FETCH_REQUEST,
+  };
+};
+
+export function fetchPinnedAccountsSuccess(accounts, next) {
+  return {
+    type: PINNED_ACCOUNTS_FETCH_SUCCESS,
+    accounts,
+    next,
+  };
+};
+
+export function fetchPinnedAccountsFail(error) {
+  return {
+    type: PINNED_ACCOUNTS_FETCH_FAIL,
+    error,
+  };
+};
+
+export function fetchPinnedAccountsSuggestions(q) {
+  return (dispatch, getState) => {
+    const params = {
+      q,
+      resolve: false,
+      limit: 4,
+      following: true,
+    };
+
+    api(getState).get('/api/v1/accounts/search', { params })
+      .then(({ data }) => dispatch(fetchPinnedAccountsSuggestionsReady(q, data)));
+  };
+};
+
+export function fetchPinnedAccountsSuggestionsReady(query, accounts) {
+  return {
+    type: PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_READY,
+    query,
+    accounts,
+  };
+};
+
+export function clearPinnedAccountsSuggestions() {
+  return {
+    type: PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CLEAR,
+  };
+};
+
+export function changePinnedAccountsSuggestions(value) {
+  return {
+    type: PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CHANGE,
+    value,
+  }
+};
+
+export function resetPinnedAccountsEditor() {
+  return {
+    type: PINNED_ACCOUNTS_EDITOR_RESET,
+  };
+};
+
diff --git a/app/javascript/flavours/glitch/actions/search.js b/app/javascript/flavours/glitch/actions/search.js
index 13885c600..ec65bdf28 100644
--- a/app/javascript/flavours/glitch/actions/search.js
+++ b/app/javascript/flavours/glitch/actions/search.js
@@ -32,7 +32,7 @@ export function submitSearch() {
 
     dispatch(fetchSearchRequest());
 
-    api(getState).get('/api/v1/search', {
+    api(getState).get('/api/v2/search', {
       params: {
         q: value,
         resolve: true,
diff --git a/app/javascript/flavours/glitch/components/hashtag.js b/app/javascript/flavours/glitch/components/hashtag.js
new file mode 100644
index 000000000..88689cc6c
--- /dev/null
+++ b/app/javascript/flavours/glitch/components/hashtag.js
@@ -0,0 +1,34 @@
+import React from 'react';
+import { Sparklines, SparklinesCurve } from 'react-sparklines';
+import { Link } from 'react-router-dom';
+import { FormattedMessage } from 'react-intl';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { shortNumberFormat } from 'flavours/glitch/util/numbers';
+
+const Hashtag = ({ hashtag }) => (
+  <div className='trends__item'>
+    <div className='trends__item__name'>
+      <Link to={`/timelines/tag/${hashtag.get('name')}`}>
+        #<span>{hashtag.get('name')}</span>
+      </Link>
+
+      <FormattedMessage id='trends.count_by_accounts' defaultMessage='{count} {rawCount, plural, one {person} other {people}} talking' values={{ rawCount: hashtag.getIn(['history', 0, 'accounts']), count: <strong>{shortNumberFormat(hashtag.getIn(['history', 0, 'accounts']))}</strong> }} />
+    </div>
+
+    <div className='trends__item__current'>
+      {shortNumberFormat(hashtag.getIn(['history', 0, 'uses']))}
+    </div>
+
+    <div className='trends__item__sparkline'>
+      <Sparklines width={50} height={28} data={hashtag.get('history').reverse().map(day => day.get('uses')).toArray()}>
+        <SparklinesCurve style={{ fill: 'none' }} />
+      </Sparklines>
+    </div>
+  </div>
+);
+
+Hashtag.propTypes = {
+  hashtag: ImmutablePropTypes.map.isRequired,
+};
+
+export default Hashtag;
diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js
index da1f74e6d..a87721ef8 100644
--- a/app/javascript/flavours/glitch/components/status.js
+++ b/app/javascript/flavours/glitch/components/status.js
@@ -528,6 +528,7 @@ export default class Status extends ImmutablePureComponent {
               {...other}
               status={status}
               account={status.get('account')}
+              showReplyCount={settings.get('show_reply_count')}
             />
           ) : null}
           {notification ? (
diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js
index cd9a2ac67..8a840030a 100644
--- a/app/javascript/flavours/glitch/components/status_action_bar.js
+++ b/app/javascript/flavours/glitch/components/status_action_bar.js
@@ -33,6 +33,16 @@ const messages = defineMessages({
   embed: { id: 'status.embed', defaultMessage: 'Embed' },
 });
 
+const obfuscatedCount = count => {
+  if (count < 0) {
+    return 0;
+  } else if (count <= 1) {
+    return count;
+  } else {
+    return '1+';
+  }
+};
+
 @injectIntl
 export default class StatusActionBar extends ImmutablePureComponent {
 
@@ -56,6 +66,7 @@ export default class StatusActionBar extends ImmutablePureComponent {
     onPin: PropTypes.func,
     onBookmark: PropTypes.func,
     withDismiss: PropTypes.bool,
+    showReplyCount: PropTypes.bool,
     intl: PropTypes.object.isRequired,
   };
 
@@ -63,6 +74,7 @@ export default class StatusActionBar extends ImmutablePureComponent {
   // evaluate to false. See react-immutable-pure-component for usage.
   updateOnProps = [
     'status',
+    'showReplyCount',
     'withDismiss',
   ]
 
@@ -134,7 +146,7 @@ export default class StatusActionBar extends ImmutablePureComponent {
   }
 
   render () {
-    const { status, intl, withDismiss } = this.props;
+    const { status, intl, withDismiss, showReplyCount } = this.props;
 
     const mutingConversation = status.get('muted');
     const anonymousAccess    = !me;
@@ -188,9 +200,27 @@ export default class StatusActionBar extends ImmutablePureComponent {
       <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.share)} icon='share-alt' onClick={this.handleShareClick} />
     );
 
+    let replyButton = (
+      <IconButton
+        className='status__action-bar-button'
+        disabled={anonymousAccess}
+        title={replyTitle}
+        icon={replyIcon}
+        onClick={this.handleReplyClick}
+      />
+    );
+    if (showReplyCount) {
+      replyButton = (
+        <div className='status__action-bar__counter'>
+          {replyButton}
+          <span className='status__action-bar__counter__label' >{obfuscatedCount(status.get('replies_count'))}</span>
+        </div>
+      );
+    }
+
     return (
       <div className='status__action-bar'>
-        <IconButton className='status__action-bar-button' disabled={anonymousAccess} title={replyTitle} icon={replyIcon} onClick={this.handleReplyClick} />
+        {replyButton}
         <IconButton className='status__action-bar-button' disabled={reblogDisabled} active={status.get('reblogged')} pressed={status.get('reblogged')} title={reblogDisabled ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(reblogMessage)} icon={reblogIcon} onClick={this.handleReblogClick} />
         <IconButton className='status__action-bar-button star-icon' disabled={anonymousAccess} animate active={status.get('favourited')} pressed={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} />
         {shareButton}
diff --git a/app/javascript/flavours/glitch/features/drawer/results/index.js b/app/javascript/flavours/glitch/features/drawer/results/index.js
index 23dc0e3cf..ac7a14ef4 100644
--- a/app/javascript/flavours/glitch/features/drawer/results/index.js
+++ b/app/javascript/flavours/glitch/features/drawer/results/index.js
@@ -12,6 +12,7 @@ import { Link } from 'react-router-dom';
 //  Components.
 import AccountContainer from 'flavours/glitch/containers/account_container';
 import StatusContainer from 'flavours/glitch/containers/status_container';
+import Hashtag from 'flavours/glitch/components/hashtag';
 
 //  Utils.
 import Motion from 'flavours/glitch/util/optional_motion';
@@ -98,15 +99,7 @@ export default function DrawerResults ({
             <section>
               <h5><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></h5>
 
-              {hashtags.map(
-                hashtag => (
-                  <Link
-                    className='hashtag'
-                    key={hashtag}
-                    to={`/timelines/tag/${hashtag}`}
-                  >#{hashtag}</Link>
-                )
-              )}
+              {hashtags.map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)}
             </section>
           ) : null}
         </div>
diff --git a/app/javascript/flavours/glitch/features/getting_started_misc/index.js b/app/javascript/flavours/glitch/features/getting_started_misc/index.js
index b67e6f97f..ee4452472 100644
--- a/app/javascript/flavours/glitch/features/getting_started_misc/index.js
+++ b/app/javascript/flavours/glitch/features/getting_started_misc/index.js
@@ -21,6 +21,7 @@ const messages = defineMessages({
   pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' },
   info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' },
   keyboard_shortcuts: { id: 'navigation_bar.keyboard_shortcuts', defaultMessage: 'Keyboard shortcuts' },
+  featured_users: { id: 'navigation_bar.featured_users', defaultMessage: 'Featured users' },
 });
 
 @connect()
@@ -33,27 +34,33 @@ export default class gettingStartedMisc extends ImmutablePureComponent {
   };
 
   openOnboardingModal = (e) => {
-    e.preventDefault();
     this.props.dispatch(openModal('ONBOARDING'));
   }
 
+  openFeaturedAccountsModal = (e) => {
+    this.props.dispatch(openModal('PINNED_ACCOUNTS_EDITOR'));
+  }
+
   render () {
     const { intl } = this.props;
 
+    let i = 1;
+
     return (
       <Column icon='ellipsis-h' heading={intl.formatMessage(messages.heading)}>
         <ColumnBackButtonSlim />
 
         <div className='scrollable'>
           <ColumnSubheading text={intl.formatMessage(messages.subheading)} />
-          <ColumnLink key='19' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />
-          <ColumnLink key='20' icon='thumb-tack' text={intl.formatMessage(messages.pins)} to='/pinned' />
-          <ColumnLink key='21' icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />
-          <ColumnLink key='22' icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />
-          <ColumnLink icon='minus-circle' text={intl.formatMessage(messages.domain_blocks)} to='/domain_blocks' />
-          <ColumnLink key='23' icon='question' text={intl.formatMessage(messages.keyboard_shortcuts)} to='/keyboard-shortcuts' />
-          <ColumnLink icon='book' text={intl.formatMessage(messages.info)} href='/about/more' />
-          <ColumnLink icon='hand-o-right' text={intl.formatMessage(messages.show_me_around)} onClick={this.openOnboardingModal} />
+          <ColumnLink key='{i++}' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />
+          <ColumnLink key='{i++}' icon='thumb-tack' text={intl.formatMessage(messages.pins)} to='/pinned' />
+          <ColumnLink key='{i++}' icon='users' text={intl.formatMessage(messages.featured_users)} onClick={this.openFeaturedAccountsModal} />
+          <ColumnLink key='{i++}' icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />
+          <ColumnLink key='{i++}' icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />
+          <ColumnLink key='{i++}' icon='minus-circle' text={intl.formatMessage(messages.domain_blocks)} to='/domain_blocks' />
+          <ColumnLink key='{i++}' icon='question' text={intl.formatMessage(messages.keyboard_shortcuts)} to='/keyboard-shortcuts' />
+          <ColumnLink key='{i++}' icon='book' text={intl.formatMessage(messages.info)} href='/about/more' />
+          <ColumnLink key='{i++}' icon='hand-o-right' text={intl.formatMessage(messages.show_me_around)} onClick={this.openOnboardingModal} />
         </div>
       </Column>
     );
diff --git a/app/javascript/flavours/glitch/features/list_editor/components/account.js b/app/javascript/flavours/glitch/features/list_editor/components/account.js
index f48df759d..71a8b7673 100644
--- a/app/javascript/flavours/glitch/features/list_editor/components/account.js
+++ b/app/javascript/flavours/glitch/features/list_editor/components/account.js
@@ -1,38 +1,17 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import { connect } from 'react-redux';
-import { makeGetAccount } from 'flavours/glitch/selectors';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import Avatar from 'flavours/glitch/components/avatar';
 import DisplayName from 'flavours/glitch/components/display_name';
 import IconButton from 'flavours/glitch/components/icon_button';
-import { defineMessages, injectIntl } from 'react-intl';
-import { removeFromListEditor, addToListEditor } from 'flavours/glitch/actions/lists';
+import { defineMessages } from 'react-intl';
 
 const messages = defineMessages({
   remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
   add: { id: 'lists.account.add', defaultMessage: 'Add to list' },
 });
 
-const makeMapStateToProps = () => {
-  const getAccount = makeGetAccount();
-
-  const mapStateToProps = (state, { accountId, added }) => ({
-    account: getAccount(state, accountId),
-    added: typeof added === 'undefined' ? state.getIn(['listEditor', 'accounts', 'items']).includes(accountId) : added,
-  });
-
-  return mapStateToProps;
-};
-
-const mapDispatchToProps = (dispatch, { accountId }) => ({
-  onRemove: () => dispatch(removeFromListEditor(accountId)),
-  onAdd: () => dispatch(addToListEditor(accountId)),
-});
-
-@connect(makeMapStateToProps, mapDispatchToProps)
-@injectIntl
 export default class Account extends ImmutablePureComponent {
 
   static propTypes = {
diff --git a/app/javascript/flavours/glitch/features/list_editor/components/search.js b/app/javascript/flavours/glitch/features/list_editor/components/search.js
index 45c4d0f2e..280632652 100644
--- a/app/javascript/flavours/glitch/features/list_editor/components/search.js
+++ b/app/javascript/flavours/glitch/features/list_editor/components/search.js
@@ -1,26 +1,12 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import { connect } from 'react-redux';
-import { defineMessages, injectIntl } from 'react-intl';
-import { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from '../../../actions/lists';
+import { defineMessages } from 'react-intl';
 import classNames from 'classnames';
 
 const messages = defineMessages({
   search: { id: 'lists.search', defaultMessage: 'Search among people you follow' },
 });
 
-const mapStateToProps = state => ({
-  value: state.getIn(['listEditor', 'suggestions', 'value']),
-});
-
-const mapDispatchToProps = dispatch => ({
-  onSubmit: value => dispatch(fetchListSuggestions(value)),
-  onClear: () => dispatch(clearListSuggestions()),
-  onChange: value => dispatch(changeListSuggestions(value)),
-});
-
-@connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 export default class Search extends React.PureComponent {
 
   static propTypes = {
diff --git a/app/javascript/flavours/glitch/features/list_editor/containers/account_container.js b/app/javascript/flavours/glitch/features/list_editor/containers/account_container.js
new file mode 100644
index 000000000..782eb42f3
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/list_editor/containers/account_container.js
@@ -0,0 +1,24 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { makeGetAccount } from 'flavours/glitch/selectors';
+import { injectIntl } from 'react-intl';
+import { removeFromListEditor, addToListEditor } from 'flavours/glitch/actions/lists';
+import Account from '../components/account';
+
+const makeMapStateToProps = () => {
+  const getAccount = makeGetAccount();
+
+  const mapStateToProps = (state, { accountId, added }) => ({
+    account: getAccount(state, accountId),
+    added: typeof added === 'undefined' ? state.getIn(['listEditor', 'accounts', 'items']).includes(accountId) : added,
+  });
+
+  return mapStateToProps;
+};
+
+const mapDispatchToProps = (dispatch, { accountId }) => ({
+  onRemove: () => dispatch(removeFromListEditor(accountId)),
+  onAdd: () => dispatch(addToListEditor(accountId)),
+});
+
+export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Account));
diff --git a/app/javascript/flavours/glitch/features/list_editor/containers/search_container.js b/app/javascript/flavours/glitch/features/list_editor/containers/search_container.js
new file mode 100644
index 000000000..5af20efbd
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/list_editor/containers/search_container.js
@@ -0,0 +1,17 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { injectIntl } from 'react-intl';
+import { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from '../../../actions/lists';
+import Search from '../components/search';
+
+const mapStateToProps = state => ({
+  value: state.getIn(['listEditor', 'suggestions', 'value']),
+});
+
+const mapDispatchToProps = dispatch => ({
+  onSubmit: value => dispatch(fetchListSuggestions(value)),
+  onClear: () => dispatch(clearListSuggestions()),
+  onChange: value => dispatch(changeListSuggestions(value)),
+});
+
+export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(Search));
diff --git a/app/javascript/flavours/glitch/features/list_editor/index.js b/app/javascript/flavours/glitch/features/list_editor/index.js
index e6df4755a..b3be3070a 100644
--- a/app/javascript/flavours/glitch/features/list_editor/index.js
+++ b/app/javascript/flavours/glitch/features/list_editor/index.js
@@ -5,8 +5,8 @@ import { connect } from 'react-redux';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import { injectIntl } from 'react-intl';
 import { setupListEditor, clearListSuggestions, resetListEditor } from 'flavours/glitch/actions/lists';
-import Account from './components/account';
-import Search from './components/search';
+import AccountContainer from './containers/account_container';
+import SearchContainer from './containers/search_container';
 import Motion from 'flavours/glitch/util/optional_motion';
 import spring from 'react-motion/lib/spring';
 
@@ -56,11 +56,11 @@ export default class ListEditor extends ImmutablePureComponent {
       <div className='modal-root__modal list-editor'>
         <h4>{title}</h4>
 
-        <Search />
+        <SearchContainer />
 
         <div className='drawer__pager'>
           <div className='drawer__inner list-editor__accounts'>
-            {accountIds.map(accountId => <Account key={accountId} accountId={accountId} added />)}
+            {accountIds.map(accountId => <AccountContainer key={accountId} accountId={accountId} added />)}
           </div>
 
           {showSearch && <div role='button' tabIndex='-1' className='drawer__backdrop' onClick={onClear} />}
@@ -68,7 +68,7 @@ export default class ListEditor extends ImmutablePureComponent {
           <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
             {({ x }) =>
               (<div className='drawer__inner backdrop' style={{ transform: x === 0 ? null : `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}>
-                {searchAccountIds.map(accountId => <Account key={accountId} accountId={accountId} />)}
+                {searchAccountIds.map(accountId => <AccountContainer key={accountId} accountId={accountId} />)}
               </div>)
             }
           </Motion>
diff --git a/app/javascript/flavours/glitch/features/local_settings/page/index.js b/app/javascript/flavours/glitch/features/local_settings/page/index.js
index d3b7b00b5..f88e23c47 100644
--- a/app/javascript/flavours/glitch/features/local_settings/page/index.js
+++ b/app/javascript/flavours/glitch/features/local_settings/page/index.js
@@ -35,34 +35,45 @@ export default class LocalSettingsPage extends React.PureComponent {
         <h1><FormattedMessage id='settings.general' defaultMessage='General' /></h1>
         <LocalSettingsPageItem
           settings={settings}
-          item={['layout']}
-          id='mastodon-settings--layout'
-          options={[
-            { value: 'auto', message: intl.formatMessage(messages.layout_auto) },
-            { value: 'multiple', message: intl.formatMessage(messages.layout_desktop) },
-            { value: 'single', message: intl.formatMessage(messages.layout_mobile) },
-          ]}
+          item={['show_reply_count']}
+          id='mastodon-settings--reply-count'
           onChange={onChange}
         >
-          <FormattedMessage id='settings.layout' defaultMessage='Layout:' />
-        </LocalSettingsPageItem>
-        <LocalSettingsPageItem
-          settings={settings}
-          item={['stretch']}
-          id='mastodon-settings--stretch'
-          onChange={onChange}
-        >
-          <FormattedMessage id='settings.wide_view' defaultMessage='Wide view (Desktop mode only)' />
-        </LocalSettingsPageItem>
-        <LocalSettingsPageItem
-          settings={settings}
-          item={['navbar_under']}
-          id='mastodon-settings--navbar_under'
-          onChange={onChange}
-        >
-          <FormattedMessage id='settings.navbar_under' defaultMessage='Navbar at the bottom (Mobile only)' />
+          <FormattedMessage id='settings.show_reply_counter' defaultMessage='Display an estimate of the reply count' />
         </LocalSettingsPageItem>
         <section>
+          <h2><FormattedMessage id='settings.layout_opts' defaultMessage='Layout options' /></h2>
+          <LocalSettingsPageItem
+            settings={settings}
+            item={['layout']}
+            id='mastodon-settings--layout'
+            options={[
+              { value: 'auto', message: intl.formatMessage(messages.layout_auto) },
+              { value: 'multiple', message: intl.formatMessage(messages.layout_desktop) },
+              { value: 'single', message: intl.formatMessage(messages.layout_mobile) },
+            ]}
+            onChange={onChange}
+          >
+            <FormattedMessage id='settings.layout' defaultMessage='Layout:' />
+          </LocalSettingsPageItem>
+          <LocalSettingsPageItem
+            settings={settings}
+            item={['stretch']}
+            id='mastodon-settings--stretch'
+            onChange={onChange}
+          >
+            <FormattedMessage id='settings.wide_view' defaultMessage='Wide view (Desktop mode only)' />
+          </LocalSettingsPageItem>
+          <LocalSettingsPageItem
+            settings={settings}
+            item={['navbar_under']}
+            id='mastodon-settings--navbar_under'
+            onChange={onChange}
+          >
+            <FormattedMessage id='settings.navbar_under' defaultMessage='Navbar at the bottom (Mobile only)' />
+          </LocalSettingsPageItem>
+        </section>
+        <section>
           <h2><FormattedMessage id='settings.compose_box_opts' defaultMessage='Compose box options' /></h2>
           <LocalSettingsPageItem
             settings={settings}
diff --git a/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/account_container.js b/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/account_container.js
new file mode 100644
index 000000000..149d05c32
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/account_container.js
@@ -0,0 +1,24 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { makeGetAccount } from 'flavours/glitch/selectors';
+import { injectIntl } from 'react-intl';
+import { pinAccount, unpinAccount } from 'flavours/glitch/actions/accounts';
+import Account from 'flavours/glitch/features/list_editor/components/account';
+
+const makeMapStateToProps = () => {
+  const getAccount = makeGetAccount();
+
+  const mapStateToProps = (state, { accountId, added }) => ({
+    account: getAccount(state, accountId),
+    added: typeof added === 'undefined' ? state.getIn(['pinnedAccountsEditor', 'accounts', 'items']).includes(accountId) : added,
+  });
+
+  return mapStateToProps;
+};
+
+const mapDispatchToProps = (dispatch, { accountId }) => ({
+  onRemove: () => dispatch(unpinAccount(accountId)),
+  onAdd: () => dispatch(pinAccount(accountId)),
+});
+
+export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Account));
diff --git a/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/search_container.js b/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/search_container.js
new file mode 100644
index 000000000..5a1efce0a
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/search_container.js
@@ -0,0 +1,21 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { injectIntl } from 'react-intl';
+import {
+  fetchPinnedAccountsSuggestions,
+  clearPinnedAccountsSuggestions,
+  changePinnedAccountsSuggestions
+} from '../../../actions/accounts';
+import Search from 'flavours/glitch/features/list_editor/components/search';
+
+const mapStateToProps = state => ({
+  value: state.getIn(['pinnedAccountsEditor', 'suggestions', 'value']),
+});
+
+const mapDispatchToProps = dispatch => ({
+  onSubmit: value => dispatch(fetchPinnedAccountsSuggestions(value)),
+  onClear: () => dispatch(clearPinnedAccountsSuggestions()),
+  onChange: value => dispatch(changePinnedAccountsSuggestions(value)),
+});
+
+export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(Search));
diff --git a/app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js b/app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js
new file mode 100644
index 000000000..7484e458e
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js
@@ -0,0 +1,78 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { connect } from 'react-redux';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import { injectIntl, FormattedMessage } from 'react-intl';
+import { fetchPinnedAccounts, clearPinnedAccountsSuggestions, resetPinnedAccountsEditor } from 'flavours/glitch/actions/accounts';
+import AccountContainer from './containers/account_container';
+import SearchContainer from './containers/search_container';
+import Motion from 'flavours/glitch/util/optional_motion';
+import spring from 'react-motion/lib/spring';
+
+const mapStateToProps = state => ({
+  accountIds: state.getIn(['pinnedAccountsEditor', 'accounts', 'items']),
+  searchAccountIds: state.getIn(['pinnedAccountsEditor', 'suggestions', 'items']),
+});
+
+const mapDispatchToProps = dispatch => ({
+  onInitialize: () => dispatch(fetchPinnedAccounts()),
+  onClear: () => dispatch(clearPinnedAccountsSuggestions()),
+  onReset: () => dispatch(resetPinnedAccountsEditor()),
+});
+
+@connect(mapStateToProps, mapDispatchToProps)
+@injectIntl
+export default class PinnedAccountsEditor extends ImmutablePureComponent {
+
+  static propTypes = {
+    onClose: PropTypes.func.isRequired,
+    intl: PropTypes.object.isRequired,
+    onInitialize: PropTypes.func.isRequired,
+    onClear: PropTypes.func.isRequired,
+    onReset: PropTypes.func.isRequired,
+    title: PropTypes.string.isRequired,
+    accountIds: ImmutablePropTypes.list.isRequired,
+    searchAccountIds: ImmutablePropTypes.list.isRequired,
+  };
+
+  componentDidMount () {
+    const { onInitialize } = this.props;
+    onInitialize();
+  }
+
+  componentWillUnmount () {
+    const { onReset } = this.props;
+    onReset();
+  }
+
+  render () {
+    const { accountIds, searchAccountIds, onClear } = this.props;
+    const showSearch = searchAccountIds.size > 0;
+
+    return (
+      <div className='modal-root__modal list-editor'>
+        <h4><FormattedMessage id='endorsed_accounts_editor.endorsed_accounts' defaultMessage='Featured accounts' /></h4>
+
+        <SearchContainer />
+
+        <div className='drawer__pager'>
+          <div className='drawer__inner list-editor__accounts'>
+            {accountIds.map(accountId => <AccountContainer key={accountId} accountId={accountId} added />)}
+          </div>
+
+          {showSearch && <div role='button' tabIndex='-1' className='drawer__backdrop' onClick={onClear} />}
+
+          <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
+            {({ x }) =>
+              (<div className='drawer__inner backdrop' style={{ transform: x === 0 ? null : `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}>
+                {searchAccountIds.map(accountId => <AccountContainer key={accountId} accountId={accountId} />)}
+              </div>)
+            }
+          </Motion>
+        </div>
+      </div>
+    );
+  }
+
+}
diff --git a/app/javascript/flavours/glitch/features/ui/components/modal_root.js b/app/javascript/flavours/glitch/features/ui/components/modal_root.js
index 23a7603d8..c9f54804a 100644
--- a/app/javascript/flavours/glitch/features/ui/components/modal_root.js
+++ b/app/javascript/flavours/glitch/features/ui/components/modal_root.js
@@ -19,6 +19,7 @@ import {
   SettingsModal,
   EmbedModal,
   ListEditor,
+  PinnedAccountsEditor,
 } from 'flavours/glitch/util/async-components';
 
 const MODAL_COMPONENTS = {
@@ -36,6 +37,7 @@ const MODAL_COMPONENTS = {
   'EMBED': EmbedModal,
   'LIST_EDITOR': ListEditor,
   'FOCAL_POINT': () => Promise.resolve({ default: FocalPointModal }),
+  'PINNED_ACCOUNTS_EDITOR': PinnedAccountsEditor,
 };
 
 export default class ModalRoot extends React.PureComponent {
diff --git a/app/javascript/flavours/glitch/reducers/accounts.js b/app/javascript/flavours/glitch/reducers/accounts.js
index c38b3cc95..c2f016a87 100644
--- a/app/javascript/flavours/glitch/reducers/accounts.js
+++ b/app/javascript/flavours/glitch/reducers/accounts.js
@@ -6,6 +6,8 @@ import {
   FOLLOWING_EXPAND_SUCCESS,
   FOLLOW_REQUESTS_FETCH_SUCCESS,
   FOLLOW_REQUESTS_EXPAND_SUCCESS,
+  PINNED_ACCOUNTS_FETCH_SUCCESS,
+  PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_READY,
 } from 'flavours/glitch/actions/accounts';
 import {
   BLOCKS_FETCH_SUCCESS,
@@ -141,6 +143,8 @@ export default function accounts(state = initialState, action) {
   case MUTES_EXPAND_SUCCESS:
   case LIST_ACCOUNTS_FETCH_SUCCESS:
   case LIST_EDITOR_SUGGESTIONS_READY:
+  case PINNED_ACCOUNTS_FETCH_SUCCESS:
+  case PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_READY:
     return action.accounts ? normalizeAccounts(state, action.accounts) : state;
   case NOTIFICATIONS_EXPAND_SUCCESS:
   case SEARCH_FETCH_SUCCESS:
diff --git a/app/javascript/flavours/glitch/reducers/index.js b/app/javascript/flavours/glitch/reducers/index.js
index 7b7bc2ca2..218a5ac8f 100644
--- a/app/javascript/flavours/glitch/reducers/index.js
+++ b/app/javascript/flavours/glitch/reducers/index.js
@@ -28,6 +28,7 @@ import custom_emojis from './custom_emojis';
 import lists from './lists';
 import listEditor from './list_editor';
 import filters from './filters';
+import pinnedAccountsEditor from './pinned_accounts_editor';
 
 const reducers = {
   dropdown_menu,
@@ -59,6 +60,7 @@ const reducers = {
   lists,
   listEditor,
   filters,
+  pinnedAccountsEditor,
 };
 
 export default combineReducers(reducers);
diff --git a/app/javascript/flavours/glitch/reducers/local_settings.js b/app/javascript/flavours/glitch/reducers/local_settings.js
index 73d034dbe..51032f345 100644
--- a/app/javascript/flavours/glitch/reducers/local_settings.js
+++ b/app/javascript/flavours/glitch/reducers/local_settings.js
@@ -11,6 +11,7 @@ const initialState = ImmutableMap({
   navbar_under : false,
   side_arm  : 'none',
   side_arm_reply_mode : 'keep',
+  show_reply_count : false,
   collapsed : ImmutableMap({
     enabled     : true,
     auto        : ImmutableMap({
diff --git a/app/javascript/flavours/glitch/reducers/pinned_accounts_editor.js b/app/javascript/flavours/glitch/reducers/pinned_accounts_editor.js
new file mode 100644
index 000000000..267521bb8
--- /dev/null
+++ b/app/javascript/flavours/glitch/reducers/pinned_accounts_editor.js
@@ -0,0 +1,57 @@
+import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
+import {
+  PINNED_ACCOUNTS_EDITOR_RESET,
+  PINNED_ACCOUNTS_FETCH_REQUEST,
+  PINNED_ACCOUNTS_FETCH_SUCCESS,
+  PINNED_ACCOUNTS_FETCH_FAIL,
+  PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_READY,
+  PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CLEAR,
+  PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CHANGE,
+  ACCOUNT_PIN_SUCCESS,
+  ACCOUNT_UNPIN_SUCCESS,
+} from '../actions/accounts';
+
+const initialState = ImmutableMap({
+  accounts: ImmutableMap({
+    items: ImmutableList(),
+    loaded: false,
+    isLoading: false,
+  }),
+
+  suggestions: ImmutableMap({
+    value: '',
+    items: ImmutableList(),
+  }),
+});
+
+export default function listEditorReducer(state = initialState, action) {
+  switch(action.type) {
+  case PINNED_ACCOUNTS_EDITOR_RESET:
+    return initialState;
+  case PINNED_ACCOUNTS_FETCH_REQUEST:
+    return state.setIn(['accounts', 'isLoading'], true);
+  case PINNED_ACCOUNTS_FETCH_FAIL:
+    return state.setIn(['accounts', 'isLoading'], false);
+  case PINNED_ACCOUNTS_FETCH_SUCCESS:
+    return state.update('accounts', accounts => accounts.withMutations(map => {
+      map.set('isLoading', false);
+      map.set('loaded', true);
+      map.set('items', ImmutableList(action.accounts.map(item => item.id)));
+    }));
+  case PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CHANGE:
+    return state.setIn(['suggestions', 'value'], action.value);
+  case PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_READY:
+    return state.setIn(['suggestions', 'items'], ImmutableList(action.accounts.map(item => item.id)));
+  case PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CLEAR:
+    return state.update('suggestions', suggestions => suggestions.withMutations(map => {
+      map.set('items', ImmutableList());
+      map.set('value', '');
+    }));
+  case ACCOUNT_PIN_SUCCESS:
+    return state.updateIn(['accounts', 'items'], list => list.unshift(action.relationship.id));
+  case ACCOUNT_UNPIN_SUCCESS:
+    return state.updateIn(['accounts', 'items'], list => list.filterNot(item => item === action.relationship.id));
+  default:
+    return state;
+  }
+};
diff --git a/app/javascript/flavours/glitch/reducers/search.js b/app/javascript/flavours/glitch/reducers/search.js
index dc6be97e2..9a525bf47 100644
--- a/app/javascript/flavours/glitch/reducers/search.js
+++ b/app/javascript/flavours/glitch/reducers/search.js
@@ -9,7 +9,7 @@ import {
   COMPOSE_REPLY,
   COMPOSE_DIRECT,
 } from 'flavours/glitch/actions/compose';
-import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
+import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
 
 const initialState = ImmutableMap({
   value: '',
@@ -39,7 +39,7 @@ export default function search(state = initialState, action) {
     return state.set('results', ImmutableMap({
       accounts: ImmutableList(action.results.accounts.map(item => item.id)),
       statuses: ImmutableList(action.results.statuses.map(item => item.id)),
-      hashtags: ImmutableList(action.results.hashtags),
+      hashtags: fromJS(action.results.hashtags),
     })).set('submitted', true);
   default:
     return state;
diff --git a/app/javascript/flavours/glitch/styles/components/search.scss b/app/javascript/flavours/glitch/styles/components/search.scss
index 91861ea19..f9e4b5883 100644
--- a/app/javascript/flavours/glitch/styles/components/search.scss
+++ b/app/javascript/flavours/glitch/styles/components/search.scss
@@ -90,16 +90,80 @@
   font-weight: 500;
 }
 
-.search-results__hashtag {
-  display: block;
-  padding: 10px;
-  color: $secondary-text-color;
-  text-decoration: none;
+.trends {
+  &__header {
+    color: $dark-text-color;
+    background: lighten($ui-base-color, 2%);
+    border-bottom: 1px solid darken($ui-base-color, 4%);
+    font-weight: 500;
+    padding: 15px;
+    font-size: 16px;
+    cursor: default;
 
-  &:hover,
-  &:active,
-  &:focus {
-    color: lighten($secondary-text-color, 4%);
-    text-decoration: underline;
+    .fa {
+      display: inline-block;
+      margin-right: 5px;
+    }
+  }
+
+  &__item {
+    display: flex;
+    align-items: center;
+    padding: 15px;
+    border-bottom: 1px solid lighten($ui-base-color, 8%);
+
+    &:last-child {
+      border-bottom: 0;
+    }
+
+    &__name {
+      flex: 1 1 auto;
+      color: $dark-text-color;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+
+      strong {
+        font-weight: 500;
+      }
+
+      a {
+        color: $darker-text-color;
+        text-decoration: none;
+        font-size: 14px;
+        font-weight: 500;
+        display: block;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+
+        &:hover,
+        &:focus,
+        &:active {
+          span {
+            text-decoration: underline;
+          }
+        }
+      }
+    }
+
+    &__current {
+      flex: 0 0 auto;
+      width: 100px;
+      font-size: 24px;
+      line-height: 36px;
+      font-weight: 500;
+      text-align: center;
+      color: $secondary-text-color;
+    }
+
+    &__sparkline {
+      flex: 0 0 auto;
+      width: 50px;
+
+      path {
+        stroke: lighten($highlight-text-color, 6%) !important;
+      }
+    }
   }
 }
diff --git a/app/javascript/flavours/glitch/styles/components/status.scss b/app/javascript/flavours/glitch/styles/components/status.scss
index cd17bb4fa..fbc26ed2a 100644
--- a/app/javascript/flavours/glitch/styles/components/status.scss
+++ b/app/javascript/flavours/glitch/styles/components/status.scss
@@ -417,15 +417,31 @@
   align-items: center;
   display: flex;
   margin-top: 8px;
+
+  &__counter {
+    display: inline-flex;
+    margin-right: 11px;
+    align-items: center;
+
+    .status__action-bar-button {
+      margin-right: 4px;
+    }
+
+    &__label {
+      display: inline-block;
+      width: 14px;
+      font-size: 12px;
+      font-weight: 500;
+      color: $action-button-color;
+    }
+  }
 }
 
 .status__action-bar-button {
-  float: left;
   margin-right: 18px;
 }
 
 .status__action-bar-dropdown {
-  float: left;
   height: 23.15px;
   width: 23.15px;
 }
diff --git a/app/javascript/flavours/glitch/util/async-components.js b/app/javascript/flavours/glitch/util/async-components.js
index 3d6d3d1f4..557ce317e 100644
--- a/app/javascript/flavours/glitch/util/async-components.js
+++ b/app/javascript/flavours/glitch/util/async-components.js
@@ -38,6 +38,10 @@ export function ListEditor () {
   return import(/* webpackChunkName: "flavours/glitch/async/list_editor" */'flavours/glitch/features/list_editor');
 }
 
+export function PinnedAccountsEditor () {
+  return import(/* webpackChunkName: "flavours/glitch/async/pinned_accounts_editor" */'flavours/glitch/features/pinned_accounts_editor');
+}
+
 export function DirectTimeline() {
   return import(/* webpackChunkName: "flavours/glitch/async/direct_timeline" */'flavours/glitch/features/direct_timeline');
 }
diff --git a/app/javascript/flavours/glitch/util/numbers.js b/app/javascript/flavours/glitch/util/numbers.js
new file mode 100644
index 000000000..fdd8269ae
--- /dev/null
+++ b/app/javascript/flavours/glitch/util/numbers.js
@@ -0,0 +1,10 @@
+import React, { Fragment } from 'react';
+import { FormattedNumber } from 'react-intl';
+
+export const shortNumberFormat = number => {
+  if (number < 1000) {
+    return <FormattedNumber value={number} />;
+  } else {
+    return <Fragment><FormattedNumber value={number / 1000} maximumFractionDigits={1} />K</Fragment>;
+  }
+};
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index b14be3434..b38dbcf73 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -7,7 +7,7 @@
   "account.disclaimer_full": "قد لا تعكس المعلومات أدناه الملف الشخصي الكامل للمستخدم.",
   "account.domain_blocked": "النطاق مخفي",
   "account.edit_profile": "تعديل الملف الشخصي",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "إبرازه على الملف الشخصي",
   "account.follow": "تابِع",
   "account.followers": "المتابعون",
   "account.follows": "يتبع",
@@ -27,7 +27,7 @@
   "account.show_reblogs": "عرض ترقيات @{name}",
   "account.unblock": "إلغاء الحظر عن @{name}",
   "account.unblock_domain": "فك حظر {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "إزالة ترويجه مِن الملف الشخصي",
   "account.unfollow": "إلغاء المتابعة",
   "account.unmute": "إلغاء الكتم عن @{name}",
   "account.unmute_notifications": "إلغاء كتم إخطارات @{name}",
@@ -90,7 +90,7 @@
   "confirmations.redraft.message": "هل أنت متأكد من أنك تريد حذف هذا المنشور و إعادة صياغته ؟ سوف تفقد جميع الردود و الترقيات و المفضلة المتصلة به.",
   "confirmations.unfollow.confirm": "إلغاء المتابعة",
   "confirmations.unfollow.message": "متأكد من أنك تريد إلغاء متابعة {name} ؟",
-  "embed.instructions": "يمكنكم إدماج هذه الحالة على موقعكم الإلكتروني عن طريق نسخ الشفرة أدناه.",
+  "embed.instructions": "يمكنكم إدماج هذا المنشور على موقعكم الإلكتروني عن طريق نسخ الشفرة أدناه.",
   "embed.preview": "هكذا ما سوف يبدو عليه :",
   "emoji_button.activity": "الأنشطة",
   "emoji_button.custom": "مخصص",
@@ -109,7 +109,7 @@
   "empty_column.community": "الخط الزمني المحلي فارغ. أكتب شيئا ما للعامة كبداية !",
   "empty_column.direct": "لم تتلق أية رسالة خاصة مباشِرة بعد. سوف يتم عرض الرسائل المباشرة هنا إن قمت بإرسال واحدة أو تلقيت البعض منها.",
   "empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.",
-  "empty_column.home": "إنك لا تتبع بعد أي شخص إلى حد الآن. زر {public} أو استخدام حقل البحث لكي تبدأ على التعرف على مستخدمين آخرين.",
+  "empty_column.home": "إنّ الخيط الزمني لصفحتك الرئيسية فارغ. قم بزيارة {public} أو استخدم حقل البحث لكي تكتشف مستخدمين آخرين.",
   "empty_column.home.public_timeline": "الخيط العام",
   "empty_column.list": "هذه القائمة فارغة مؤقتا و لكن سوف تمتلئ تدريجيا عندما يبدأ الأعضاء المُنتَمين إليها بنشر تبويقات.",
   "empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.",
@@ -162,6 +162,7 @@
   "missing_indicator.label": "تعذر العثور عليه",
   "missing_indicator.sublabel": "تعذر العثور على هذا المورد",
   "mute_modal.hide_notifications": "هل تود إخفاء الإخطارات القادمة من هذا المستخدم ؟",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "الحسابات المحجوبة",
   "navigation_bar.community_timeline": "الخيط العام المحلي",
   "navigation_bar.direct": "الرسائل المباشِرة",
@@ -181,7 +182,7 @@
   "navigation_bar.preferences": "التفضيلات",
   "navigation_bar.public_timeline": "الخيط العام الموحد",
   "navigation_bar.security": "الأمان",
-  "notification.favourite": "{name} أعجب بمنشورك",
+  "notification.favourite": "أُعجِب {name} بمنشورك",
   "notification.follow": "{name} يتابعك",
   "notification.mention": "{name} ذكرك",
   "notification.reblog": "{name} قام بترقية تبويقك",
@@ -272,7 +273,7 @@
   "status.pinned": "تبويق مثبَّت",
   "status.reblog": "رَقِّي",
   "status.reblog_private": "القيام بالترقية إلى الجمهور الأصلي",
-  "status.reblogged_by": "{name} رقى",
+  "status.reblogged_by": "رقّاه {name}",
   "status.redraft": "إزالة و إعادة الصياغة",
   "status.reply": "ردّ",
   "status.replyAll": "رُد على الخيط",
diff --git a/app/javascript/mastodon/locales/ast.json b/app/javascript/mastodon/locales/ast.json
index 1327d0ec2..96e3a14d9 100644
--- a/app/javascript/mastodon/locales/ast.json
+++ b/app/javascript/mastodon/locales/ast.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Not found",
   "missing_indicator.sublabel": "This resource could not be found",
   "mute_modal.hide_notifications": "Hide notifications from this user?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.direct": "Direct messages",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index fa1ba594d..b41045fb8 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Not found",
   "missing_indicator.sublabel": "This resource could not be found",
   "mute_modal.hide_notifications": "Hide notifications from this user?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.direct": "Direct messages",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index 0ffc7509b..7378b703b 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "No trobat",
   "missing_indicator.sublabel": "Aquest recurs no pot ser trobat",
   "mute_modal.hide_notifications": "Amagar notificacions d'aquest usuari?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Usuaris bloquejats",
   "navigation_bar.community_timeline": "Línia de temps Local",
   "navigation_bar.direct": "Missatges directes",
diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json
index feb796577..717397d41 100644
--- a/app/javascript/mastodon/locales/co.json
+++ b/app/javascript/mastodon/locales/co.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Micca trovu",
   "missing_indicator.sublabel": "Ùn era micca pussivule di truvà sta risorsa",
   "mute_modal.hide_notifications": "Piattà nutificazione da st'utilizatore?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Utilizatori bluccati",
   "navigation_bar.community_timeline": "Linea pubblica lucale",
   "navigation_bar.direct": "Missaghji diretti",
diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json
index e93baf750..b71179ca0 100644
--- a/app/javascript/mastodon/locales/cs.json
+++ b/app/javascript/mastodon/locales/cs.json
@@ -7,7 +7,7 @@
   "account.disclaimer_full": "Níže uvedené informace nemusejí zcela odrážet profil uživatele.",
   "account.domain_blocked": "Doména skryta",
   "account.edit_profile": "Upravit profil",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "Představit na profilu",
   "account.follow": "Sleduj",
   "account.followers": "Sledovatelé",
   "account.follows": "Sleduje",
@@ -27,7 +27,7 @@
   "account.show_reblogs": "Zobrazit boosty od uživatele @{name}",
   "account.unblock": "Odblokovat uživatele @{name}",
   "account.unblock_domain": "Odkrýt doménu {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "Nepředstavit na profilu",
   "account.unfollow": "Přestat sledovat",
   "account.unmute": "Přestat ignorovat uživatele @{name}",
   "account.unmute_notifications": "Odtišit oznámení od uživatele @{name}",
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Nenalezeno",
   "missing_indicator.sublabel": "Tento zdroj se nepodařilo najít",
   "mute_modal.hide_notifications": "Skrýt oznámení před tímto uživatelem?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokovaní uživatelé",
   "navigation_bar.community_timeline": "Místní časová osa",
   "navigation_bar.direct": "Přímé zprávy",
@@ -229,11 +230,11 @@
   "privacy.unlisted.short": "Nezobrazované",
   "regeneration_indicator.label": "Načítám…",
   "regeneration_indicator.sublabel": "Váš domovský proud se připravuje!",
-  "relative_time.days": "{number}d",
-  "relative_time.hours": "{number}h",
+  "relative_time.days": "{number} d",
+  "relative_time.hours": "{number} h",
   "relative_time.just_now": "teď",
-  "relative_time.minutes": "{number}m",
-  "relative_time.seconds": "{number}s",
+  "relative_time.minutes": "{number} m",
+  "relative_time.seconds": "{number} s",
   "reply_indicator.cancel": "Zrušit",
   "report.forward": "Přeposlat k {target}",
   "report.forward_hint": "Tento účet je z jiného serveru. Chcete na něj také poslat anonymizovanou kopii?",
diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json
index 10f52465d..4c38b8eb2 100644
--- a/app/javascript/mastodon/locales/da.json
+++ b/app/javascript/mastodon/locales/da.json
@@ -7,27 +7,27 @@
   "account.disclaimer_full": "Nedenstående oplysninger reflekterer ikke nødvendigvis brugerens profil fuldstændigt.",
   "account.domain_blocked": "Domænet er blevet skjult",
   "account.edit_profile": "Rediger profil",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "Fremhæv på profil",
   "account.follow": "Følg",
   "account.followers": "Følgere",
   "account.follows": "Følger",
   "account.follows_you": "Følger dig",
   "account.hide_reblogs": "Skjul fremhævelserne fra @{name}",
-  "account.media": "Multimedier",
+  "account.media": "Medie",
   "account.mention": "Nævn @{name}",
   "account.moved_to": "{name} er flyttet til:",
   "account.mute": "Dæmp @{name}",
   "account.mute_notifications": "Dæmp notifikationer fra @{name}",
   "account.muted": "Dæmpet",
-  "account.posts": "Dyt",
-  "account.posts_with_replies": "Toots og svar",
+  "account.posts": "Trut",
+  "account.posts_with_replies": "Trut samt svar",
   "account.report": "Rapporter @{name}",
   "account.requested": "Afventer godkendelse. Tryk for at annullere følgeanmodning",
   "account.share": "Del @{name}s profil",
   "account.show_reblogs": "Vis fremhævelserne fra @{name}",
   "account.unblock": "Fjern blokeringen af @{name}",
   "account.unblock_domain": "Skjul ikke længere {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "Fremhæv ikke på profil",
   "account.unfollow": "Følg ikke længere",
   "account.unmute": "Fjern dæmpningen af @{name}",
   "account.unmute_notifications": "Fjern dæmpningen af notifikationer fra @{name}",
@@ -83,7 +83,7 @@
   "confirmations.delete_list.confirm": "Slet",
   "confirmations.delete_list.message": "Er du sikker på, du vil slette denne liste?",
   "confirmations.domain_block.confirm": "Skjul helt domæne",
-  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.",
+  "confirmations.domain_block.message": "Er du helt sikker på du vil blokere hele {domain} domænet? I de fleste tilfælde vil få specifikke blokeringer eller dæmpninger være nok og at fortrække. Du vil ikke se indhold fra det domæne hverken på offentlige tidslinjer eller i dine notifikationer. Dine følgere fra det domæne vil blive fjernet.",
   "confirmations.mute.confirm": "Dæmp",
   "confirmations.mute.message": "Er du sikker på, du vil dæmpe {name}?",
   "confirmations.redraft.confirm": "Slet & omskriv",
@@ -103,7 +103,7 @@
   "emoji_button.people": "Mennesker",
   "emoji_button.recent": "Oftest brugt",
   "emoji_button.search": "Søg...",
-  "emoji_button.search_results": "Søgeresultat",
+  "emoji_button.search_results": "Søgeresultater",
   "emoji_button.symbols": "Symboler",
   "emoji_button.travel": "Rejser & steder",
   "empty_column.community": "Den lokale tidslinje er tom. Skriv noget offentligt for at starte lavinen!",
@@ -139,7 +139,7 @@
   "keyboard_shortcuts.hotkey": "Hurtigtast",
   "keyboard_shortcuts.legend": "for at vise denne legende",
   "keyboard_shortcuts.mention": "for at nævne forfatteren",
-  "keyboard_shortcuts.profile": "to open author's profile",
+  "keyboard_shortcuts.profile": "til profil af åben forfatter",
   "keyboard_shortcuts.reply": "for at svare",
   "keyboard_shortcuts.search": "for at fokusere søgningen",
   "keyboard_shortcuts.toggle_hidden": "for at vise/skjule tekst bag CW",
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Ikke fundet",
   "missing_indicator.sublabel": "Denne ressource kunne ikke blive fundet",
   "mute_modal.hide_notifications": "Skjul notifikationer fra denne bruger?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokerede brugere",
   "navigation_bar.community_timeline": "Lokal tidslinje",
   "navigation_bar.direct": "Direkte beskeder",
@@ -215,8 +216,8 @@
   "onboarding.page_six.read_guidelines": "Læs venligst {domain}s {guidelines}!",
   "onboarding.page_six.various_app": "apps til mobilen",
   "onboarding.page_three.profile": "Rediger din profil for at ændre profilbillede, beskrivelse og visningsnavn. Der vil du også finde andre indstillinger.",
-  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
-  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.page_three.search": "Brug søgefeltdet for at finde folk og at kigge på hashtags, så som {illustration} and {introductions}. For at finde en person der ikke er på denne instans, brug deres fulde brugernavn.",
+  "onboarding.page_two.compose": "Skriv opslag fra skrive kolonnen. Du kan uploade billeder, ændre privatlivsindstillinger, og tilføje indholds advarsler med ikoner forneden.",
   "onboarding.skip": "Spring over",
   "privacy.change": "Ændre status privatliv",
   "privacy.direct.long": "Post til kun de nævnte brugere",
@@ -243,7 +244,7 @@
   "report.target": "Anmelder {target}",
   "search.placeholder": "Søg",
   "search_popout.search_format": "Avanceret søgeformat",
-  "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
+  "search_popout.tips.full_text": "Simpel tekst returnerer statusser du har skrevet, favoriseret, fremhævet, eller er blevet nævnt i, lige så vel som matchende brugernavne, visningsnavne, og hashtags.",
   "search_popout.tips.hashtag": "emnetag",
   "search_popout.tips.status": "status",
   "search_popout.tips.text": "Simpelt tekst returnerer passende visningsnavne, brugernavne og hashtags",
@@ -275,7 +276,7 @@
   "status.reblogged_by": "{name} fremhævede",
   "status.redraft": "Slet og omskriv",
   "status.reply": "Svar",
-  "status.replyAll": "Svar tråd",
+  "status.replyAll": "Svar samtale",
   "status.report": "Anmeld @{name}",
   "status.sensitive_toggle": "Tryk for at se",
   "status.sensitive_warning": "Følsomt indhold",
@@ -291,11 +292,11 @@
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Notifikationer",
   "tabs_bar.search": "Søg",
-  "trends.count_by_accounts": "{count} {rawCount, flere, en {person} flere {people}} snakker",
+  "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} snakker",
   "ui.beforeunload": "Din kladde vil gå tabt hvis du forlader Mastodon.",
   "upload_area.title": "Træk og slip for at uploade",
-  "upload_button.label": "Tilføj multimedier",
-  "upload_form.description": "Beskrivelse for de svagtseende",
+  "upload_button.label": "Tilføj medie",
+  "upload_form.description": "Beskriv for de svagtseende",
   "upload_form.focus": "Beskær",
   "upload_form.undo": "Slet",
   "upload_progress.label": "Uploader...",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index 83e675682..3427b322b 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Nicht gefunden",
   "missing_indicator.sublabel": "Die Ressource konnte nicht gefunden werden",
   "mute_modal.hide_notifications": "Benachrichtigungen von diesem Account verbergen?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blockierte Profile",
   "navigation_bar.community_timeline": "Lokale Zeitleiste",
   "navigation_bar.direct": "Direktnachrichten",
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index f034f195a..2d836be90 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -1175,6 +1175,10 @@
         "id": "navigation_bar.info"
       },
       {
+        "defaultMessage": "Mobile apps",
+        "id": "navigation_bar.apps"
+      },
+      {
         "defaultMessage": "Terms of service",
         "id": "getting_started.terms"
       },
diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json
index 1e059b682..76a003f65 100644
--- a/app/javascript/mastodon/locales/el.json
+++ b/app/javascript/mastodon/locales/el.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Δε βρέθηκε",
   "missing_indicator.sublabel": "Αδύνατη η εύρεση αυτού του πόρου",
   "mute_modal.hide_notifications": "Απόκρυψη ειδοποιήσεων αυτού του χρήστη;",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Αποκλεισμένοι χρήστες",
   "navigation_bar.community_timeline": "Τοπική ροή",
   "navigation_bar.direct": "Προσωπικά μηνύματα",
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 53429f92c..27b2cb45e 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -166,6 +166,7 @@
   "missing_indicator.label": "Not found",
   "missing_indicator.sublabel": "This resource could not be found",
   "mute_modal.hide_notifications": "Hide notifications from this user?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.direct": "Direct messages",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index 862c2b4d0..f4c316441 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -7,7 +7,7 @@
   "account.disclaimer_full": "Subaj informoj povas reflekti la profilon de la uzanto nekomplete.",
   "account.domain_blocked": "Domajno kaŝita",
   "account.edit_profile": "Redakti profilon",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "Montri en profilo",
   "account.follow": "Sekvi",
   "account.followers": "Sekvantoj",
   "account.follows": "Sekvatoj",
@@ -27,7 +27,7 @@
   "account.show_reblogs": "Montri diskonigojn de @{name}",
   "account.unblock": "Malbloki @{name}",
   "account.unblock_domain": "Malkaŝi {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "Ne montri en profilo",
   "account.unfollow": "Ne plu sekvi",
   "account.unmute": "Malsilentigi @{name}",
   "account.unmute_notifications": "Malsilentigi sciigojn de @{name}",
@@ -139,7 +139,7 @@
   "keyboard_shortcuts.hotkey": "Rapidklavo",
   "keyboard_shortcuts.legend": "por montri ĉi tiun noton",
   "keyboard_shortcuts.mention": "por mencii la aŭtoron",
-  "keyboard_shortcuts.profile": "to open author's profile",
+  "keyboard_shortcuts.profile": "por malfermi la profilon de la aŭtoro",
   "keyboard_shortcuts.reply": "por respondi",
   "keyboard_shortcuts.search": "por fokusigi la serĉilon",
   "keyboard_shortcuts.toggle_hidden": "por montri/kaŝi tekston malantaŭ enhava averto",
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Ne trovita",
   "missing_indicator.sublabel": "Ĉi tiu elemento ne estis trovita",
   "mute_modal.hide_notifications": "Ĉu vi volas kaŝi la sciigojn el ĉi tiu uzanto?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokitaj uzantoj",
   "navigation_bar.community_timeline": "Loka tempolinio",
   "navigation_bar.direct": "Rektaj mesaĝoj",
@@ -169,7 +170,7 @@
   "navigation_bar.domain_blocks": "Kaŝitaj domajnoj",
   "navigation_bar.edit_profile": "Redakti profilon",
   "navigation_bar.favourites": "Stelumoj",
-  "navigation_bar.filters": "Muted words",
+  "navigation_bar.filters": "Silentigitaj vortoj",
   "navigation_bar.follow_requests": "Petoj de sekvado",
   "navigation_bar.info": "Pri ĉi tiu nodo",
   "navigation_bar.keyboard_shortcuts": "Rapidklavoj",
@@ -260,7 +261,7 @@
   "status.direct": "Rekte mesaĝi @{name}",
   "status.embed": "Enkorpigi",
   "status.favourite": "Stelumi",
-  "status.filtered": "Filtered",
+  "status.filtered": "Filtrita",
   "status.load_more": "Ŝargi pli",
   "status.media_hidden": "Aŭdovidaĵo kaŝita",
   "status.mention": "Mencii @{name}",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index 2359f4ba3..b17e1411e 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "No encontrado",
   "missing_indicator.sublabel": "No se encontró este recurso",
   "mute_modal.hide_notifications": "Ocultar notificaciones de este usuario?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Usuarios bloqueados",
   "navigation_bar.community_timeline": "Historia local",
   "navigation_bar.direct": "Direct messages",
diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json
index aab24b231..ec27fe425 100644
--- a/app/javascript/mastodon/locales/eu.json
+++ b/app/javascript/mastodon/locales/eu.json
@@ -7,7 +7,7 @@
   "account.disclaimer_full": "Baliteke beheko informazioak erabiltzailearen profilaren zati bat baino ez erakustea.",
   "account.domain_blocked": "Ezkutatutako domeinua",
   "account.edit_profile": "Aldatu profila",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "Nabarmendu profilean",
   "account.follow": "Jarraitu",
   "account.followers": "Jarraitzaileak",
   "account.follows": "Jarraitzen",
@@ -27,7 +27,7 @@
   "account.show_reblogs": "Erakutsi @{name}(r)en bultzadak",
   "account.unblock": "Desblokeatu @{name}",
   "account.unblock_domain": "Berriz erakutsi {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "Ez nabarmendu profilean",
   "account.unfollow": "Jarraitzeari utzi",
   "account.unmute": "Desmututu @{name}",
   "account.unmute_notifications": "Desmututu @{name}(r)en jakinarazpenak",
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Ez aurkitua",
   "missing_indicator.sublabel": "Baliabide hau ezin izan da aurkitu",
   "mute_modal.hide_notifications": "Ezkutatu erabiltzaile honen jakinarazpenak?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokeatutako erabiltzaileak",
   "navigation_bar.community_timeline": "Denbora-lerro lokala",
   "navigation_bar.direct": "Mezu zuzenak",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index f95c9dc78..03c6bb7ce 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -7,7 +7,7 @@
   "account.disclaimer_full": "اطلاعات زیر ممکن است نمایهٔ این کاربر را به تمامی نشان ندهد.",
   "account.domain_blocked": "دامین پنهان‌شده",
   "account.edit_profile": "ویرایش نمایه",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "نمایش در نمایه",
   "account.follow": "پی بگیرید",
   "account.followers": "پیگیران",
   "account.follows": "پی می‌گیرد",
@@ -27,7 +27,7 @@
   "account.show_reblogs": "نشان‌دادن بازبوق‌های  @{name}",
   "account.unblock": "رفع انسداد @{name}",
   "account.unblock_domain": "رفع پنهان‌سازی از {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "نهفتن از نمایه",
   "account.unfollow": "پایان پیگیری",
   "account.unmute": "باصدا کردن @{name}",
   "account.unmute_notifications": "باصداکردن اعلان‌ها از طرف @{name}",
@@ -139,7 +139,7 @@
   "keyboard_shortcuts.hotkey": "میان‌بر",
   "keyboard_shortcuts.legend": "برای نمایش این راهنما",
   "keyboard_shortcuts.mention": "برای نام‌بردن از نویسنده",
-  "keyboard_shortcuts.profile": "to open author's profile",
+  "keyboard_shortcuts.profile": "گشودن نمایهٔ نویسنده",
   "keyboard_shortcuts.reply": "برای پاسخ‌دادن",
   "keyboard_shortcuts.search": "برای فعال‌کردن جستجو",
   "keyboard_shortcuts.toggle_hidden": "برای نمایش/نهفتن نوشتهٔ پشت هشدار محتوا",
@@ -162,6 +162,7 @@
   "missing_indicator.label": "پیدا نشد",
   "missing_indicator.sublabel": "این منبع پیدا نشد",
   "mute_modal.hide_notifications": "اعلان‌های این کاربر پنهان شود؟",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "کاربران مسدودشده",
   "navigation_bar.community_timeline": "نوشته‌های محلی",
   "navigation_bar.direct": "پیغام‌های خصوصی",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index c092764fc..0861c42fc 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Ei löytynyt",
   "missing_indicator.sublabel": "Tätä resurssia ei löytynyt",
   "mute_modal.hide_notifications": "Piilota tältä käyttäjältä tulevat ilmoitukset?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Estetyt käyttäjät",
   "navigation_bar.community_timeline": "Paikallinen aikajana",
   "navigation_bar.direct": "Viestit",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index a4797f1fc..79ce01c05 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -7,7 +7,7 @@
   "account.disclaimer_full": "Les données ci-dessous peuvent ne pas refléter ce profil dans sa totalité.",
   "account.domain_blocked": "Domaine caché",
   "account.edit_profile": "Modifier le profil",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "Figure sur le profil",
   "account.follow": "Suivre",
   "account.followers": "Abonné⋅e⋅s",
   "account.follows": "Abonnements",
@@ -27,7 +27,7 @@
   "account.show_reblogs": "Afficher les partages de @{name}",
   "account.unblock": "Débloquer",
   "account.unblock_domain": "Ne plus masquer {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "Ne figure pas sur le profil",
   "account.unfollow": "Ne plus suivre",
   "account.unmute": "Ne plus masquer",
   "account.unmute_notifications": "Réactiver les notifications de @{name}",
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Non trouvé",
   "missing_indicator.sublabel": "Ressource introuvable",
   "mute_modal.hide_notifications": "Masquer les notifications de cette personne ?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Comptes bloqués",
   "navigation_bar.community_timeline": "Fil public local",
   "navigation_bar.direct": "Messages directs",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index 6cb399d87..d83b11d74 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -86,8 +86,8 @@
   "confirmations.domain_block.message": "Realmente está segura de que quere bloquear por completo o dominio {domain}? Normalmente é suficiente, e preferible, bloquear de xeito selectivo varios elementos. Non verá contidos de ese dominio en ningunha liña temporal ou nas notificacións. As súas seguidoras en ese dominio serán eliminadas.",
   "confirmations.mute.confirm": "Acalar",
   "confirmations.mute.message": "Está segura de que quere acalar a {name}?",
-  "confirmations.redraft.confirm": "Delete & redraft",
-  "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.",
+  "confirmations.redraft.confirm": "Eliminar e reescribir",
+  "confirmations.redraft.message": "Está segura de querer eliminar este estado e voltalo a escribir? Perderá todas as réplicas, promocións e favoritas da mensaxe.",
   "confirmations.unfollow.confirm": "Deixar de seguir",
   "confirmations.unfollow.message": "Quere deixar de seguir a {name}?",
   "embed.instructions": "Copie o código inferior para incrustar no seu sitio web este estado.",
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Non atopado",
   "missing_indicator.sublabel": "Non se puido atopar o recurso",
   "mute_modal.hide_notifications": "Esconder notificacións deste usuario?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Usuarias bloqueadas",
   "navigation_bar.community_timeline": "Liña temporal local",
   "navigation_bar.direct": "Mensaxes directas",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index ef86c0dfa..d20adce11 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -23,17 +23,17 @@
   "account.posts_with_replies": "Toots with replies",
   "account.report": "לדווח על @{name}",
   "account.requested": "בהמתנה לאישור",
-  "account.share": "לשתף את אודות @{name}",
+  "account.share": "לשתף את הפרופיל של @{name}",
   "account.show_reblogs": "להראות הדהודים מאת @{name}",
   "account.unblock": "הסרת חסימה מעל @{name}",
   "account.unblock_domain": "הסר חסימה מקהילת {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "לא להציג בפרופיל",
   "account.unfollow": "הפסקת מעקב",
   "account.unmute": "הפסקת השתקת @{name}",
   "account.unmute_notifications": "להפסיק הסתרת הודעות מעם @{name}",
-  "account.view_full_profile": "הראה אודות מלאות",
-  "alert.unexpected.message": "An unexpected error occurred.",
-  "alert.unexpected.title": "Oops!",
+  "account.view_full_profile": "הצגת פרופיל מלא",
+  "alert.unexpected.message": "אירעה שגיאה בלתי צפויה.",
+  "alert.unexpected.title": "אופס!",
   "boost_modal.combo": "ניתן להקיש {combo} כדי לדלג בפעם הבאה",
   "bundle_column_error.body": "משהו השתבש בעת הצגת הרכיב הזה.",
   "bundle_column_error.retry": "לנסות שוב",
@@ -162,6 +162,7 @@
   "missing_indicator.label": "לא נמצא",
   "missing_indicator.sublabel": "This resource could not be found",
   "mute_modal.hide_notifications": "להסתיר הודעות מחשבון זה?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "חסימות",
   "navigation_bar.community_timeline": "ציר זמן מקומי",
   "navigation_bar.direct": "Direct messages",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index 2b5adc353..e6ce3359d 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Nije nađen",
   "missing_indicator.sublabel": "This resource could not be found",
   "mute_modal.hide_notifications": "Hide notifications from this user?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokirani korisnici",
   "navigation_bar.community_timeline": "Lokalni timeline",
   "navigation_bar.direct": "Direct messages",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 791be67e3..80c3a1de8 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Nincs találat",
   "missing_indicator.sublabel": "Ezen forrás nem található",
   "mute_modal.hide_notifications": "Értesítések elrejtése ezen felhasználótól?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Tiltott felhasználók",
   "navigation_bar.community_timeline": "Helyi idővonal",
   "navigation_bar.direct": "Direct messages",
diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json
index 1d90c20de..e5ad93fd8 100644
--- a/app/javascript/mastodon/locales/hy.json
+++ b/app/javascript/mastodon/locales/hy.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Չգտնվեց",
   "missing_indicator.sublabel": "This resource could not be found",
   "mute_modal.hide_notifications": "Թաքցնե՞լ ցանուցումներն այս օգտատիրոջից։",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Արգելափակված օգտատերեր",
   "navigation_bar.community_timeline": "Տեղական հոսք",
   "navigation_bar.direct": "Direct messages",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index 6b182bbe9..010d35b73 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Tidak ditemukan",
   "missing_indicator.sublabel": "This resource could not be found",
   "mute_modal.hide_notifications": "Hide notifications from this user?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Pengguna diblokir",
   "navigation_bar.community_timeline": "Linimasa lokal",
   "navigation_bar.direct": "Direct messages",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index 75924d9e1..06e6b02d7 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Ne trovita",
   "missing_indicator.sublabel": "This resource could not be found",
   "mute_modal.hide_notifications": "Hide notifications from this user?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokusita uzeri",
   "navigation_bar.community_timeline": "Lokala tempolineo",
   "navigation_bar.direct": "Direct messages",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index a0e1a1d2d..c9ac57c74 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -139,7 +139,7 @@
   "keyboard_shortcuts.hotkey": "Tasto di scelta rapida",
   "keyboard_shortcuts.legend": "per mostrare questa spiegazione",
   "keyboard_shortcuts.mention": "per menzionare l'autore",
-  "keyboard_shortcuts.profile": "to open author's profile",
+  "keyboard_shortcuts.profile": "per aprire il profilo dell'autore",
   "keyboard_shortcuts.reply": "per rispondere",
   "keyboard_shortcuts.search": "per spostare il focus sulla ricerca",
   "keyboard_shortcuts.toggle_hidden": "per mostrare/nascondere il testo dei CW",
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Non trovato",
   "missing_indicator.sublabel": "Risorsa non trovata",
   "mute_modal.hide_notifications": "Nascondere le notifiche da quest'utente?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Utenti bloccati",
   "navigation_bar.community_timeline": "Timeline locale",
   "navigation_bar.direct": "Messaggi diretti",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index 21bcb42f8..d8ec6e934 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -7,7 +7,7 @@
   "account.disclaimer_full": "以下の情報は不正確な可能性があります。",
   "account.domain_blocked": "ドメイン非表示中",
   "account.edit_profile": "プロフィールを編集",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "プロフィールで紹介する",
   "account.follow": "フォロー",
   "account.followers": "フォロワー",
   "account.follows": "フォロー",
@@ -27,7 +27,7 @@
   "account.show_reblogs": "@{name}さんからのブーストを表示",
   "account.unblock": "@{name}さんのブロックを解除",
   "account.unblock_domain": "{domain}を表示",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "プロフィールから外す",
   "account.unfollow": "フォロー解除",
   "account.unmute": "@{name}さんのミュートを解除",
   "account.unmute_notifications": "@{name}さんからの通知を受け取るようにする",
@@ -166,6 +166,7 @@
   "missing_indicator.label": "見つかりません",
   "missing_indicator.sublabel": "見つかりませんでした",
   "mute_modal.hide_notifications": "このユーザーからの通知を隠しますか?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "ブロックしたユーザー",
   "navigation_bar.community_timeline": "ローカルタイムライン",
   "navigation_bar.direct": "ダイレクトメッセージ",
diff --git a/app/javascript/mastodon/locales/ka.json b/app/javascript/mastodon/locales/ka.json
index 494270b9d..d2a915c60 100644
--- a/app/javascript/mastodon/locales/ka.json
+++ b/app/javascript/mastodon/locales/ka.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "არაა ნაპოვნი",
   "missing_indicator.sublabel": "ამ რესურსის პოვნა ვერ მოხერხდა",
   "mute_modal.hide_notifications": "დავმალოთ შეტყობინებები ამ მომხმარებლისგან?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "დაბლოკილი მომხმარებლები",
   "navigation_bar.community_timeline": "ლოკალური თაიმლაინი",
   "navigation_bar.direct": "პირდაპირი წერილები",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index b7b280591..18150d17c 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "찾을 수 없습니다",
   "missing_indicator.sublabel": "이 리소스를 찾을 수 없었습니다",
   "mute_modal.hide_notifications": "이 사용자로부터의 알림을 뮤트하시겠습니까?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "차단한 사용자",
   "navigation_bar.community_timeline": "로컬 타임라인",
   "navigation_bar.direct": "다이렉트 메시지",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index c19598a21..72ffbdc6e 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -7,7 +7,7 @@
   "account.disclaimer_full": "De informatie hieronder kan mogelijk een incompleet beeld geven van dit gebruikersprofiel.",
   "account.domain_blocked": "Domein verborgen",
   "account.edit_profile": "Profiel bewerken",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "Op profiel weergeven",
   "account.follow": "Volgen",
   "account.followers": "Volgers",
   "account.follows": "Volgt",
@@ -27,7 +27,7 @@
   "account.show_reblogs": "Toon boosts van @{name}",
   "account.unblock": "Deblokkeer @{name}",
   "account.unblock_domain": "{domain} niet langer negeren",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "Niet op profiel weergeven",
   "account.unfollow": "Ontvolgen",
   "account.unmute": "@{name} niet langer negeren",
   "account.unmute_notifications": "@{name} meldingen niet langer negeren",
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Niet gevonden",
   "missing_indicator.sublabel": "Deze hulpbron kan niet gevonden worden",
   "mute_modal.hide_notifications": "Verberg meldingen van deze persoon?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Geblokkeerde gebruikers",
   "navigation_bar.community_timeline": "Lokale tijdlijn",
   "navigation_bar.direct": "Directe berichten",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index d11e3a5a4..3e9612edb 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Ikke funnet",
   "missing_indicator.sublabel": "Denne ressursen ble ikke funnet",
   "mute_modal.hide_notifications": "Skjul varslinger fra denne brukeren?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokkerte brukere",
   "navigation_bar.community_timeline": "Lokal tidslinje",
   "navigation_bar.direct": "Direct messages",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index 1d81594ca..6aaf4cc63 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Pas trobat",
   "missing_indicator.sublabel": "Aquesta ressorsa es pas estada trobada",
   "mute_modal.hide_notifications": "Rescondre las notificacions d’aquesta persona ?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Personas blocadas",
   "navigation_bar.community_timeline": "Flux public local",
   "navigation_bar.direct": "Messatges dirèctes",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index 42371059d..595e5d57b 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -166,6 +166,7 @@
   "missing_indicator.label": "Nie znaleziono",
   "missing_indicator.sublabel": "Nie można odnaleźć tego zasobu",
   "mute_modal.hide_notifications": "Chcesz ukryć powiadomienia od tego użytkownika?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Zablokowani użytkownicy",
   "navigation_bar.community_timeline": "Lokalna oś czasu",
   "navigation_bar.direct": "Wiadomości bezpośrednie",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index 2764fb4c6..bdfdd46e5 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Não encontrado",
   "missing_indicator.sublabel": "Esse recurso não pôde ser encontrado",
   "mute_modal.hide_notifications": "Esconder notificações deste usuário?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Usuários bloqueados",
   "navigation_bar.community_timeline": "Local",
   "navigation_bar.direct": "Mensagens diretas",
diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json
index 94e68db43..b3c4c3ad9 100644
--- a/app/javascript/mastodon/locales/pt.json
+++ b/app/javascript/mastodon/locales/pt.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Não encontrado",
   "missing_indicator.sublabel": "Este recurso não foi encontrado",
   "mute_modal.hide_notifications": "Esconder notificações deste utilizador?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Utilizadores bloqueados",
   "navigation_bar.community_timeline": "Local",
   "navigation_bar.direct": "Direct messages",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index 0e8586903..8ba26a3c8 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Не найдено",
   "missing_indicator.sublabel": "Запрашиваемый ресурс не найден",
   "mute_modal.hide_notifications": "Убрать уведомления от этого пользователя?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Список блокировки",
   "navigation_bar.community_timeline": "Локальная лента",
   "navigation_bar.direct": "Личные сообщения",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index 7554485a3..8c36f866d 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Nenájdené",
   "missing_indicator.sublabel": "Tento zdroj sa nepodarilo nájsť",
   "mute_modal.hide_notifications": "Skryť notifikácie od tohoto užívateľa?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokovaní užívatelia",
   "navigation_bar.community_timeline": "Lokálna časová os",
   "navigation_bar.direct": "Súkromné správy",
diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json
index 8f637143c..01617f790 100644
--- a/app/javascript/mastodon/locales/sl.json
+++ b/app/javascript/mastodon/locales/sl.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Not found",
   "missing_indicator.sublabel": "This resource could not be found",
   "mute_modal.hide_notifications": "Hide notifications from this user?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.direct": "Direct messages",
diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json
index 473b18fa3..96b46077a 100644
--- a/app/javascript/mastodon/locales/sr-Latn.json
+++ b/app/javascript/mastodon/locales/sr-Latn.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Nije pronađeno",
   "missing_indicator.sublabel": "This resource could not be found",
   "mute_modal.hide_notifications": "Sakrij obaveštenja od ovog korisnika?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokirani korisnici",
   "navigation_bar.community_timeline": "Lokalna lajna",
   "navigation_bar.direct": "Direct messages",
diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json
index 4ee47fb2d..681337947 100644
--- a/app/javascript/mastodon/locales/sr.json
+++ b/app/javascript/mastodon/locales/sr.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Није пронађено",
   "missing_indicator.sublabel": "This resource could not be found",
   "mute_modal.hide_notifications": "Сакриј обавештења од овог корисника?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Блокирани корисници",
   "navigation_bar.community_timeline": "Локална лајна",
   "navigation_bar.direct": "Direct messages",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index 0d55fbd82..5c11e72d9 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -3,9 +3,9 @@
   "account.block": "Blockera @{name}",
   "account.block_domain": "Dölj allt från {domain}",
   "account.blocked": "Blockerad",
-  "account.direct": "Direct Message @{name}",
+  "account.direct": "Direktmeddelande @{name}",
   "account.disclaimer_full": "Informationen nedan kan spegla användarens profil ofullständigt.",
-  "account.domain_blocked": "Domän gömd",
+  "account.domain_blocked": "Domän dold",
   "account.edit_profile": "Redigera profil",
   "account.endorse": "Feature on profile",
   "account.follow": "Följ",
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Hittades inte",
   "missing_indicator.sublabel": "Den här resursen kunde inte hittas",
   "mute_modal.hide_notifications": "Dölj notifikationer från denna användare?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blockerade användare",
   "navigation_bar.community_timeline": "Lokal tidslinje",
   "navigation_bar.direct": "Direktmeddelanden",
diff --git a/app/javascript/mastodon/locales/te.json b/app/javascript/mastodon/locales/te.json
index f8228e7ff..8aeebd048 100644
--- a/app/javascript/mastodon/locales/te.json
+++ b/app/javascript/mastodon/locales/te.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "దొరకలేదు",
   "missing_indicator.sublabel": "ఈ వనరు కనుగొనబడలేదు",
   "mute_modal.hide_notifications": "ఈ వినియోగదారు నుండి నోటిఫికేషన్లను దాచాలా?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "బ్లాక్ చేయబడిన వినియోగదారులు",
   "navigation_bar.community_timeline": "స్థానిక కాలక్రమం",
   "navigation_bar.direct": "ప్రత్యక్ష సందేశాలు",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index ce48e7259..5e4995b71 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Not found",
   "missing_indicator.sublabel": "This resource could not be found",
   "mute_modal.hide_notifications": "Hide notifications from this user?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.direct": "Direct messages",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index 032a82a22..be7ad47e4 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Bulunamadı",
   "missing_indicator.sublabel": "This resource could not be found",
   "mute_modal.hide_notifications": "Hide notifications from this user?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Engellenen kullanıcılar",
   "navigation_bar.community_timeline": "Yerel zaman tüneli",
   "navigation_bar.direct": "Direct messages",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index cca98840b..5f7e5c266 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "Не знайдено",
   "missing_indicator.sublabel": "Ресурс не знайдений",
   "mute_modal.hide_notifications": "Приховати сповіщення від користувача?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Заблоковані користувачі",
   "navigation_bar.community_timeline": "Локальна стрічка",
   "navigation_bar.direct": "Прямі повідомлення",
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index dd4ec4adc..378a1a45b 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "找不到内容",
   "missing_indicator.sublabel": "无法找到此资源",
   "mute_modal.hide_notifications": "同时隐藏来自这个用户的通知",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "已屏蔽的用户",
   "navigation_bar.community_timeline": "本站时间轴",
   "navigation_bar.direct": "私信",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index 70442cfe8..f0718468a 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "找不到內容",
   "missing_indicator.sublabel": "無法找到內容",
   "mute_modal.hide_notifications": "隱藏來自這用戶的通知嗎?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "被你封鎖的用戶",
   "navigation_bar.community_timeline": "本站時間軸",
   "navigation_bar.direct": "個人訊息",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index 841f79dd3..ca5919e30 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -162,6 +162,7 @@
   "missing_indicator.label": "找不到",
   "missing_indicator.sublabel": "找不到此資源",
   "mute_modal.hide_notifications": "隱藏來自這個使用者的通知?",
+  "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "封鎖的使用者",
   "navigation_bar.community_timeline": "本地時間軸",
   "navigation_bar.direct": "私訊",
diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss
index 22dbfa8cf..020be5ad2 100644
--- a/app/javascript/styles/mastodon/forms.scss
+++ b/app/javascript/styles/mastodon/forms.scss
@@ -50,6 +50,12 @@ code {
         color: $highlight-text-color;
       }
     }
+
+    code {
+      border-radius: 3px;
+      padding: 0.2em 0.4em;
+      background: darken($ui-base-color, 12%);
+    }
   }
 
   .card {