diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb
index ec00f0c0668014ff04905d598958262527d5f207..c210d336a3817673ae78f34a5cf7f6abb5266ffb 100644
--- a/app/controllers/settings/preferences_controller.rb
+++ b/app/controllers/settings/preferences_controller.rb
@@ -59,6 +59,9 @@ class Settings::PreferencesController < Settings::BaseController
       :setting_use_pending_items,
       :setting_trends,
       :setting_crop_images,
+      :setting_show_follow_button_on_timeline,
+      :setting_show_subscribe_button_on_timeline,
+      :setting_show_target,
       notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account trending_tag),
       interactions: %i(must_be_follower must_be_following must_be_following_dm)
     )
diff --git a/app/javascript/mastodon/actions/bookmarks.js b/app/javascript/mastodon/actions/bookmarks.js
index 544ed2ff2244553b9e39e1ef8133a7fc3681a973..b3e0a2ab4b992517d13c5345610a21b11a5fb21b 100644
--- a/app/javascript/mastodon/actions/bookmarks.js
+++ b/app/javascript/mastodon/actions/bookmarks.js
@@ -1,5 +1,7 @@
+import { fetchRelationships } from './accounts';
 import api, { getLinks } from '../api';
 import { importFetchedStatuses } from './importer';
+import { uniq } from '../utils/uniq';
 
 export const BOOKMARKED_STATUSES_FETCH_REQUEST = 'BOOKMARKED_STATUSES_FETCH_REQUEST';
 export const BOOKMARKED_STATUSES_FETCH_SUCCESS = 'BOOKMARKED_STATUSES_FETCH_SUCCESS';
@@ -20,6 +22,7 @@ export function fetchBookmarkedStatuses() {
     api(getState).get('/api/v1/bookmarks').then(response => {
       const next = getLinks(response).refs.find(link => link.rel === 'next');
       dispatch(importFetchedStatuses(response.data));
+      dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id))));
       dispatch(fetchBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
     }).catch(error => {
       dispatch(fetchBookmarkedStatusesFail(error));
@@ -61,6 +64,7 @@ export function expandBookmarkedStatuses() {
     api(getState).get(url).then(response => {
       const next = getLinks(response).refs.find(link => link.rel === 'next');
       dispatch(importFetchedStatuses(response.data));
+      dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id))));
       dispatch(expandBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
     }).catch(error => {
       dispatch(expandBookmarkedStatusesFail(error));
diff --git a/app/javascript/mastodon/actions/favourites.js b/app/javascript/mastodon/actions/favourites.js
index 9448b1efe7eae93a64212d91cadde348cb7f086f..9b28ac4c4647ba0c8489be19013e72ecff5c9464 100644
--- a/app/javascript/mastodon/actions/favourites.js
+++ b/app/javascript/mastodon/actions/favourites.js
@@ -1,5 +1,7 @@
+import { fetchRelationships } from './accounts';
 import api, { getLinks } from '../api';
 import { importFetchedStatuses } from './importer';
+import { uniq } from '../utils/uniq';
 
 export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST';
 export const FAVOURITED_STATUSES_FETCH_SUCCESS = 'FAVOURITED_STATUSES_FETCH_SUCCESS';
@@ -20,6 +22,7 @@ export function fetchFavouritedStatuses() {
     api(getState).get('/api/v1/favourites').then(response => {
       const next = getLinks(response).refs.find(link => link.rel === 'next');
       dispatch(importFetchedStatuses(response.data));
+      dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id))));
       dispatch(fetchFavouritedStatusesSuccess(response.data, next ? next.uri : null));
     }).catch(error => {
       dispatch(fetchFavouritedStatusesFail(error));
@@ -64,6 +67,7 @@ export function expandFavouritedStatuses() {
     api(getState).get(url).then(response => {
       const next = getLinks(response).refs.find(link => link.rel === 'next');
       dispatch(importFetchedStatuses(response.data));
+      dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id))));
       dispatch(expandFavouritedStatusesSuccess(response.data, next ? next.uri : null));
     }).catch(error => {
       dispatch(expandFavouritedStatusesFail(error));
diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js
index 798f9b37ea5182a6022f08d3b75ff68d21e77d54..c6ac071246840d717eb540802e39aa6fc295f334 100644
--- a/app/javascript/mastodon/actions/notifications.js
+++ b/app/javascript/mastodon/actions/notifications.js
@@ -38,7 +38,7 @@ defineMessages({
 });
 
 const fetchRelatedRelationships = (dispatch, notifications) => {
-  const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.account.id);
+  const accountIds = notifications.map(item => item.account.id);
 
   if (accountIds.length > 0) {
     dispatch(fetchRelationships(accountIds));
diff --git a/app/javascript/mastodon/actions/timelines.js b/app/javascript/mastodon/actions/timelines.js
index ee54a082da9efb3c6ca2666e695f928913bca065..baf106207a347d7fb50df16fad621f1e23ece44f 100644
--- a/app/javascript/mastodon/actions/timelines.js
+++ b/app/javascript/mastodon/actions/timelines.js
@@ -1,8 +1,10 @@
+import { fetchRelationships } from './accounts';
 import { importFetchedStatus, importFetchedStatuses } from './importer';
 import api, { getLinks } from 'mastodon/api';
 import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
 import compareId from 'mastodon/compare_id';
 import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
+import { uniq } from '../utils/uniq';
 
 export const TIMELINE_UPDATE  = 'TIMELINE_UPDATE';
 export const TIMELINE_DELETE  = 'TIMELINE_DELETE';
@@ -29,6 +31,7 @@ export function updateTimeline(timeline, status, accept) {
     }
 
     dispatch(importFetchedStatus(status));
+    dispatch(fetchRelationships([status.reblog ? status.reblog.account.id : status.account.id]));
 
     dispatch({
       type: TIMELINE_UPDATE,
@@ -97,6 +100,7 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
     api(getState).get(path, { params }).then(response => {
       const next = getLinks(response).refs.find(link => link.rel === 'next');
       dispatch(importFetchedStatuses(response.data));
+      dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id))));
       dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.status === 206, isLoadingRecent, isLoadingMore, isLoadingRecent && preferPendingItems));
       done();
     }).catch(error => {
diff --git a/app/javascript/mastodon/components/account_action_bar.js b/app/javascript/mastodon/components/account_action_bar.js
new file mode 100644
index 0000000000000000000000000000000000000000..3719d2b499da0ddb8605a170a857b72d90536b29
--- /dev/null
+++ b/app/javascript/mastodon/components/account_action_bar.js
@@ -0,0 +1,73 @@
+import React from 'react';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import PropTypes from 'prop-types';
+import IconButton from './icon_button';
+import { defineMessages, injectIntl } from 'react-intl';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import { me, show_follow_button_on_timeline, show_subscribe_button_on_timeline } from '../initial_state';
+
+const messages = defineMessages({
+  follow: { id: 'account.follow', defaultMessage: 'Follow' },
+  unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
+  unsubscribe: { id: 'account.unsubscribe', defaultMessage: 'Unsubscribe' },
+  subscribe: { id: 'account.subscribe', defaultMessage: 'Subscribe' },
+  requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' },
+});
+
+export default @injectIntl
+class AccountActionBar extends ImmutablePureComponent {
+
+  static propTypes = {
+    account: ImmutablePropTypes.map.isRequired,
+    onFollow: PropTypes.func.isRequired,
+    onSubscribe: PropTypes.func.isRequired,
+    intl: PropTypes.object.isRequired,
+  };
+
+  updateOnProps = [
+    'account',
+  ]
+
+  handleFollow = () => {
+    this.props.onFollow(this.props.account);
+  }
+
+  handleSubscribe = () => {
+    this.props.onSubscribe(this.props.account);
+  }
+
+  render () {
+    const { account, intl } = this.props;
+
+    if (!account || (!show_follow_button_on_timeline && !show_subscribe_button_on_timeline)) {
+      return <div />;
+    }
+
+    let buttons, following_buttons, subscribing_buttons;
+
+    if (account.get('id') !== me && account.get('relationship', null) !== null) {
+      const following   = account.getIn(['relationship', 'following']);
+      const subscribing = account.getIn(['relationship', 'subscribing']);
+      const requested   = account.getIn(['relationship', 'requested']);
+
+      if (show_subscribe_button_on_timeline && (!account.get('moved') || subscribing)) {
+        subscribing_buttons = <IconButton icon='rss-square' title={intl.formatMessage(subscribing ? messages.unsubscribe : messages.subscribe)} onClick={this.handleSubscribe} active={subscribing} />;
+      }
+      if (show_follow_button_on_timeline && (!account.get('moved') || following)) {
+        if (requested) {
+          following_buttons = <IconButton disabled icon='hourglass' title={intl.formatMessage(messages.requested)} />;
+        } else {
+          following_buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />;
+        }
+      }
+      buttons = <span>{subscribing_buttons}{following_buttons}</span>
+    }
+
+    return (
+      <div className='account__action-bar'>
+        {buttons}
+      </div>
+    );
+  }
+
+}
diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js
index 2177f0cfabe0b5c189d7eee3f24e66f059c0cc5e..6f763b46e6e5dcdc385e58b0f5339363cdffbf30 100644
--- a/app/javascript/mastodon/components/status.js
+++ b/app/javascript/mastodon/components/status.js
@@ -8,6 +8,7 @@ import RelativeTimestamp from './relative_timestamp';
 import DisplayName from './display_name';
 import StatusContent from './status_content';
 import StatusActionBar from './status_action_bar';
+import AccountActionBar from './account_action_bar';
 import AttachmentList from './attachment_list';
 import Card from '../features/status/components/card';
 import { injectIntl, FormattedMessage } from 'react-intl';
@@ -78,6 +79,8 @@ class Status extends ImmutablePureComponent {
     onToggleHidden: PropTypes.func,
     onToggleCollapsed: PropTypes.func,
     onQuoteToggleHidden: PropTypes.func,
+    onFollow: PropTypes.func.isRequired,
+    onSubscribe: PropTypes.func.isRequired,
     muted: PropTypes.bool,
     hidden: PropTypes.bool,
     unread: PropTypes.bool,
@@ -274,6 +277,14 @@ class Status extends ImmutablePureComponent {
     return status.get('quote');
   }
 
+  handleFollow = () => {
+    this.props.onFollow(this._properStatus().get('account'));
+  }
+
+  handleSubscribe = () => {
+    this.props.onSubscribe(this._properStatus().get('account'));
+  }
+
   render () {
     let media = null;
     let statusAvatar, prepend, rebloggedByText, unlistedQuoteText;
@@ -487,6 +498,7 @@ class Status extends ImmutablePureComponent {
           {prepend}
 
           <div className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), muted: this.props.muted, read: unread === false })} data-id={status.get('id')}>
+            <AccountActionBar account={status.get('account')} {...other} />
             <div className='status__expand' onClick={this.handleExpandClick} role='presentation' />
             <div className='status__info'>
               <a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener noreferrer'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.js
index adb7a4e8b4b81b4cecb529515d2259bea04f228b..57a8b7d3da1ecde37bec898994f6d81e427e5030 100644
--- a/app/javascript/mastodon/containers/status_container.js
+++ b/app/javascript/mastodon/containers/status_container.js
@@ -29,6 +29,10 @@ import {
   revealQuote,
 } from '../actions/statuses';
 import {
+  followAccount,
+  unfollowAccount,
+  subscribeAccount,
+  unsubscribeAccount,
   unmuteAccount,
   unblockAccount,
 } from '../actions/accounts';
@@ -36,12 +40,13 @@ import {
   blockDomain,
   unblockDomain,
 } from '../actions/domain_blocks';
+
 import { initMuteModal } from '../actions/mutes';
 import { initBlockModal } from '../actions/blocks';
 import { initReport } from '../actions/reports';
 import { openModal } from '../actions/modal';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import { boostModal, deleteModal } from '../initial_state';
+import { boostModal, deleteModal, unfollowModal, unsubscribeModal } from '../initial_state';
 import { showAlertForError } from '../actions/alerts';
 
 const messages = defineMessages({
@@ -52,6 +57,8 @@ const messages = defineMessages({
   replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
   replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
   blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
+  unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
+  unsubscribeConfirm: { id: 'confirmations.unsubscribe.confirm', defaultMessage: 'Unsubscribe' },
 });
 
 const makeMapStateToProps = () => {
@@ -222,6 +229,37 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
     }
   },
 
+  onFollow (account) {
+    if (account.getIn(['relationship', 'following']) || account.getIn(['relationship', 'requested'])) {
+      if (unfollowModal) {
+        dispatch(openModal('CONFIRM', {
+          message: <FormattedMessage id='confirmations.unfollow.message' defaultMessage='Are you sure you want to unfollow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
+          confirm: intl.formatMessage(messages.unfollowConfirm),
+          onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
+        }));
+      } else {
+        dispatch(unfollowAccount(account.get('id')));
+      }
+    } else {
+      dispatch(followAccount(account.get('id')));
+    }
+  },
+
+  onSubscribe (account) {
+    if (account.getIn(['relationship', 'subscribing'])) {
+      if (unsubscribeModal) {
+        dispatch(openModal('CONFIRM', {
+          message: <FormattedMessage id='confirmations.unsubscribe.message' defaultMessage='Are you sure you want to unsubscribe {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
+          confirm: intl.formatMessage(messages.unsubscribeConfirm),
+          onConfirm: () => dispatch(unsubscribeAccount(account.get('id'))),
+        }));
+      } else {
+        dispatch(unsubscribeAccount(account.get('id')));
+      }
+    } else {
+      dispatch(subscribeAccount(account.get('id')));
+    }
+  },
 });
 
 export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Status));
diff --git a/app/javascript/mastodon/initial_state.js b/app/javascript/mastodon/initial_state.js
index e724b7959221210184c946d2571f2b70d58d9a8d..2c55c7ba00e96f7bc3d2cd4e503c0f32d2aee037 100644
--- a/app/javascript/mastodon/initial_state.js
+++ b/app/javascript/mastodon/initial_state.js
@@ -26,5 +26,8 @@ export const usePendingItems = getMeta('use_pending_items');
 export const showTrends = getMeta('trends');
 export const title = getMeta('title');
 export const cropImages = getMeta('crop_images');
+export const show_follow_button_on_timeline = getMeta('show_follow_button_on_timeline');
+export const show_subscribe_button_on_timeline = getMeta('show_subscribe_button_on_timeline');
+export const show_target = getMeta('show_target');
 
 export default initialState;
diff --git a/app/javascript/mastodon/selectors/index.js b/app/javascript/mastodon/selectors/index.js
index 6f1ce9602a4986beee09fa21bbc5c5ad60f8771f..01d914b923ce27eef0aaf8790d233109d9075923 100644
--- a/app/javascript/mastodon/selectors/index.js
+++ b/app/javascript/mastodon/selectors/index.js
@@ -89,15 +89,28 @@ export const makeGetStatus = () => {
       (state, { id }) => state.getIn(['statuses', state.getIn(['statuses', id, 'reblog'])]),
       (state, { id }) => state.getIn(['accounts', state.getIn(['statuses', id, 'account'])]),
       (state, { id }) => state.getIn(['accounts', state.getIn(['statuses', state.getIn(['statuses', id, 'reblog']), 'account'])]),
+      (state, { id }) => state.getIn(['relationships', state.getIn(['statuses', id, 'account'])]),
+      (state, { id }) => state.getIn(['relationships', state.getIn(['statuses', state.getIn(['statuses', id, 'reblog']), 'account'])]),
+      (state, { id }) => state.getIn(['accounts', state.getIn(['accounts', state.getIn(['statuses', id, 'account']), 'moved'])]),
+      (state, { id }) => state.getIn(['accounts', state.getIn(['accounts', state.getIn(['statuses', state.getIn(['statuses', id, 'reblog']), 'account']), 'moved'])]),
       getFiltersRegex,
     ],
 
-    (statusBase, statusReblog, accountBase, accountReblog, filtersRegex) => {
+    (statusBase, statusReblog, accountBase, accountReblog, relationship, reblogRelationship, moved, reblogMoved, filtersRegex) => {
       if (!statusBase) {
         return null;
       }
 
+      accountBase = accountBase.withMutations(map => {
+        map.set('relationship', relationship);
+        map.set('moved', moved);
+      });
+
       if (statusReblog) {
+        accountReblog = accountReblog.withMutations(map => {
+          map.set('relationship', reblogRelationship);
+          map.set('moved', reblogMoved);
+        });
         statusReblog = statusReblog.set('account', accountReblog);
       } else {
         statusReblog = null;
diff --git a/app/javascript/mastodon/utils/uniq.js b/app/javascript/mastodon/utils/uniq.js
new file mode 100644
index 0000000000000000000000000000000000000000..00f1804a19e07d60eb7540798f7962d57bb31c51
--- /dev/null
+++ b/app/javascript/mastodon/utils/uniq.js
@@ -0,0 +1,3 @@
+export const uniq = array => {
+  return array.filter((x, i, self) => self.indexOf(x) === i)
+};
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 427dad4e3d359505dc895bc7af14ef4042d385ad..b4aec09f023f919204eb52b34b9458dc852eccdb 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -1421,76 +1421,6 @@ a .account__avatar {
   }
 }
 
-.account__action-bar {
-  border-top: 1px solid lighten($ui-base-color, 8%);
-  border-bottom: 1px solid lighten($ui-base-color, 8%);
-  line-height: 36px;
-  overflow: hidden;
-  flex: 0 0 auto;
-  display: flex;
-}
-
-.account__action-bar-dropdown {
-  padding: 10px;
-
-  .icon-button {
-    vertical-align: middle;
-  }
-
-  .dropdown--active {
-    .dropdown__content.dropdown__right {
-      left: 6px;
-      right: initial;
-    }
-
-    &::after {
-      bottom: initial;
-      margin-left: 11px;
-      margin-top: -7px;
-      right: initial;
-    }
-  }
-}
-
-.account__action-bar-links {
-  display: flex;
-  flex: 1 1 auto;
-  line-height: 18px;
-  text-align: center;
-}
-
-.account__action-bar__tab {
-  text-decoration: none;
-  overflow: hidden;
-  flex: 0 1 100%;
-  border-right: 1px solid lighten($ui-base-color, 8%);
-  padding: 10px 0;
-  border-bottom: 4px solid transparent;
-
-  &.active {
-    border-bottom: 4px solid $ui-highlight-color;
-  }
-
-  & > span {
-    display: block;
-    font-size: 12px;
-    color: $darker-text-color;
-  }
-
-  strong {
-    display: block;
-    font-size: 15px;
-    font-weight: 500;
-    color: $primary-text-color;
-
-    @each $lang in $cjk-langs {
-      &:lang(#{$lang}) {
-        font-weight: 700;
-      }
-    }
-  }
-}
-
 .account-authorize {
   padding: 14px 10px;
 
@@ -1577,6 +1507,15 @@ a.account__display-name {
   margin-right: 10px;
 }
 
+.account__action-bar {
+  position: absolute;
+  height: 24px;
+  width: 48px;
+  top: 60px;
+  left: 10px;
+  z-index: 1;
+}
+
 .status__avatar {
   height: 48px;
   left: 10px;
@@ -2331,6 +2270,11 @@ a.account__display-name {
         margin-right: 15px;
       }
     }
+
+    .account__action-bar {
+      top: 67px;
+      left: 15px;
+    }
   }
 }
 
diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb
index 68b388c8e89b0e662aac438c1e2714b4f24e086e..a031378a3b9876f4096822bc628fccb88f2371c3 100644
--- a/app/lib/user_settings_decorator.rb
+++ b/app/lib/user_settings_decorator.rb
@@ -15,30 +15,33 @@ class UserSettingsDecorator
   private
 
   def process_update
-    user.settings['notification_emails'] = merged_notification_emails if change?('notification_emails')
-    user.settings['interactions']        = merged_interactions if change?('interactions')
-    user.settings['default_privacy']     = default_privacy_preference if change?('setting_default_privacy')
-    user.settings['default_sensitive']   = default_sensitive_preference if change?('setting_default_sensitive')
-    user.settings['default_language']    = default_language_preference if change?('setting_default_language')
-    user.settings['unfollow_modal']      = unfollow_modal_preference if change?('setting_unfollow_modal')
-    user.settings['unsubscribe_modal']   = unsubscribe_modal_preference if change?('setting_unsubscribe_modal')
-    user.settings['boost_modal']         = boost_modal_preference if change?('setting_boost_modal')
-    user.settings['delete_modal']        = delete_modal_preference if change?('setting_delete_modal')
-    user.settings['auto_play_gif']       = auto_play_gif_preference if change?('setting_auto_play_gif')
-    user.settings['display_media']       = display_media_preference if change?('setting_display_media')
-    user.settings['expand_spoilers']     = expand_spoilers_preference if change?('setting_expand_spoilers')
-    user.settings['reduce_motion']       = reduce_motion_preference if change?('setting_reduce_motion')
-    user.settings['system_font_ui']      = system_font_ui_preference if change?('setting_system_font_ui')
-    user.settings['noindex']             = noindex_preference if change?('setting_noindex')
-    user.settings['theme']               = theme_preference if change?('setting_theme')
-    user.settings['hide_network']        = hide_network_preference if change?('setting_hide_network')
-    user.settings['aggregate_reblogs']   = aggregate_reblogs_preference if change?('setting_aggregate_reblogs')
-    user.settings['show_application']    = show_application_preference if change?('setting_show_application')
-    user.settings['advanced_layout']     = advanced_layout_preference if change?('setting_advanced_layout')
-    user.settings['use_blurhash']        = use_blurhash_preference if change?('setting_use_blurhash')
-    user.settings['use_pending_items']   = use_pending_items_preference if change?('setting_use_pending_items')
-    user.settings['trends']              = trends_preference if change?('setting_trends')
-    user.settings['crop_images']         = crop_images_preference if change?('setting_crop_images')
+    user.settings['notification_emails']               = merged_notification_emails if change?('notification_emails')
+    user.settings['interactions']                      = merged_interactions if change?('interactions')
+    user.settings['default_privacy']                   = default_privacy_preference if change?('setting_default_privacy')
+    user.settings['default_sensitive']                 = default_sensitive_preference if change?('setting_default_sensitive')
+    user.settings['default_language']                  = default_language_preference if change?('setting_default_language')
+    user.settings['unfollow_modal']                    = unfollow_modal_preference if change?('setting_unfollow_modal')
+    user.settings['unsubscribe_modal']                 = unsubscribe_modal_preference if change?('setting_unsubscribe_modal')
+    user.settings['boost_modal']                       = boost_modal_preference if change?('setting_boost_modal')
+    user.settings['delete_modal']                      = delete_modal_preference if change?('setting_delete_modal')
+    user.settings['auto_play_gif']                     = auto_play_gif_preference if change?('setting_auto_play_gif')
+    user.settings['display_media']                     = display_media_preference if change?('setting_display_media')
+    user.settings['expand_spoilers']                   = expand_spoilers_preference if change?('setting_expand_spoilers')
+    user.settings['reduce_motion']                     = reduce_motion_preference if change?('setting_reduce_motion')
+    user.settings['system_font_ui']                    = system_font_ui_preference if change?('setting_system_font_ui')
+    user.settings['noindex']                           = noindex_preference if change?('setting_noindex')
+    user.settings['theme']                             = theme_preference if change?('setting_theme')
+    user.settings['hide_network']                      = hide_network_preference if change?('setting_hide_network')
+    user.settings['aggregate_reblogs']                 = aggregate_reblogs_preference if change?('setting_aggregate_reblogs')
+    user.settings['show_application']                  = show_application_preference if change?('setting_show_application')
+    user.settings['advanced_layout']                   = advanced_layout_preference if change?('setting_advanced_layout')
+    user.settings['use_blurhash']                      = use_blurhash_preference if change?('setting_use_blurhash')
+    user.settings['use_pending_items']                 = use_pending_items_preference if change?('setting_use_pending_items')
+    user.settings['trends']                            = trends_preference if change?('setting_trends')
+    user.settings['crop_images']                       = crop_images_preference if change?('setting_crop_images')
+    user.settings['show_follow_button_on_timeline']    = show_follow_button_on_timeline_preference if change?('setting_show_follow_button_on_timeline')
+    user.settings['show_subscribe_button_on_timeline'] = show_subscribe_button_on_timeline_preference if change?('setting_show_subscribe_button_on_timeline')
+    user.settings['show_target']                       = show_target_preference if change?('setting_show_target')
   end
 
   def merged_notification_emails
@@ -137,6 +140,18 @@ class UserSettingsDecorator
     boolean_cast_setting 'setting_crop_images'
   end
 
+  def show_follow_button_on_timeline_preference
+    boolean_cast_setting 'setting_show_follow_button_on_timeline'
+  end
+
+  def show_subscribe_button_on_timeline_preference
+    boolean_cast_setting 'setting_show_subscribe_button_on_timeline'
+  end
+
+  def show_target_preference
+    boolean_cast_setting 'setting_show_target'
+  end
+
   def boolean_cast_setting(key)
     ActiveModel::Type::Boolean.new.cast(settings[key])
   end
diff --git a/app/models/user.rb b/app/models/user.rb
index 01723e97d0e9c975071d1017599b48661263c427..e2851860e4bd9755fa7970edeef0859b15e8c020 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -110,6 +110,7 @@ class User < ApplicationRecord
            :reduce_motion, :system_font_ui, :noindex, :theme, :display_media, :hide_network,
            :expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
            :advanced_layout, :use_blurhash, :use_pending_items, :trends, :crop_images,
+           :show_follow_button_on_timeline, :show_subscribe_button_on_timeline, :show_target,
            to: :settings, prefix: :setting, allow_nil: false
 
   attr_reader :invite_code
diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb
index dde09716172295c715494b63c11362f07ac321ca..d43f4ca30a73f13590f39dd4a68f86d10093c16b 100644
--- a/app/serializers/initial_state_serializer.rb
+++ b/app/serializers/initial_state_serializer.rb
@@ -25,22 +25,25 @@ class InitialStateSerializer < ActiveModel::Serializer
     }
 
     if object.current_account
-      store[:me]                = object.current_account.id.to_s
-      store[:unfollow_modal]    = object.current_account.user.setting_unfollow_modal
-      store[:unsubscribe_modal] = object.current_account.user.setting_unsubscribe_modal
-      store[:boost_modal]       = object.current_account.user.setting_boost_modal
-      store[:delete_modal]      = object.current_account.user.setting_delete_modal
-      store[:auto_play_gif]     = object.current_account.user.setting_auto_play_gif
-      store[:display_media]     = object.current_account.user.setting_display_media
-      store[:expand_spoilers]   = object.current_account.user.setting_expand_spoilers
-      store[:reduce_motion]     = object.current_account.user.setting_reduce_motion
-      store[:advanced_layout]   = object.current_account.user.setting_advanced_layout
-      store[:use_blurhash]      = object.current_account.user.setting_use_blurhash
-      store[:use_pending_items] = object.current_account.user.setting_use_pending_items
-      store[:is_staff]          = object.current_account.user.staff?
-      store[:trends]            = Setting.trends && object.current_account.user.setting_trends
-      store[:crop_images]       = object.current_account.user.setting_crop_images
-    else
+      store[:me]                                = object.current_account.id.to_s
+      store[:unfollow_modal]                    = object.current_account.user.setting_unfollow_modal
+      store[:unsubscribe_modal]                 = object.current_account.user.setting_unsubscribe_modal
+      store[:boost_modal]                       = object.current_account.user.setting_boost_modal
+      store[:delete_modal]                      = object.current_account.user.setting_delete_modal
+      store[:auto_play_gif]                     = object.current_account.user.setting_auto_play_gif
+      store[:display_media]                     = object.current_account.user.setting_display_media
+      store[:expand_spoilers]                   = object.current_account.user.setting_expand_spoilers
+      store[:reduce_motion]                     = object.current_account.user.setting_reduce_motion
+      store[:advanced_layout]                   = object.current_account.user.setting_advanced_layout
+      store[:use_blurhash]                      = object.current_account.user.setting_use_blurhash
+      store[:use_pending_items]                 = object.current_account.user.setting_use_pending_items
+      store[:is_staff]                          = object.current_account.user.staff?
+      store[:trends]                            = Setting.trends && object.current_account.user.setting_trends
+      store[:crop_images]                       = object.current_account.user.setting_crop_images
+      store[:show_follow_button_on_timeline]    = object.current_account.user.setting_show_follow_button_on_timeline
+      store[:show_subscribe_button_on_timeline] = object.current_account.user.setting_show_subscribe_button_on_timeline
+      store[:show_target]                       = object.current_account.user.setting_show_target
+      else
       store[:auto_play_gif] = Setting.auto_play_gif
       store[:display_media] = Setting.display_media
       store[:reduce_motion] = Setting.reduce_motion
diff --git a/app/views/settings/preferences/other/show.html.haml b/app/views/settings/preferences/other/show.html.haml
index db7d806bc05445eed383840606b674be059d0b2a..962fd6802f0f7184566bb8340c979f2f4ad5fbe5 100644
--- a/app/views/settings/preferences/other/show.html.haml
+++ b/app/views/settings/preferences/other/show.html.haml
@@ -28,6 +28,17 @@
   .fields-group
     = f.input :setting_show_application, as: :boolean, wrapper: :with_label, recommended: true
 
+  %h4= t 'preferences.fedibird_features'
+
+  .fields-group
+    = f.input :setting_show_follow_button_on_timeline, as: :boolean, wrapper: :with_label
+
+  .fields-group
+    = f.input :setting_show_subscribe_button_on_timeline, as: :boolean, wrapper: :with_label
+
+  -# .fields-group
+  -#   = f.input :setting_show_target, as: :boolean, wrapper: :with_label
+
   %h4= t 'preferences.public_timelines'
 
   .fields-group
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 7ab08c21bffa7a0048d6031a259b2db7e2f51f9c..2b13f40d83a1fcb93850faefb7878d41c8136a96 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -997,6 +997,7 @@ en:
       too_few_options: must have more than one item
       too_many_options: can't contain more than %{max} items
   preferences:
+    fedibird_features: Fedibird features
     other: Other
     posting_defaults: Posting defaults
     public_timelines: Public timelines
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index f34f60a759c6294de382c2abc71b4e1cd871c4c8..c7355f2408b6fd83574f56c7094c40105504539b 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -966,6 +966,7 @@ ja:
       too_few_options: は複数必要です
       too_many_options: は%{max}個までです
   preferences:
+    fedibird_features: Fedibirdの機能
     other: その他
     posting_defaults: デフォルトの投稿設定
     public_timelines: 公開タイムライン
diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml
index 0bbb8d755caa7df8f43d3f3265db95b1af52ce27..6531812589b6b3922eb61d17352d62cdbfe131cc 100644
--- a/config/locales/simple_form.en.yml
+++ b/config/locales/simple_form.en.yml
@@ -41,6 +41,9 @@ en:
         setting_hide_network: Who you follow and who follows you will not be shown on your profile
         setting_noindex: Affects your public profile and status pages
         setting_show_application: The application you use to toot will be displayed in the detailed view of your toots
+        setting_show_follow_button_on_timeline: You can easily check the follow status and build a follow list quickly
+        setting_show_subscribe_button_on_timeline: You can easily check the status of your subscriptions and quickly build a subscription list
+        setting_show_target: Enable the function to switch between posting target and follow / subscribe target
         setting_use_blurhash: Gradients are based on the colors of the hidden visuals but obfuscate any details
         setting_use_pending_items: Hide timeline updates behind a click instead of automatically scrolling the feed
         username: Your username will be unique on %{domain}
@@ -140,6 +143,9 @@ en:
         setting_noindex: Opt-out of search engine indexing
         setting_reduce_motion: Reduce motion in animations
         setting_show_application: Disclose application used to send toots
+        setting_show_follow_button_on_timeline: Show follow button on timeline
+        setting_show_subscribe_button_on_timeline: Show subscribe button on timeline
+        setting_show_target: Enable targeting features
         setting_system_font_ui: Use system's default font
         setting_theme: Site theme
         setting_trends: Show today's trends
diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml
index 1a29511a99bb3d275d1bcf81a73d0cabd11ad04d..f4222a97e820537fb30b1bdd977004d0932f0046 100644
--- a/config/locales/simple_form.ja.yml
+++ b/config/locales/simple_form.ja.yml
@@ -41,6 +41,9 @@ ja:
         setting_hide_network: フォローとフォロワーの情報がプロフィールページで見られないようにします
         setting_noindex: 公開プロフィールおよび各投稿ページに影響します
         setting_show_application: トゥートするのに使用したアプリがトゥートの詳細ビューに表示されるようになります
+        setting_show_follow_button_on_timeline: フォロー状態を確認し易くなり、素早くフォローリストを構築できます
+        setting_show_subscribe_button_on_timeline: 購読状態を確認し易くなり、素早く購読リストを構築できます
+        setting_show_target: 投稿対象と、フォロー・購読の対象を切り替える機能を有効にします
         setting_use_blurhash: ぼかしはメディアの色を元に生成されますが、細部は見えにくくなっています
         setting_use_pending_items: 新着があってもタイムラインを自動的にスクロールしないようにします
         username: あなたのユーザー名は %{domain} の中で重複していない必要があります
@@ -140,6 +143,9 @@ ja:
         setting_noindex: 検索エンジンによるインデックスを拒否する
         setting_reduce_motion: アニメーションの動きを減らす
         setting_show_application: 送信したアプリを開示する
+        setting_show_follow_button_on_timeline: タイムライン上にフォローボタンを表示する
+        setting_show_subscribe_button_on_timeline: タイムライン上に購読ボタンを表示する
+        setting_show_target: ターゲット機能を有効にする
         setting_system_font_ui: システムのデフォルトフォントを使う
         setting_theme: サイトテーマ
         setting_trends: 本日のトレンドタグを表示する
diff --git a/config/settings.yml b/config/settings.yml
index f31e7c90cbba516a9bafc28f054b6ff89cbdfcfc..2b4fa5fe3e9e582ef3e9ef3283fb514dbd0f4c32 100644
--- a/config/settings.yml
+++ b/config/settings.yml
@@ -38,6 +38,9 @@ defaults: &defaults
   trends: true
   trendable_by_default: false
   crop_images: true
+  show_follow_button_on_timeline: false
+  show_subscribe_button_on_timeline: false
+  show_target: false
   notification_emails:
     follow: false
     reblog: false